js原型鏈與js繼承解析

最近在網上看了諸多js原型鏈的解析,說得雲裡霧裡,不明所以。徹底了解後,決定出個博客記錄一下,一是方便後來人學習,二是方便日後複習。

首先,我們來看一下構造函數、原型、實例之間的關係圖:

所以,我們通常所說的原型鏈,其實指的就是  prototype 的鏈路,因為構造函數和實例是沒有任何指向父節點的成員的。

我們先來看一下 Array 的原型鏈:

三個節點分別是:

Array.prototype --> 有 concat, fill, find 等成員。

Object.prototype -->

null   -> 結束。

好了,那麼我們來實現一下自己的原型鏈。

 1 var People = function () {
 2 };
 3 People.prototype.ff = function () {
 4   console.log('People.prototype.f');
 5 };
 6 
 7 const Man = function () {
 8 }
 9 
10 Man.prototype = Object.create(People.prototype); // -------------->關聯起來, 注意是從People.prototype創建的
11 Man.prototype.constructor = Man; // 構造函數,上圖上已經明確constructor要指向自己的構造函數。但是我們是從People.prototype創建的,
                      // 而Object.create並不會創建constructor, 需要我們去賦值。
12 const m = new Man(); 13 14 m.ff();

第10行進行關聯,使用 Man 創建 m 實例,可以找到 ff 函數。

如果沒有第10行進行關聯,則會找不到 ff 函數。

需要注意的是,我們使用 Object.create 進行創建,這個稍候再討論。

雖然我們實現了原型鏈,但是成員變量property我們並沒有作處理,比如上面的代碼,如果在People中添加一個成員,在Man中是找不到的。

如下代碼:

 1 var People = function () {
 2     this.p1 = 2;
 3 };
 4 People.prototype.ff = function () {
 5     console.log('People.prototype.f');
 6 };
 7 
 8 const Man = function () {
 9     this.m1 = 2;
10 }
11 
12 Man.prototype = Object.create(People.prototype);
13 Man.prototype.constructor = Man;
14 const m = new Man();
15 
16 m.ff();
17 
18 console.log(m);

打印出來的結果:

m 只包含自己的m1成員。

這個時候,我們需要從People繼承它的property. 具體實現就是在Man的構造函數中,使用People.call(this)。代碼如下:

 1 var People = function () {
 2     this.p1 = 2;
 3 };
 4 People.prototype.ff = function () {
 5     console.log('People.prototype.f');
 6 };
 7 
 8 const Man = function () {
 9     People.call(this);
10     this.m1 = 2;
11 }
12 
13 Man.prototype = Object.create(People.prototype);
14 Man.prototype.constructor = Man;
15 const m = new Man();
16 
17 m.ff();
18 
19 console.log(m);

打印出來的結果:

我們發現p1、m1都包含了。

這也說明了,成員函數的繼承,其實是包含的關係,它並不像原型鏈一樣各自含有各自的東東。

People.call(this)這句代碼,已經明確說明執行People的構造函數,而People使用的this就變成Man的this了。

講到這裡,那麼繼承的實現也就很清晰了:

1、繼承基類的成員變量。

2、將原型鏈連起來。

補充說明:

Object.create. Javascript | MDN 的解釋:

Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。 

則相當於:

1 function create(obj) {
2     function F(){};
3     F.__proto__ = obj; //以传入参数为原型构造对象
4     return new F(); 
5 }  

 注意我們創建的代碼:

Man.prototype = Object.create(People.prototype);
Man.prototype.constructor = Man;

相當於 Man.prototype.__proto__ = People.prototype;  ---> 指向父節點。

 所以這條鏈路就連起來了。

const m = new Man()

這個相當於:

1 var o = new Object();
2 o.__proto__ = Man.prototype;
3 Man.call(o);

了解原理後,我們就了解爲什麼有些人寫:

Man.prototype = new People(); 可以通過。

依據篇首圖形,我們知道實例(new出來的對象)含有 __proto__成員變量,正好與 prototype 一致,只不過缺少了 constructor,這個是有隱患的。

hasOwnProperty

用於判斷成員變量的,是不是prototype中的函數。

因為成員變量繼承了過來,已經變成自己的成員,所以hasOwnProperty是可以判斷基類的成員的。

如果 !hasOwnProperty,就會延著prototype鏈路檢索,直到null.

isPrototypeOf
用於判斷實例對象是否在原型鏈上。
如:
People.prototype.isPrototypeOf(m)
instanceof
判斷實例對象是否爲某個構建函數創建的。如:
console.log(m instanceof Man) // true
console.log(m instanceof aaa) // false
原文地址:https://www.cnblogs.com/lin277541/p/10512356.html