揭开Javascript属性constructor/prototype的底层原理
2013-05-16 21:48
507 查看
在Javascript语言中,constructor属性是专门为function而设计的,它存在于每一个function的prototype属性中。这个constructor保存了指向function的一个引用。在定义一个函数(代码如下所示)时,
JavaScript内部会执行如下几个动作:
为该函数添加一个原形属性(即prototype对象).
为prototype对象额外添加一个constructor属性,并且该属性保存指向函数F的一个引用。
这样当我们把函数F作为自定义构造函数来创建对象的时候,对象实例内部会自动保存一个指向其构造函数(这里就是我们的自定义构造函数F)的prototype对象的一个属性__proto__,所以我们在每一个对象实例中就可以访问构造函数的prototype所有拥有的全部属性和方法,就好像它们是实例自己的一样。
当然该实例也有一个constructor属性了(从prototype那里获得的),这时候constructor的作用就很明显了,因为在这时,每一个对象实例都可以通过constrcutor对象访问它的构造函数,请看下面代码:
我们可以利用这个特性来完成下面的事情: 对象类型判断,如
其实constructor的出现原本就是用来进行对象类型判断的,但是constructor属性易变,不可信赖。
我们有一种更加安全可靠的判定方法:instanceof 操作符。
下面代码仍然返回true
if(f instanceof F) {
// do sth with F
}.
原型链继承,由于constructor存在于prototype对象上,因此我们可以结合constructor沿着原型链找到最原始的构造函数,如下面代码:
上面的例子只是为了说明constructor在原型链中的作用,更实际一点的意义在于:一个子类对象可以获得其父类的所有属性和方法,称之为继承,关于继承我们有好多可以分析和讨论,本篇限于篇幅不在此讨论。
一个容易掉入的陷阱(gotchas) 之前提到constructor易变,那是因为函数的prototype属性容易被更改,我们用时下很流行的编码方式来说明问题,请看下面的示例代码:
var f = new F();
alert(f.constructor === F); // output false
怎么回事?F不是实例对象f的构造函数了吗?
当然是!只不过构造函数F的原型被开发者重写了,这种方式将原有的prototype对象用一个对象的字面量{}来代替。
而新建的对象{}只是Object的一个实例,系统(或者说浏览器)在解析的时候并不会在{}上自动添加一个constructor属性,因为这是function创建时的专属操作,仅当你声明函数的时候解析器才会做此动作。
然而你会发现constructor并不是不存在的,下面代码可以测试它的存在性:
alert(typeof f.constructor == 'undefined');// output false
既然存在,那这个constructor是从哪儿冒出来的呢?
我们要回头分析这个对象字面量{}。
因为{}是创建对象的一种简写,所以{}相当于是new Object()。
那既然{}是Object的实例,自然而然他获得一个指向构造函数Object()的prototype属性的一个引用__proto__,又因为Object.prototype上有一个指向Object本身的constructor属性。所以可以看出这个constructor其实就是Object.prototype的constructor,下面代码可以验证其结论:
alert(f.constructor === Object.prototype.constructor);//output true
alert(f.constructor === Object);// also output true
一个解决办法就是手动恢复他的constructor,下面代码非常好地解决了这个问题:
之后一切恢复正常,constructor重新获得的构造函数的引用,我们可以再一次测试上面的代码,这次返回true
var f = new F();
alert(f.constructor === F); // output true this time ^^
解惑:构造函数上怎么还有一个constructor?它又是哪儿来的?
细心的朋友会发现,像JavaScript内建的构造函数,如Array, RegExp, String, Number, Object, Function等等居然自己也有一个constructor:
alert(typeof Array.constructor != 'undefined');// output true
经过测试发现,此物非彼物它和prototype上constructor不是同一个对象,他们是共存的:
alert(typeof Array.constructor != 'undefined');// output true
alert(typeof Array.prototype.constructor === Array); // output true
不过这件事情也是好理解的,因为构造函数也是函数。
是函数说明它就是Function构造函数的实例对象,自然他内部也有一个指向Function.prototype的内部引用__proto__啦。
因此我们很容易得出结论,这个constructor(构造函数上的constructor不是prototype上的)其实就是Function构造函数的引用:
alert(Array.constructor === Function);// output true
alert(Function.constructor === Function); // output true
OK, constructor从此真相大白,你不在对它陌生了~
function F() { // some code }.
JavaScript内部会执行如下几个动作:
为该函数添加一个原形属性(即prototype对象).
为prototype对象额外添加一个constructor属性,并且该属性保存指向函数F的一个引用。
这样当我们把函数F作为自定义构造函数来创建对象的时候,对象实例内部会自动保存一个指向其构造函数(这里就是我们的自定义构造函数F)的prototype对象的一个属性__proto__,所以我们在每一个对象实例中就可以访问构造函数的prototype所有拥有的全部属性和方法,就好像它们是实例自己的一样。
当然该实例也有一个constructor属性了(从prototype那里获得的),这时候constructor的作用就很明显了,因为在这时,每一个对象实例都可以通过constrcutor对象访问它的构造函数,请看下面代码:
var f = new F(); alert(f.constructor === F);// output true alert(f.constructor === F.prototype.constructor);// output true
我们可以利用这个特性来完成下面的事情: 对象类型判断,如
if(f.constructor === F) { // do sth with F }
其实constructor的出现原本就是用来进行对象类型判断的,但是constructor属性易变,不可信赖。
我们有一种更加安全可靠的判定方法:instanceof 操作符。
下面代码仍然返回true
if(f instanceof F) {
// do sth with F
}.
原型链继承,由于constructor存在于prototype对象上,因此我们可以结合constructor沿着原型链找到最原始的构造函数,如下面代码:
function Base() {} // Sub1 inherited from Base through prototype chain function Sub1(){} Sub1.prototype = new Base(); Sub1.prototype.constructor = Sub1; Sub1.superclass = Base.prototype; // Sub2 inherited from Sub1 through prototype chain function Sub2(){} Sub2.prototype = new Sub1(); Sub2.prototype.constructor = Sub2; Sub2.superclass = Sub1.prototype; // Test prototype chain alert(Sub2.prototype.constructor); // function Sub2(){} alert(Sub2.superclass.constructor); // function Sub1(){} alert(Sub2.superclass.constructor.superclass.constructor); // function Base(){}
上面的例子只是为了说明constructor在原型链中的作用,更实际一点的意义在于:一个子类对象可以获得其父类的所有属性和方法,称之为继承,关于继承我们有好多可以分析和讨论,本篇限于篇幅不在此讨论。
一个容易掉入的陷阱(gotchas) 之前提到constructor易变,那是因为函数的prototype属性容易被更改,我们用时下很流行的编码方式来说明问题,请看下面的示例代码:
function F() {} F.prototype = { _name: 'Eric', getName: function() { return this._name; } };初看这种方式并无问题,但是你会发现下面的代码失效了:
var f = new F();
alert(f.constructor === F); // output false
怎么回事?F不是实例对象f的构造函数了吗?
当然是!只不过构造函数F的原型被开发者重写了,这种方式将原有的prototype对象用一个对象的字面量{}来代替。
而新建的对象{}只是Object的一个实例,系统(或者说浏览器)在解析的时候并不会在{}上自动添加一个constructor属性,因为这是function创建时的专属操作,仅当你声明函数的时候解析器才会做此动作。
然而你会发现constructor并不是不存在的,下面代码可以测试它的存在性:
alert(typeof f.constructor == 'undefined');// output false
既然存在,那这个constructor是从哪儿冒出来的呢?
我们要回头分析这个对象字面量{}。
因为{}是创建对象的一种简写,所以{}相当于是new Object()。
那既然{}是Object的实例,自然而然他获得一个指向构造函数Object()的prototype属性的一个引用__proto__,又因为Object.prototype上有一个指向Object本身的constructor属性。所以可以看出这个constructor其实就是Object.prototype的constructor,下面代码可以验证其结论:
alert(f.constructor === Object.prototype.constructor);//output true
alert(f.constructor === Object);// also output true
一个解决办法就是手动恢复他的constructor,下面代码非常好地解决了这个问题:
function F() {} F.prototype = { constructor: F, /* reset constructor */ _name: 'Eric', getName: function() { return this._name; } };
之后一切恢复正常,constructor重新获得的构造函数的引用,我们可以再一次测试上面的代码,这次返回true
var f = new F();
alert(f.constructor === F); // output true this time ^^
解惑:构造函数上怎么还有一个constructor?它又是哪儿来的?
细心的朋友会发现,像JavaScript内建的构造函数,如Array, RegExp, String, Number, Object, Function等等居然自己也有一个constructor:
alert(typeof Array.constructor != 'undefined');// output true
经过测试发现,此物非彼物它和prototype上constructor不是同一个对象,他们是共存的:
alert(typeof Array.constructor != 'undefined');// output true
alert(typeof Array.prototype.constructor === Array); // output true
不过这件事情也是好理解的,因为构造函数也是函数。
是函数说明它就是Function构造函数的实例对象,自然他内部也有一个指向Function.prototype的内部引用__proto__啦。
因此我们很容易得出结论,这个constructor(构造函数上的constructor不是prototype上的)其实就是Function构造函数的引用:
alert(Array.constructor === Function);// output true
alert(Function.constructor === Function); // output true
OK, constructor从此真相大白,你不在对它陌生了~
相关文章推荐
- Javascript属性constructor/prototype的底层原理
- Javascript属性constructor/prototype的底层原理
- 构造函数对象[置顶] 揭开Javascript属性constructor/prototype的底层原理
- JavaScript中几个重要的属性(this、constructor、prototype)介绍
- 【JavaScript】(6)Object、String、Array、Date对象的方法、属性(constructor、prototype)
- JavaScript中几个重要的属性(this、constructor、prototype)
- JavaScript中几个重要的属性(this、constructor、prototype)介绍
- javascript 对象及原型继承有关的属性:constructor、prototype、isPrototypeOf、instanceof、in 、hasOwnProperty 等等
- JavaScript---对象学习(一)object对象、string对象、javaScript的constructor属性和prototype属性
- JavaScript中几个重要的属性(this、constructor、prototype)
- JavaScript中几个重要的属性(this、constructor、prototype)介绍
- JavaScript中constructor和prototype属性
- JavaScript Object.prototype.constructor属性详解
- javascript 对象及原型继承有关的属性:constructor、prototype、isPrototypeOf、instanceof、in 、hasOwnProperty 等等
- javascript 中的 prototype和constructor属性的使用(constructor属性始终指向创建当前对象的构造函数,每个函数都有一个默认的属性prototype,而这个prototype的constructor默认指向这个函数)
- javascript之prototype原型属性案例
- JavaScript中的prototype属性
- MyBatis动态SQL底层原理分析 与 JavaScript中的Date对象,以及UTC、GMT、时区的关系
- JavaScript:prototype属性使用说明
- JS中的constructor与prototype(2)--js继承的实现原理