首先,对constructor属性有如下几点了解:浏览器
对于一些公共的属性和方法,我么能够经过原型对象,把它们定义在构造函数的外部,使构造函数成为一个空函数:函数
function Person(){} Person.prototype.name = "Nicholas"; Person.prototype.age = 29; Person.prototype.job = "Software Engineer"; Person.prototype.sayName = function(){ alert(this.name); };
可是,这样的话,每增长一个公共的属性或方法都得写上Person.prototype。为了减小重复的书写,更常见的作法是用一个包含全部属性和方法的对象字面量来重写整个原型对象性能
function Person(){} Person.prototype = { name : "Nicholas", age : 29, job: "Software Engineer", sayName : function () { alert(this.name); } };
通过这样重写以后,从视觉上更好的封装原型的功能,咱们至关于把Person.prototype设置成了一个以对象字面量形式建立的新对象。
可是,这样设置有一个问题:constructor属性再也不指向person了this
var friend = new Person(); alert(friend instanceof Person); //true alert(friend.constructor == Person); //false
咱们能够看到,instanceof表示friend仍然是Person的实例,可是constructor却代表friend的构造函数(父类)再也不是Person了,constructor与instanceof的结果不一致这就形成了instanceof的失真。那么对象的constructor到底指向谁呢?prototype
var friend = new Person(); alert(friend.constructor == Person); //false alert(friend.constructor == Object); //true
没错,constructor 属性指向了Object,也就是说经过这种对象字面量方式改变原型对象以后,原型/实例对象的constructor属性指向了Object,它们的构造函数(父类)变成了Object。
这是为何呢?
每建立一个函数就会同时建立它的prototype对象,这个对象也会自动得到constructor属性。而经过对象字面量形式改写原型对象,本质上算是彻底重写了默认的原型对象,也便是说咱们写了一个新的对象,它是个新对象,所以它的constructor属性也就变成了新对象的constructor属性,指向Object构造函数,再也不指向Person函数了。
所以,经过constructor操做符还能返回正确的结果,可是经过instanceof不能准确肯定对象的类型设计
var friend = new Person(); alert(friend instanceof Object); //true alert(friend instanceof Person); //true alert(friend.constructor == Person); //false alert(friend.constructor == Object); //true
经过改写原型对象的方式改变属性和方法,不只使instanceof操做符失真,若是实例对象定义在修改以前,还会致使实例对象没法访问新加的属性和方法:3d
var friend = new Person(); Person.prototype.sayHi = function(){ alert("hi"); }; friend.sayHi(); //"hi"(没有问题!)
这段代码显示,经过使用Person.prototype.……
的方式逐个向原型对象上添加的属性,能够被实例对象成果访问;可是,经过改写原型对象的形式添加新属性和方法并不是如此:指针
function Person(){} var friend = new Person(); Person.prototype = { constructor: Person, name : "Nicholas", age : 29, job : "Software Engineer", sayName : function () { alert(this.name); } }; friend.sayName(); //error
显然,经过改写原型对象的方式,实例对象不能访问新添加的属性和方法。致使这一现象的根本缘由和instanceof
操做符失真的缘由一致:constructor
的指向发生了变化
请参考如下图示:code
简单来说,调用构造函数时,会为实例添加一个指向最初原型的_proto_
指针,重写原型对象成为一个新对象,就等于:对象
_proto_
属性指向的仍然是原有的原型对象。1.常规写法,可是比较啰嗦,每次都要重复写Person.prototype
function Person(){} Person.prototype.name = "Nicholas"; Person.prototype.age = 29; //...
2.使用对象字面量方式改写原型对象,可是这样会改变constructor的指向
function Person(){} Person.prototype = { name : "Nicholas", age : 29, //... };
3.若是 constructor 的值真的很重要,能够像下面这样特地将它设置回适当的值。
function Person(){} Person.prototype = { constructor : Person, name : "Nicholas", age : 29, //... };
在代码中特地包含一个 constructor属性,并将它的值设置为Person,从而确保了经过该属性可以访问到适当的值
4.可是以上述形式从新指定constructor属性,会使得constructor属性的[[Enumerable]]特性被设置为 true,也就是变成了可枚举类型的属性,可是原生的constructor属性是不可枚举类型,[[Enumerable]]特性为 false,所以可以使用下列语句修改它的[[Enumerable]]属性,仍符合原生的设定:
function Person(){} Person.prototype = { name : "Nicholas", age : 29, job : "Software Engineer", sayName : function () { alert(this.name); } }; //重设构造函数,只适用于 ECMAScript 5 兼容的浏览器 Object.defineProperty(Person.prototype, "constructor", { enumerable: false, value: Person });