首先,對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 });