還在問什麼是JavaScript建構函式、例項、原型物件以及原型鏈?看完這篇你就懂
阿新 • • 發佈:2020-08-24
## 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