object的_proto_ 和 函數(shù)的prototype
prototype是函數(shù)的一個屬性,這個屬性是一個指針,用來指向一個對象;_proto是一個對象擁有的內(nèi)置隱式原型屬性,js內(nèi)部通過_proto來尋找原型鏈屬性,假如對象內(nèi)部不存在某個屬性,就會從_proto中去尋找,_proto也會有自己的_proto_,直到找到未知,這就是我們所說的原型鏈的概念。
- _proto_ 和 prototype
function Base () {
this.name = 'Base';
}
var instance = new Base();
// console.log()
instance.__proto__ === Base // false
instance.__proto__ === Base.prototype // true
instance.__proto__.constructor === Base.prototype // false
instance.__proto__.constructor === Base // true
Base是構(gòu)造函數(shù),如果進行new Base()操作,那么首先會進行初始化一個空{(diào)},然后將{}的 proto 即隱式原型指向Base的prototype,然后執(zhí)行了 Base.call({}) 將this作用域交予{},然后就完成了實例化。
所以instance的proto 是等于Base的prototype的,那么Base.prototype.constructor 自然是指向本身了。
function Base () {
this.name = 'Base';
}
var instance = new Base();
instance.__proto__.constructor === Base // true
instance.__proto__.constructor === Base.prototype.constructor // true
instance.__proto__ === Base.prototype // true
由上述代碼,我們應該已經(jīng)理解了 _proto_和prototype的區(qū)別了
如果改變一下代碼:
function Base () {
this.name = 'Base';
}
Base.prototype.play = () => { console.log('play') };
var instance = new Base();
// console.log()
instance
打印的instance是存在下列屬性,一個是顯示的name,一個是隱式的proto,展開proto就是Base的prototype,即隱式的構(gòu)造函數(shù)和proto,還有一個顯式的play函數(shù);
- extends
es6
class Base {
constructor () {
this.name = 'Base';
}
play () {
console.log('play');
}
};
class A extends Base {
constructor () {
super();
this.age = 15;
}
}
const instanceA = new A();
// console.log()
instanceA.name // Base
instanceA.age // 15
如何實現(xiàn)的呢?
由上面的知識,我們可以知道 當執(zhí)行一個new() 操作之后,新變量的proto 指向構(gòu)造函數(shù)的prototype,那么我們大膽地打印一下instanceA.proto和A.prototype看一下結(jié)果。
class Base {
constructor () {
this.name = 'Base';
}
play () {
console.log('play');
}
};
class A extends Base {
constructor () {
super();
this.age = 15;
}
}
const instanceA = new A();
// console.log()
instanceA.__proto__ // 打印結(jié)果是一個擁有constructor 和__proto__的object,即A的實例化結(jié)果
A.prototype // 打印結(jié)果是一個擁有constructor 和__proto__的object,即 ?的實例化結(jié)果
instanceA.__proto__ === A.prototype // true 那么上面的?號應該是Base無疑了
由此可以看出es6 class 的語法糖是如何構(gòu)造的了。
如果我們使用一個class繼承另外一個class,就是將本身的prototype指向父class的實例化了
看看es5的代碼:
function Base () { this.name = 'Base' };
function A () { this.age = 15 };
cosnt instanseA = new A();
那么如何才能讓A繼承到Base呢?
function Base () { this.name = 'Base' };
Base.prototype.play = () => { console.log('play') };
function A () { this.age = 15 };
A.prototype = new Base();
cosnt instanseA = new A();
// console.log()
instanseA.name // 'Base'
instanseA.age // 15
instanseA.play // function
原理剖析:A的實例化是instanseA ,當讀取instanseA上面的屬性name時,發(fā)現(xiàn)不存在,只能從proto上面尋找,instanseA 的proto指向的是A的prototype,A的prototype下面有兩個屬性,一個是constructor,一個是proto,發(fā)現(xiàn)A上面也沒有;然后我們寫了這么一行代碼,是A.prototype = new Base();也就是說通過instanseA.proto 找到了A的prototype,就找到了Base的實例化,Base的實例化應該是有兩個屬性,一個是name,一個是proto,然而發(fā)現(xiàn)只能讀取到name,并沒有play函數(shù),然后又從Base的proto上面查找,我們寫了Base.prototype.play是一個函數(shù),所以在proto 上面就找到了play函數(shù)。
一個函數(shù)的prototype上面掛在的函數(shù)可以在實例化的proto_找到。
以上原理總結(jié)一句話就是:
父類屬性放構(gòu)造函數(shù), 父類方法放原型鏈, 子類prototype指向父實例, 子類構(gòu)造函數(shù)調(diào)用父類構(gòu)造函數(shù), 子類構(gòu)造函數(shù)指回自己)