1. 程式人生 > >還在問什麼是JavaScript建構函式、例項、原型物件以及原型鏈?看完這篇你就懂

還在問什麼是JavaScript建構函式、例項、原型物件以及原型鏈?看完這篇你就懂

## 1概述 ES6, 全稱 ECMAScript 6.0 ,2015.06 發版。在ES6之前,物件不是基於**類**建立的,而是用一種稱為**建構函式**的**特殊函式**來定義物件和它們的特徵。 ## 2建構函式 建構函式是一種特殊的函式,主要用來初始化物件,即為物件成員變數賦初始值,它總與 new 一起使用。我們可以把物件中一些公共的屬性和方法抽取出來,然後封裝到這個函式裡面。 // 利用建構函式建立物件 function Person(uname, age) { this.uname = uname; this.age = age; this.sing = function() { console.log('我會唱歌'); } } var ldh = new Person('劉德華', 18); var zxy = new Person('張學友', 19); console.log(ldh); ldh.sing(); zxy.sing(); 在 JS 中,使用建構函式時要注意以下兩點: 1. 建構函式用於建立某一類物件,其首字母要大寫 2. 建構函式要和 new 一起使用才有意義 #### (1)建構函式執行過程 new 在執行時會做四件事情: ① 在記憶體中建立一個新的空物件。 ② 讓 this 指向這個新的物件。 ③ 執行建構函式裡面的程式碼,給這個新物件新增屬性和方法。 ④ 返回這個新物件(所以建構函式裡面不需要 return )。 #### (2)建構函式的成員 JavaScript 的建構函式中可以新增一些成員,可以在建構函式本身上新增,也可以在建構函式內部的 this 上新增。通過這兩種方式新增的成員,就分別稱為靜態成員和例項成員。 - 靜態成員:在建構函式本上新增的成員稱為靜態成員,只能由建構函式本身來訪問 ``` //靜態成員 在建構函式本身上新增的成員 sex 就是靜態成員 // 靜態成員只能通過建構函式來訪問 Person.sex = '男'; console.log(Person.sex); ``` - 例項成員:在建構函式內部建立的物件成員稱為例項成員,只能由例項化的物件來訪問 ``` function Person(uname) { this.uname = uname; } 例項成員就是建構函式內部通過this新增的成員 uname就是例項成員 ``` ## 3建構函式原型prototype 建構函式通過原型分配的函式是所有物件所共享的。 JavaScript 規定**,每一個建構函式都有一個 prototype 屬性**,指向另一個物件。注意這個 prototype 就是一個物件,這個物件的所有屬性和方法,都會被建構函式所擁有。 我們可以把那些不變的方法,直接定義在 prototype 物件上,這樣所有物件的例項就可以共享這些方法。 function Person(uname, age) { this.uname = uname; this.age = age; // this.sing = function() { // console.log('我會唱歌'); // } } // 一般情況下,我們的公共屬性定義到建構函式裡面, 公共的方法我們放到原型物件身上 Person.prototype.sing = function() { console.log('我會唱歌'); } var ldh = new Person('劉德華', 18); var zxy = new Person('張學友', 19); console.log(ldh.sing === zxy.sing); //true ldh.sing(); zxy.sing(); >1. 原型是什麼 ? >一個物件,我們也稱為 prototype 為原型物件。 >2. 原型的作用是什麼 ? >共享方法。 ## 4 物件原型 `__proto__ ` 物件都會有一個屬性` __proto__` 指向建構函式的 `prototype `原型物件,之所以我們物件可以使用建構函式` prototype` 原型物件的屬性和方法,就是因為物件有` __proto__ `原型的存在。 function Person(uname, age) { this.uname = uname; this.age = age; } Person.prototype.sing = function() { console.log('我會唱歌'); } var mingo = new Person('小明', 18); var zxy = new Person('張學友', 19); mingo.sing(); console.log(mingo); // 物件身上系統自己新增一個 __proto__ 指向我們建構函式的原型物件 prototype console.log(mingo.__proto__ === Person.prototype); //true - `__proto__`物件原型和原型物件` prototype `是等價的 **(如圖箭頭指向,mingo是Person建構函式創建出來的一個物件例項,這個物件例項的原型(`__proto__`),就是Person建構函式的原型物件`prototype`。)** - `__proto__`物件原型的意義就在於為物件的查詢機制提供一個方向,或者說一條路線,但是它是一個非標準屬性, 因此實際開發中,不可以使用這個屬性,它只是內部指向原型物件 `prototype` ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/31384de72d9a4cad9a5115e855fbc8b7~tplv-k3u1fbpfcp-zoom-1.image) ## 5constructor 建構函式 物件原型` __proto__`和建構函式`prototype`原型物件(其實指的就是同一個)裡面有一個屬性 constructor 屬性 ,constructor 我們稱 為建構函式,因為它指回建構函式本身。 ![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/34d18010abd14c488622e8de869770df~tplv-k3u1fbpfcp-zoom-1.image) constructor 主要用於**記錄該物件引用於哪個建構函式**,**它可以讓原型物件重新指向原來的建構函式**。 一般情況下,物件的方法都在建構函式的原型物件中設定。如果有多個物件的方法,我們可以給原型物件採取物件形式賦值,但是這樣就會覆蓋建構函式原型物件原來的內容,這樣修改後的原型物件 constructor 就不再指向當前構造函數了。 此時,我們可以在修改後的原型物件中,新增一個 constructor 指向原來的建構函式。 function Person(uname, age) { this.uname = uname; this.age = age; } // 很多情況下,我們需要手動的利用constructor 這個屬性指回 原來的建構函式 Person.prototype = { // 如果我們修改了原來的原型物件,給原型物件賦值的是一個物件,則必須手動的利用constructor指回原來的建構函式 constructor: Person, sing: function() { console.log('我會唱歌'); }, movie: function() { console.log('我會演電影'); } } var mingo = new Person('小明', 18); var zxy = new Person('張學友', 19); console.log(Person.prototype); console.log(mingo.__proto__); console.log(Person.prototype.constructor); console.log(mingo.__proto__.constructor); ## 6原型鏈 >每個建構函式都有一個原型物件,原型物件都包含一個指向建構函式的指標,而例項都包含一個指向原型物件的內部指標。那麼假如我們讓原型物件等於另一個型別的例項,結果會怎樣?顯然,此時的原型物件將包含一個指向另一個原型的指標,相應地,另一個原型中也包含著一個指向另一個建構函式的指標。假如另一個原型又是另一個型別的例項,那麼上述關係依然成立。如此層層遞進,就構成了例項與原型的鏈條。這就是所謂的原型鏈的基本概念。——摘自《javascript高階程式設計》 function Person(uname, age) { this.uname = uname; this.age = age; } Person.prototype.sing = function() { console.log('我會唱歌'); } var mingo = new Person('小明', 18); // 1. 只要是物件就有__proto__ 原型, 指向原型物件 console.log(Person.prototype); console.log(Person.prototype.__proto__ === Object.prototype);//true // 2.我們Person原型物件裡面的__proto__原型指向的是 Object.prototype console.log(Object.prototype.__proto__); // 3. 我們Object.prototype原型物件裡面的__proto__原型 指向為 null console.log(Object); Object 是 JavaScript 中最頂級的物件,其它所有物件都是基於它的,包括你建立的函式 ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/96a2143d4404477fa75c7b3356a92cb5~tplv-k3u1fbpfcp-zoom-1.image) ## 7JavaScript 的成員查詢機制(基於原型鏈規則) ① 當訪問一個物件的屬性(包括方法)時,首先查詢這個物件自身有沒有該屬性。 ② 如果沒有就查詢它的原型(也就是 `__proto__`指向的 prototype 原型物件)。 ③ 如果還沒有就查詢原型物件的原型(Object的原型物件)。 ④ 依此類推一直找到 Object 為止(null)。 ⑤ `__proto__`物件原型的意義就在於為物件成員查詢機制提供一個方向,或者說一條路線。 ## 8 原型物件this指向 1建構函式中的this 指向我們例項物件 2原型物件裡面放的是方法, 這個方法裡面的this 指向的是 這個方法的呼叫者, 也就是這個例項物件 function Person(uname, age) { this.uname = uname; this.age = age; } var that; Person.prototype.sing = function() { console.log('我會唱歌'); that = this; console.log(this); } var mingo = new Person('小明', 18); // 1. 在建構函式中,裡面this指向的是物件例項 mingo // 2.原型物件函式裡面的this 指向的也是 例項物件 mingo mingo.sing(); console.log(that === mingo); //t