先上張圖
構造函數
上面的圖看懂了麼,沒懂不要緊。先看個栗子:
function Foo() { }
Foo.prototype.name = 'haha'
const foo = new Foo()
const bar = new Foo()
console.log(foo.name) // haha
console.log(bar.name) // haha
複制代碼
沒錯,這個
Foo
是構造函數,
foo
是執行個體對象。嗯,繼續往下看。
原型
-
prototype(原型對象)
:是一個對象,這個對象包含了所有執行個體對象共享的屬性和方法。每個函數都有prototype
prototype
屬性(除了bind、箭頭函數、Function.prototype),但也隻有函數才擁有這個屬性。
我們把
列印出來,可以看出它包含了執行個體對象Foo.prototype
共同的屬性foo 和 bar
。name = 'haha'
和constructor
當作沒看到。哈哈,開玩笑,既然看到了,就分析下這兩妖孽是啥。__proto__
-
__proto__(原型指針)
:每個__proto__
對象都有這個屬性(除了js
),顧名思義,它指向的是該執行個體對象的原型對象。比如說,栗子中執行個體對象null
的foo
就是__proto__
。Foo.prototype
console.log(foo.__proto__ === Foo.prototype) // true 複制代碼
-
constructor
:每個constructor
原型都有個prototype
,它指向的是構造函數。上面栗子中constructor
的Foo.prototype
就是構造函數constructor
。Foo
console.log(Foo.prototype.constructor === Foo) // true 複制代碼
原型鍊
以上,我們可以看出構造函數、原型對象、執行個體之間的關系。每個構造函數都有一個原型對象(
prototype
),原型對象都包含一個指向構造函數的指針(
constructor
),而每個執行個體都包含一個指向原型對象的指針(
__proto__
)。
如果原型對象(
prototype
)是個執行個體呢,這樣我們通過
Foo.prototype.__proto__
又會找到另一個原型對象(
Object.prototype
)。如此層層遞進,執行個體與原型就形成了一個鍊條,這就是原型鍊。
console.log(Foo.prototype.__proto__ === Object.prototype) // true
複制代碼
來自于生命起源的問題,先有雞還是先有蛋
console.log(Function.__proto__ === Function.prototype) // true
console.log(Function.__proto__.__proto__ === Object.prototype) // true
console.log(Object.__proto__ === Function.prototype) // true
複制代碼
有興趣的可以看看知乎上的讨論