天天看點

了解JavaScript原型鍊了解JavaScript原型鍊

了解JavaScript原型鍊

(一)__proto__的預設指向

定義

雖然JavaScript裡一切皆對象,但為了了解原型鍊系統,我們需要将JavaScript的對象分為對象和函數兩大類。在此基礎上,JavaScript的原型鍊邏輯遵從以下通用規則:

通用規則

  1. 對象有__proto__屬性,函數有prototype屬性;
  2. 對象由函數生成;
  3. 生成對象時,對象的__proto__屬性指向函數的prototype屬性。
  4. 在沒有手動修改__proto__屬性的指向時,以上三條便是JavaScript預設原型鍊指向邏輯。

為了讓原型鍊有終點,在原型鍊的最頂端,JavaScript規定了Object.prototype.proto === null。

逐漸深入的詳細解釋一下:

1.一般情況

建立空對象時,實際上我們是用Object函數來生成對象的:
>var o = {}
>o.__proto__ === Object.prototype
true

我們也可以顯式的使用Object函數來建立對象:
>var o = Object()
o.__proto__ === Object.prototype
true

當我們使用函數來建立自定義的對象時,上面的規則同樣适用:
>function MyObj(){}
>typeof MyObj
"function"
>var mo = new MyObj()
>mo.__proto__ === MyObj.prototype
true
           

2.函數對象

既然JavaScript裡“一切皆對象”,那函數自然也是對象的一種。對于函數作為對象來說,上面的規則同樣适用:

函數對象都是由Function函數生成的:
>function fn(){}
>fn.__proto__ === Function.prototype
true
           

可以看到,把函數當做對象時,生成它的函數就是 Function函數。那Function函數本身呢?同樣适用!

Function函數本身作為對象時,生成它的函數是他自身!
>Function.__proto__ === Function.prototype
true
           

同樣我們知道,Object函數也是一個函數對象,那麼它是否符合上面的規則呢?當然!

Object函數既然是函數,那生成它的函數自然是Function函數咯:
>Object.__proto__ === Function.prototype
true
           

3.prototype是誰?

好了,現在我們知道,對象的__proto__屬性是從生成它的函數的prototype那裡得來的,那我們不禁要問,函數的prototype又是誰?

一般函數預設的prototype是系統自動生成的一個對象:
>function fn(){}
>typeof fn.prototype
"object"
>fn.prototype
{constructor: ƒ}
    constructor: ƒ fn()
    __proto__: Object

>fn.prototype.constructor === fn
true
>fn.prototype.__proto__ === Object.prototype
true
           

一般函數預設的prototype是一個類型為"object"的對象,它有兩個屬性:constructor和 proto。其中constructor屬性指向這個函數自身,__proto__屬性指向Object.prototype,這說明一般函數的prototype屬性是由Object函數生成的。

4.特殊情況

前面我們特别強調了是一般函數,那不一般的函數是誰呢?當然是Object函數和Function函數!

先來看Object.prototype:

>typeof Object.prototype
"object"
>Object.prototype
{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}
    constructor: ƒ Object()
    hasOwnProperty: ƒ hasOwnProperty()
    isPrototypeOf: ƒ isPrototypeOf()
    propertyIsEnumerable: ƒ propertyIsEnumerable()
    toLocaleString: ƒ toLocaleString()
    toString: ƒ toString()
    valueOf: ƒ valueOf()
    __defineGetter__: ƒ __defineGetter__()
    __defineSetter__: ƒ __defineSetter__()
    __lookupGetter__: ƒ __lookupGetter__()
    __lookupSetter__: ƒ __lookupSetter__()
    get __proto__: ƒ __proto__()
    set __proto__: ƒ __proto__()
           

可以看到Object函數的prototype屬性也是一個類型為"object"的對象,但和一般函數的預設prototype屬性不一樣的是,它多了一大堆方法,這些方法都是JavaScript對象的系統預設方法。

再仔細看,好像少了什麼,對了,Object函數的prototype屬性裡沒有__proto__屬性,我們試着把它的__proto__屬性打出來看看:

>Object.prototype.__proto__
null
           

這就是Object函數特殊情況了:Object.prototype.proto === null,我們知道,這就是JavaScript原型鍊的終點了。