1. 程式人生 > 其它 >JS 原型、原型鏈,不再傻傻分不清楚,一文徹底讀懂原型鏈

JS 原型、原型鏈,不再傻傻分不清楚,一文徹底讀懂原型鏈

原型鏈圖

圖很重要!圖很重要!圖很重要!強烈建議讓圖片置頂起來,結合圖片閱讀下去。

普通物件與函式物件

JS 中的物件分為 普通物件函式物件

型別 屬性 例子 對應上面的圖
普通物件 __proto__ {}、new 例項 f1、f2、o1、o2、x.prototype
函式物件 __proto__ 以及 prototype ObjectFunctionRegExp Foo、Object、Function

__proto__prototype 的作用

屬性 作用
__proto__ 內含屬性 constructor__proto__
prototype 預設有 constructor 屬性,用於記錄例項是由哪個建構函式建立

準則

  1. 原型物件的 constructor 指向建構函式本身
  2. 例項的 __proto__f1.__prototype__) 和原型物件(Foo.prototype)指向同一個地方
// function Foo()
function Foo(){}

var f1 = new Foo()
var f2 = new Foo()

f1.__proto__ === Foo.prototype  // 準則 2
f2.__proto__ === Foo.prototype  // 準則 2
Foo.prototype.__proto__ === Object.prototype    // 準則 2 (Foo.prototype 原型物件也是普通的物件)
Object.prototype.__proto__ === null // 原型鏈結束
Foo.prototype.constructor === Foo   // 準則 1
Foo.__proto__ === Function.prototype    // 準則 2 (Foo 是 Function的例項)


// function Object()
var o1 = new Object()
var o2 = new Object()

o1.__proto__ === Object.prototype   // 準則 2
o2.__proto__ === Object.prototype   // 準則 2
Object.prototype.__proto__ === null // 原型鏈結束
Object.prototype.constructor === Object // 準則 1
Object.__proto__ === Function.prototype // 準則 2 (Object 是 Function的例項,這...)
Object.__proto__.__proto__ === Object.prototype // 準則 2 (Object.__proto__ 指向 Function.prototype)


// Function Function()
Function.__proto__ = Function.prototype // 準則 2 (自己是自己的例項,啊這...)
Function.prototype.constructor = Function; // 準則 1
Function.prototype.__proto__  === Object.prototype  // 準則 2 (Function.prototype 原型物件也是普通的物件)

題目

接下來是實操演練了

function person() {
    this.age = 10;
}

var p3 = new person()

person.prototype = {
    age: 1,
    getAge: function() {
        console.log(this.age);
    }
}
var p1 = new person();
var p2 = new person();
p1.getAge();  // 10 先找 person 本身的 age
p2.age = 12;
person.prototype.age = 15;
p2.getAge();  // 12
p3.getAge();  // Uncaught TypeError: p3.getAge is not a function
p3.constructor.prototype.getAge(); // 1,p3[.__proto__].constructor 就是 person 
person.__proto__.age = 18;  // person.__proto__ === Object.__proto__ === Function.prototype,這裡可以認為修改的是 Object.age = 18 也可以是 Function.prototype.age = 18
p1.__proto__.getAge();  // 15 找原型鏈上的 person,即 person.prototype, p1.__proto__ === person.prototype
p1.__proto__.age = 20;  // 即 person.prototype.age = 20
person.prototype.getAge();  //20


p1.constructor === person;  // false, person 沒有 constructor 屬性,例項化前被改寫了
p1.constructor === Object;  // true, 沿著 [[Prototype]] 鏈,在 Object 找到 constructor 屬性
p1.__proto__.constructor === Object // true 

p3.constructor === person;  // true, person 有 constructor 屬性,例項化前沒有被改寫了
p3.constructor === Object;  // false, 沿著 [[Prototype]] 鏈,在 Object 找到 constructor 屬性
p3.__proto__.constructor === Object // false 

p1 屬性

p3 屬性

p1 沒有 .constructor 屬性,所以它沿者 [[Prototype]] 鏈向上委託到了 person.prototype。但是這個物件也沒有 .constructor,因為在例項化之前 person.prototype 指向了另一個函式。

所以它繼續委託,這次輪到了 Object.prototype,委託鏈的最頂端。那個 物件上確實擁有 .constructor,它指向內建的 Object(..) 函式。

總結

  1. 所有物件的 __proto__ 最終指向 Object.prototype,而 Object.prototype__proto__ 指向 null
    • x.__proto__.[__proto__.] === Object.prototype
    • Object.prototype === null
  2. 例項的 __proto__ 指向其建構函式的原型物件 X.prototype
    • x.__proto__ === X.prototype
  3. 原型物件也是普通物件