最近被同事問道一個問題:
function Father() {
this.color = ['red', 'green'];
}
function Child() {
this.test = 1;
}
Child.prototype = new Father();
let instance = new Child();
這個例子是經典原型鏈繼承,未做constructor修復的一個例子。
問:
instance的屬性test哪里來的,要知道這里的此時instance的構造函數instance.constructor是Father,我們再根據一般Java,ES6類中的構造函數的拷貝屬性用法,instance的屬性要有也是Father的color?
問題就在這里了,js的new並不像Java 這種OOP語言的new一樣,js的new后面的不是類,就是普通函數,普通函數以new調用,內部過程為:MDN,New
test的屬性是通過this的改變獲取到的,壓根沒走js原型對象中的constructor。所以這里就是一個誤區。
那么js原型對象中的constructor有什么作用?上面的繼承讓我們instance.color通過原型鏈訪問到了Father的color屬性,最后為什么一般要修復Child.prototype.constructor = Child呢。
答案其實也沒那么重要:
constructor屬性不影響任何JavaScript的內部屬性。constructor其實沒有什么用處,只是JavaScript語言設計的歷史遺留物。由於constructor屬性是可以變更的,所以未必真的指向對象的構造函數,只是一個提示。不過,從編程習慣上,我們應該盡量讓對象的constructor指向其構造函數,以維持這個慣例。
目前看到的作用之一,通過實例的構造函數給閉包中的函數增加屬性方法。
var a,b;
(function(){
function A (arg1,arg2) {
this.a = 1;
this.b=2;
}
A.prototype.log = function () {
console.log(this.a);
}
a = new A();
b = new A();
})()
a.log();
// 1
b.log();
// 1
通過以上代碼我們可以得到兩個對象,a,b,他們同為類A的實例。因為A在閉包里,所以現在我們是不能直接訪問A的,那如果我想給類A增加新方法怎么辦?
// a.constructor.prototype 在chrome,firefox中可以通過 a.__proto__ 直接訪問
a.constructor.prototype.log2 = function () {
console.log(this.b)
}
a.log2();
// 2
b.log2();
// 2
通過訪問constructor就可以了。
這位老哥的解釋也就是上面的代碼的一個應用了。
不過你拿到 prototype 也不見得能拿到 正確的 constructor,JS沒有提供方式確保你拿到“正確”的
constructor。我們只能根據慣例相信對象上的 constructor 應該是“正確”的 constructor。
這也是我們上面做constructor修復的一個原因了。
參考:
知乎
紅寶石書