JS 原型、原型鏈,不再傻傻分不清楚,一文徹底讀懂原型鏈
阿新 • • 發佈:2021-07-02
原型鏈圖
圖很重要!圖很重要!圖很重要!強烈建議讓圖片置頂起來,結合圖片閱讀下去。
普通物件與函式物件
JS 中的物件分為 普通物件
和 函式物件
型別 | 屬性 | 例子 | 對應上面的圖 |
---|---|---|---|
普通物件 | __proto__ |
{}、new 例項 |
f1、f2、o1、o2、x.prototype |
函式物件 | __proto__ 以及 prototype |
Object 、Function 、RegExp 等 |
Foo、Object、Function |
__proto__
和 prototype
的作用
屬性 | 作用 |
---|---|
__proto__ |
內含屬性 constructor 和 __proto__ |
prototype |
預設有 constructor 屬性,用於記錄例項是由哪個建構函式建立 |
準則
- 原型物件的
constructor
指向建構函式本身 - 例項的
__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(..)
函式。
總結
- 所有物件的
__proto__
最終指向Object.prototype
,而Object.prototype
的__proto__
指向null
x.__proto__.[__proto__.] === Object.prototype
Object.prototype === null
- 例項的
__proto__
指向其建構函式的原型物件X.prototype
x.__proto__ === X.prototype
- 原型物件也是普通物件