1、JS中的繼承
當談到繼承時,JavaScript 只有一種結構:對象。每個實例對象(object)都有一個私有屬性(稱之為 proto )指向它的構造函數的原型對象(prototype)。
該原型對象也有一個自己的原型對象(__proto__),層層向上直到一個對象的原型對象為null。根據定義,null沒有原型,並作為這個原型鏈中的最後一個環節。
2、繼承屬性
JavaScript 對象是動態的屬性“包”(指其自己的屬性)。JavaScript 對象有一個指向一個原型對象的鏈。
當試圖訪問一個對象的屬性時,它不僅僅在該對象上搜尋,還會搜尋該對象的原型,以及該對象的原型的原型,依次層層向上搜索,直到找到一個名字匹配的屬性或到達原型鏈的末尾。
3、創建原型鏈的Demo
// 讓我們從一個函數裏創建一個對象o,它自身擁有屬性a和b的:
let f = function () {
this.a = 1;
this.b = 2;
}
/* 這麼寫也一樣
function f() {
this.a = 1;
this.b = 2;
}
*/
let o = new f(); // {a: 1, b: 2}
// 在f函數的原型上定義屬性
f.prototype.b = 3;
f.prototype.c = 4;
// 不要在 f 函數的原型上直接定義 f.prototype = {b:3,c:4};這樣會直接打破原型鏈
// o.[[Prototype]] 有屬性 b 和 c
// (其實就是 o.__proto__ 或者 o.constructor.prototype)
// o.[[Prototype]].[[Prototype]] 是 Object.prototype.
// 最後o.[[Prototype]].[[Prototype]].[[Prototype]]是null
// 這就是原型鏈的末尾,即 null,
// 根據定義,null 就是沒有 [[Prototype]]。
// 綜上,整個原型鏈如下:
// {a:1, b:2} ---> {b:3, c:4} ---> Object.prototype---> null
console.log(o.a); // 1
// a是o的自身屬性嗎?是的,該屬性的值為 1
console.log(o.b); // 2
// b是o的自身屬性嗎?是的,該屬性的值為 2
// 原型上也有一個'b'屬性,但是它不會被訪問到。
// 這種情況被稱為"屬性遮蔽 (property shadowing)"
console.log(o.c); // 4
// c是o的自身屬性嗎?不是,那看看它的原型上有沒有
// c是o.[[Prototype]]的屬性嗎?是的,該屬性的值為 4
console.log(o.d); // undefined
// d 是 o 的自身屬性嗎?不是,那看看它的原型上有沒有
// d 是 o.[[Prototype]] 的屬性嗎?不是,那看看它的原型上有沒有
// o.[[Prototype]].[[Prototype]] 為 null,停止搜索
// 找不到 d 屬性,返回 undefined