詳解JS原型與原型鏈


一、原型

  任何對象都有一個原型對象,這個原型對象由對象的內置屬性_proto_指向它的構造函數的prototype指向的對象

  即任何對象都是由一個構造函數創建的,但是,不是每一個對象都有prototype,只有方法才有prototype。

function Person() { } var p = new Person(); //方法才有prototype,普通對象無prototype
console.log(Person.prototype); // Object{} 
console.log(p.prototype); // undifined //任何對象都是有構造函數的,Person這種方法的構造函數是Function。 //備注:constructor很容易被改變,一般不用它,此處只是打印下列對象的構造函數是什么。
console.log(p.constructor); //function Person(){} 
console.log(Person.constructor); //function Function(){} 
console.log({}.constructor); // function Object(){}
console.log(Object.constructor); // function Function() {}
console.log([].constructor);  //function Array(){}

  那什么是構造函數呢?用function聲明的都是函數,而如果直接調用的話,那么Person()就是一個普通函數,只有用函數new產生對象時,這個函數才是new出來對象的構造函數。

二、采用new關鍵創建對象的過程

1、聲明方法

  當我們聲明一個function關鍵字的方法時,會為這個方法添加一個prototype屬性,指向默認的原型對象,並且此prototype的constructor屬性也指向方法對象。此二個屬性會在創建對象時被對象的屬性引用。

2、我們如果用Hello創建一個對象h,看這個對象有什么屬性。

  備注:getPrototypeOf是獲取_proto_。

  我們驚喜的發現,new出來的對象,它的constructor指向了方法對象,它的_proto_和prototype相等。  即new一個對象,它的_proto_屬性指向了方法的prototype屬性,並且constructor指向了prototype的constructor屬性。

3、所以new創建一個對象的過程

function Hello(name) { this.name = name; } var h = new Hello("..."); //偽代碼:
function newObj(name){ var obj = {}; obj.__proto__ = Hello.prototype; obj.constructor = Hello.prototype.constructor; var result = Hello.call(obj, name); return typeof result==='object' && result!=null ? result : obj;  //當無返回對象或默認時返回obj。
} var hh = newObj("..."); console.log(hh); console.log(h); //雖然hh!=h,但是可以看到這個hh就和h的結構一樣了

  過程:

(1)先創建一個空對象,

(2)設置空對象的_proto_指向方法的原型,

(3)設置空對象的constructor為構造函數的名稱

(4)用新對象做this指向方法調用函數,

(5)將初始化完畢的新對象返回

  具體可以看下之前總結的這篇:原生JS實現new方法、new一個對象發生的四部、new里面常用的優先級

4、原型鏈

  第一個 __proto__  指向的是 Hello.prototype;第二個 __proto__ 指向的是 Object.prototype,

  從上面說明的過程中,我們發現只要是對象就是由構造函數來創建的,並且內部二個屬性是從構造函數的prototype衍生的一個指向,而構造函數的prototype也是一個對象,那么它應該肯定也有一個構造函數,首先它是一個Object {} 對象,那么它的構造函數肯定是Object,所以就會有一個指針_proto_指向Object.prototype。最后Object.prototype因為沒有_proto_,指向null,這樣就構成了一個原型鏈。

三、原型鏈

1、什么是原型鏈?

  每個對象都有一個原型_proto_,這個原型還可以有它自己的原型,以此類推,形成一個原型鏈。查找特定屬性的時候,我們先去這個對象里去找,如果沒有的話就去它的原型對象里面去,如果還是沒有的話再去向原型對象的原型對象里去尋找...... 這個操作被委托在整個原型鏈上,這個就是我們說的原型鏈了。

  原型鏈的核心就是依賴對象的_proto_的指向,當查找自身不存在的屬性時,就一層層的扒出創建對象的構造函數,直至到Object時,就沒有_proto_指向了。因為_proto_實質找的是prototype,所以我們只要找這個鏈條上的構造函數的prototype。其中Object.prototype是沒有_proto_屬性的,它==null。

  原型鏈查找分析:

(1)當訪問一個對象的成員的時候,會先在自身找有沒有,如果找到直接使用。

(2)如果沒有找到,則去原型鏈指向的對象的構造函數的prototype中找,找到直接使用,沒找到就返回undifined或報錯。

function Person(name){ this.name = name; } var p = new Person(); //p ---> Person.prototype --->Object.prototype---->null

2、原型指針

  我們知道了原型的概念,接下來我們就看下面這張圖來具體分析一下原型的指針:

 

  中間最上面藍色模塊標注的構造函數Foo, 里面有兩個屬性: _proto_ 和 prototype, 這兩個很容易使人混淆。

(1)prototype屬性,它是函數所獨有的,它是從一個函數指向一個對象。它的含義是函數的原型對象,也就是這個函數(其實所有函數都可以作為構造函數)所創建的實例的原型對象; 這個屬性是一個指針,指向一個對象,這個對象的用途就是包含所有實例共享的屬性和方法(我們把這個對象叫做原型對象);

(2)__proto__ 是原型鏈查詢中實際用到的,它總是指向 prototype,換句話說就是指向構造函數的原型對象,它是對象獨有的。注意,為什么Foo構造函數也有這個屬性呢,因為在js的宇宙里萬物皆對象,包括函數;

  根據以上的概括我們能知道Foo構造函數_proto_指向的是他的構造函數的原型對象,它的構造函數是Function, 也就是說Foo的_proto_指向Function.prototype, 

  我們再看到左邊綠色的a和b函數的_proto_指像的是Foo.prototype,因為他們是通過 new Foo實例化出來的,它們的構造函數就是Foo(),即a._proto_ = Foo.prototype;

  接着我們來看看最右邊紫色的模塊Function.prororype, 它的_proto_指針指向的是Object.prototype,Object._proto_又為null.。

  於是我們就可以得出:在原型鏈中的指向是,函數 → 構造函數  → Function.prototype → Object.protype → null ;

(3)constructor:

  我們看到最中間灰色模塊有一個constructor屬性,這個又是做什么用的呢?

  每個函數都有一個原型對象,該原型對象有一個constructor屬性,指向創建對象的函數本身。

  此外,我們還可以使用constructor屬性,所有的實例對象都可以訪問constructor屬性,constructor屬性是創建實例對象的函數的引用。我們可以使用constructor屬性驗證實例的原型類型(與操作符instanceof非常類似)。

  由於constructor屬性僅僅是原始構造函數的引用,因此我們可以使用該屬性創建新的對象,如:

  通過第一個對象實例化對象的constuctor方法創建第2個實例化對象,說明創建的新對象ninja2 是Ninja的實例,由於ninja和ninja2不是同一個對象可以得出它們是兩個截然不同的實例;

3、結論

(1)__proto__ 是原型鏈查詢中實際用到的,它總是指向 prototype;

(2)prototype 是函數所獨有的在定義構造函數時自動創建,它總是被 __proto__ 所指。

(3)所有對象都有__proto__屬性,函數這個特殊對象除了具有__proto__屬性,還有特有的原型屬性prototype。

(4)prototype對象默認有兩個屬性,constructor屬性和__proto__屬性。

(5)prototype屬性可以給函數和對象添加可共享(繼承)的方法、屬性,而__proto__是查找某函數或對象的原型鏈方式。

(6)constructor,這個屬性包含了一個指針,指回原構造函數。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM