javascript之原型與原型鏈

flynike發表於2021-09-09

前言

  瞭解JavaScript的原型與原型鏈有助於我們更深層次的理解這門語言,看過很多相關的文章,寫的都很好,下面是根據自己的理解,一步步揭開原型與原型鏈


正文

一、資料型別

  在JavaScript中有6種資料型別: String, Number, Boolean, Undefined, Null, Object。(Symbol暫時不討論),其中String, Number, Boolean, Undefined, Null屬於值型別,Object屬於引用型別。

二、typeof操作符

先來看看使用typeof操作符能返回那些資料型別:

    typeof undefined; // 'undefined'
    typeof 100; // 'number'
    typeof '123'; //'string'
    typeof true; //'boolean'
    typeof null; // 'object'
    typeof {}; // 'object'
    typeof []; // 'object'
    typeof function() {}; // 'function'
    typeof /\w/g; 'object'
    typeof new String(); // 'object'
    typeof new Number(); // 'object'
    typeof new Date(); // 'object'
    // ...
複製程式碼

除了string, number, undefined, boolean基本型別外,函式,物件,正則,null, new String(), new Number(), new Date()都是屬於物件,上面可以看出typeof無法檢測出引用型別的資料型別。
所以typeof一般用來檢測基本型別,引用型別的具體資料型別一般都用instanceof操作符,但是要弄清楚其中的原理, 首先需要知道什麼是物件?

三、什麼是物件

簡單的說,一切引用型別都是物件,物件是屬性的集合

四、物件與函式的關係

我們平時開發中大多數情況下會這樣寫:

    var obj = {id: 0, name: 'abc'};
    var arr = [1, 2, 3];
複製程式碼

上面的寫法是一種語法糖,其本質是這樣:

    var obj = new Object();
    obj.id = 0;
    obj.name = 'abc';
    
    var arr = new Array(1, 2, 3);
複製程式碼

可以看出,obj和arr都是由函式用過new操作符建立,在這個過程中,經歷了4個階段:

  1. 建立一個新物件;
  2. 將函式的作用域賦給新物件;
  3. 執行函式中的程式碼;
  4. 返回新物件。

在JavaScript的內建物件中,String, Object, Array, RegExp, Date, Function 等物件都是由函式建立。函式建立了物件,函式屬於物件,函式自己建立了自己。是時候該prototype上場了。

五、prototype原型

每一個函式都有一個prototype屬性,屬性值是一個Object例項物件, 且該物件預設都有一個constructor屬性,指向函式本身

    function Foo() {};
    Foo.prototype instanceof Object // true
    Foo.prototype.constructor === Foo; // true
複製程式碼

Object已經為我們證實了這一點

javascript之原型與原型鏈

那麼先來實現一下:

    function User(name) {
        this.id = 0;
        this.name = name;
    };
    User.prototype.getUserName = function() { 
        return this.name;
    };
    
    var user = new User('xm', 10);
    user.getUserName(); // xm
複製程式碼

上面的程式碼中,User是一個函式,user是通過對User函式使用new操作符建立出來的物件,並且能直接呼叫定義在User函式原型中的方法。可以把這個user列印出來看一下

javascript之原型與原型鏈
發現這個物件下面有一個__proto__的屬性。這個__proto__就是物件身上的隱式原型。

六、__proto__隱式原型

每一個物件都有一個隱式原型__proto__ , 指向該物件建構函式的prototype的屬性

    //還是上面的程式碼
    user.__proto__ === User.prototype; // true
複製程式碼

那麼這裡也就證實了定義在建構函式prototype原型中的方法和屬性是被他的例項所共享的。 由於函式的prototype屬性是一個物件,即Object的例項,所以,函式的prototype也有一個隱式原型__proto__,指向Object.prototype,此時就有一個特例,即:

    Object.prototype.__proto__ === null; // true
複製程式碼

同時也就說明不能在沿著__proto__這條鏈往下走了,也就是原型鏈的終點。

七、原型鏈

當試圖得到一個物件的屬性時,如果這個物件本身沒有這個屬性,那麼會沿著這個物件的__proto__這條鏈去找,這就是原型鏈, 原型鏈的終點是null

如圖所示:

javascript之原型與原型鏈

八、instanceof操作符

instanceof操作符用來檢測一個建構函式的prototype是否在一個物件的原型鏈上

先來看看使用instanceof能返回那些引用型別的資料型別:

    (new String('123')) instanceof String; // true
    (new String('123')) instanceof Object; // true
    (new Number(1)) instanceof Number; // true
    (new Number(1) instanceof Object; // true
    (function() {}) instanceof Function; // true
    (function() {}) instanceof Object; // true
    (new Date()) instanceof Date; // true
    (new Date()) instanceof Object; // true
    (/\w/g) instanceof RegExp; // true
    (/\w/g) instanceof Object; // true
    [] instanceof Array; // true
    [] instanceif Object; // true
    ({}) instanceof Object; // true
    
    Object instanceof Function; // true
    Function instanceof Object; // true
    Function instanceof Function; // true
    // ...
複製程式碼

根據instanceof操作符的檢測規則,來分析一下後面3個:

    Object.prototype === Function.prototype.__proto__; // true
    Function.prototype.__proto__ === Object.prototype; // true
    Function.protoType === Function.__proto__; // true
複製程式碼

瞭解了原型鏈,instanceof操作符的檢測的過程也就知道了。

總結

  • 所有函式都有一個prototype屬性,屬性值是一個Object例項物件,即該建構函式例項的原型。
  • 所有物件都有一個__proto__隱式屬性,指向這個物件的建構函式的prototype。
  • 當試圖得到一個物件的屬性時,如果這個物件本身沒有這個屬性,那麼會沿著它的__proto__這條鏈去找,這條鏈就是原型鏈。
  • 原型鏈的終點是null

如果有遺漏或者不對的地方,還請指正,先謝過~~~///(^v^)\~~

相關文章