1.前言
原型與原型鏈知識歷來都是面試中考察的重點,說難不算太難,但要完全理解還是得下一定的功夫,先來看一道面試題開開胃口吧:
function User() {}
User.prototype.sayHello = function() {}
var u1 = new User();
var u2 = new User();
console.log(u1.sayHello === u2.sayHello);
console.log(User.prototype.constructor);
console.log(User.prototype === Function.prototype);
console.log(User.__proto__ === Function.prototype);
console.log(User.__proto__ === Function.__proto__);
console.log(u1.__proto__ === u2.__proto__);
console.log(u1.__proto__ === User.__proto__);
console.log(Function.__proto__ === Object.__proto__);
console.log(Function.prototype.__proto__ === Object.prototype.__proto__);
console.log(Function.prototype.__proto__ === Object.prototype);
2.基礎鋪墊
-
JavaScript所有的物件本質上都是通過new 函式創建的,包括物件字面量的形式定義物件(相當于new Object()的語法糖),

-
所有的函式本質上都是通過
new Function創建的,包括Object、Array等

-
所有的函式都是物件,
3. prototype
每個函式都有一個屬性prototype,它就是原型,默認情況下它是一個普通Object物件,這個物件是呼叫該建構式所創建的實體的原型,

4. contructor屬性
JavaScript同樣存在由原型指向建構式的屬性:constructor,即Func.prototype.constructor --> Func

5. __proto__
JavaScript中所有物件(除了null)都具有一個__proto__屬性,該屬性指向該物件的原型,
function User() {}
var u1 = new User();
// u1.__proto__ -> User.prototype
console.log(u1.__proto__ === User.prototype) // true
顯而易見,實體的__proto__屬性指向了建構式的原型,那么多個實體的__proto__會指向同一個原型嗎?
var u2 = new User();
console.log(u1.__proto__ === u2.__proto__) // true
其實學到這里就可以產生一些騷想法了,多個實體的__proto__都指向建構式的原型,那么實體如果能通過一種方式,訪問原型上的方法,屬性等,就可以實作繼承的效果,
我們繼續更新一下原型與原型鏈的關系圖:

6. 原型鏈
實體物件在查找屬性時,如果查找不到,就會沿著__proto__去與物件關聯的原型上查找,如果還查找不到,就去找原型的原型,直至查到最頂層,這也就是原型鏈的概念,
就借助面試題,舉幾個原型鏈的例子:
6.1舉例
u1.sayHello():
u1上是沒有sayHello方法的,因此訪問u1.__proto__(User.prototype),成功訪問到sayHello方法u2.toString()
u2,User.prototype都沒有toString方法,User.prototype也是一個普通物件,因此繼續尋找User.prototype.__proto__(Object.prototype),成功呼叫到toString方法
7. 提高升華
學完上面那些,大多數面試題都可以做出來了,例如下面這種
function A() {}
function B(a) {
this.a = a;
}
function C(a) {
if (a) {
this.a = a;
}
}
A.prototype.a = 1;
B.prototype.a = 1;
C.prototype.a = 1;
console.log(new A().a); //1
console.log(new B().a); //undefined
console.log(new C(2).a); //2
但距離解決文章的最初的面試題還欠缺些什么,比如Function.__proto__ === Object.__proto__、Function.prototype.__proto__ === Object.prototype.__proto__等,接著我們來一一攻克它,
7.1 Objcet.__proto__ 、 Object.prototype、Object.prototype.__proto__
-
Object是建構式,在第二部分我們講過所有的函式都是通過new Function創建了,因此Object相當于Function的實體,即Object.__proto__ --> Function.prototype, -
Object.prototype是Object建構式的原型,處于原型鏈的頂端,Object.prototype.__proto__已經沒有可以指向的上層原型,因此其值為null
// 總結:
Object.__proto__ --> Function.prototype
Object.prototype.__proto__ --> null
7.2 Function.__proto__、Function.prototype、Function.prototype.__proto__
-
Function.prototype是Function的原型,是所有函式實體的原型,例如上面講的Object.__proto__ -
Function.prototype是一個普通物件,因此Function.prototype.__proto__ --> Object.prototype -
Function.__proto__:__proto__指向創造它的建構式的原型,那誰創造了Function那?- 猜想:函式也是物件,那
Function.__proto__會指向Object.prototype嗎?但上文提到,Object是new Function生成,Object.__proto__ --> Function.prototype,如果真的如此指向,未免有些混亂,因此我去做了一下測驗:

實踐證明只存在
Object.__proto__ --> Function.prototype- 苦思冥想沒得出結果,難道
Function函式是猴子不成,從石頭縫里面蹦出來的?于是我進行了一同亂七八糟的測驗,沒想到找出了端倪,

Function函式不通過任何東西創建,JS引擎啟動時,添加到記憶體中 - 猜想:函式也是物件,那
7.3 升華
最后將原型與原型鏈方面的知識凝結成一張圖:

- 所有函式(包括
Function)的__proto__指向Function.prototype - 自定義物件實體的
__proto__指向建構式的原型 - 函式的
prototype的__proto__指向Object.prototype Object.prototype.__proto__ --> null
8. 總結
知識的海洋往往比想象中還要遼闊,原型與原型鏈這邊也反復的學過多次,我認為應該學的比較全面,比較完善了,但遇到這個面試題后,我才發現我所學的只不過是一根枝干,JS里面真的有很多深層次的寶藏等待挖掘,學海無涯,與君共勉,
最后再附贈個簡單的面試題,提高一下自信:
var F = function () {}
Object.prototype.a = function () {}
Function.prototype.b = function () {}
var f = new F();
console.log(f.a, f.b, F.a, F.b);
// f.__proto__ --> F.prototype --> Object.prototype
// F.__proto__ --> Function.prototype --> Object.prototype
本文部分圖源:渡一教育的學習資料
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/303607.html
標籤:其他
上一篇:Vue 常用特性:表單操作、自定義指令、計算屬性、偵聽器、過濾器、生命周期
下一篇:微信小程式分包
