基本知識
在js
中可以理解為只有一種結(jié)構(gòu)逻悠,那就是 object
叙谨,每個實例對象(object
)都會有一個私有屬性(__proto__
)指向他的原型對象(prototype
)。該原型對象(prototype
)自己也有__proto__
欧宜,層層向上指,直到一個對象的原型對象為null
疾渣。根據(jù)定義篡诽,null沒有原型,為原型鏈的最后一個環(huán)節(jié)榴捡。
幾乎所有 JavaScript 中的對象都是位于原型鏈頂端的Object
的實例杈女。
基于原型鏈的繼承
繼承屬性
JavaScript 對象是動態(tài)的屬性“包”(指其自己的屬性)。JavaScript 對象有一個指向一個原型對象的鏈吊圾。當試圖訪問一個對象的屬性時达椰,它不僅僅在該對象上搜尋,還會搜尋該對象的原型项乒,以及該對象的原型的原型啰劲,依次層層向上搜索,直到找到一個名字匹配的屬性或到達原型鏈的末尾
// eg:
let func = function(){
this.a = 1;
this.b = 2;
}
let obj = new func() // { a:1, b:2 }
func.prototype.b = 3;
func.prototype.c = 4;
// 那么我們就可以得到一個原型鏈
obj // { a:1, b:2 }
obj.__proto__ // { b:3, c:4 }
obj.__proto__.__proto__ // Object.prototype
obj.__proto__.__proto__.__proto__ // null
// 原型鏈的末尾檀何,即 null
// 所以整條原型鏈如下:
{a:1, b:2} ---> {b:3, c:4} ---> Object.prototye---> null
// 那么檢查一下:
obj.a // 1
obj.b // 2
obj.c // 4
obj.d // undefined (因為未在原型鏈上被找到)
// 疑問:
// 原型上也有一個'b'屬性,但是它不會被訪問到.這種情況稱為"屬性遮蔽 (property shadowing)"
繼承方法
在js中并沒有基于類繼承的方法蝇裤,任何函數(shù)都可以添加到對象上作為對象的屬性。函數(shù)的繼承與其他的屬性繼承沒有差別频鉴,包括上面的“屬性遮蔽”(這種情況相當于其他語言的方法重寫)栓辜。
當繼承的函數(shù)被調(diào)用的時候,可以簡單理解為在哪里被調(diào)用this就指向哪里垛孔,因為this會指向當前繼承的對象藕甩,而不是繼承函數(shù)所在的原型對象
let obj2 = {
a:1,
b:function(){
return this.a + 1;
}}
console.log(obj2.b()) // 2 this指向了obj2
let obj3 = Object.create(obj2)
obj3.a = 4;
console.log(obj3.b())// 5 this指向了obj3
}
使用不同的方法來創(chuàng)建對象和生成原型鏈
語法結(jié)構(gòu)創(chuàng)建的對象
let obj4 = { a:1 };
// obj4 繼承了Object.proptype上的所有屬性
// 原型鏈:{ a:1 } => Object.proptype => null
let obj5 = [ 1, 2, 3 ];
// obj5 繼承了Array.proptype上的所有屬性 但是js中array也屬于對象,
// 所以Array.proptype 也繼承了Object.proptype上的所有屬性周荐。
// 原型鏈:[ 1, 2, 3 ] => Array.proptype => Object.proptype => null
function obj6(){
return 2;
}
// 同樣obj6繼承了Function.proptype上的所有屬性狭莱,但是js中function也屬于對象
// 原型鏈:obj6 => Function.prototype => Object.proptype => null
構(gòu)造器創(chuàng)建的對象
在 JavaScript 中,構(gòu)造器其實就是一個普通的函數(shù)概作。當使用 new 操作符 來作用這個函數(shù)時贩毕,它就可以被稱為構(gòu)造方法(構(gòu)造函數(shù))。
function func1() {
this.list = [];
};
func1.proptype.addlist = function(item){
this.list.push(item)
};
let func2 = new func1;
// func2 => func1.proptype => Object.Proptype => null
Object.create 創(chuàng)建的對象
ECMAScript 5 中引入了一個新方法:Object.create()
仆嗦』越祝可以調(diào)用這個方法來創(chuàng)建一個新對象。新對象的原型就是調(diào)用 create 方法時傳入的第一個參數(shù):
let obj7 = { a : 3 }
// obj7 => Object.proptype => null
let obj8 = Object.create(obj7);
// obj8 => obj7 => Object.proptype => null
let obj9 = Object.create(obj8)
//obj9 => obj8 => obj7 => Object.proptype => null
var obj10 = Object.create(null);
// d ---> null
console.log(obj10.hasOwnProperty);
// undefined, 因為d沒有繼承Object.prototype
class關(guān)鍵字創(chuàng)建的對象
ECMAScript6 引入了一套新的關(guān)鍵字用來實現(xiàn) class瘩扼。使用基于類語言的開發(fā)人員會對這些結(jié)構(gòu)感到熟悉谆甜,但它們是不同的。JavaScript 仍然基于原型集绰。這些新的關(guān)鍵字包括 class
, constructor
规辱,static
,extends
和 super
栽燕。
class Check{
constructor(a){
this.a = a;
}
}
class AddNum extends Check{
constructor(num) {
super(num);
}
get addnum() {
return this.num + this.num
}
set setNum(num) {
this.num = num
}
}
let test = new AddNum(3)
// test => {a:3 ,addnum:NaN} => AddNum的原型對象 => Check的原型對象 => Object.proptype => null
性能
在原型鏈上查找屬性比較耗時罕袋,對性能有副作用改淑,這在性能要求苛刻的情況下很重要。另外浴讯,試圖訪問不存在的屬性時會遍歷整個原型鏈
遍歷對象的屬性時朵夏,原型鏈上的每個可枚舉屬性都會被枚舉出來。要檢查對象是否具有自己定義的屬性榆纽,而不是其原型鏈上的某個屬性仰猖,則必須使用所有對象從Object.prototype繼承的 hasOwnProperty 方法。下面給出一個具體的例子來說明它:
let obj11 = {
a:1
}
let obj12 = Object.create(obj11)
console.log(obj11.hasOwnProperty('a'));
// true
console.log(obj11.hasOwnProperty('b'));
// false
console.log(obj12.hasOwnProperty('a'));
// false
console.log(obj12.__proto__.hasOwnProperty('a'));
// true
hasOwnProperty 是 JavaScript 中處理屬性并且不會遍歷原型鏈的方法之一奈籽。(另一種方法: Object.keys())
Object.keys(obj12)
// []
Object.keys(obj11)
// ["a"]
注意:檢查屬性是否undefined還不夠饥侵。該屬性可能存在,但其值恰好設置為undefined衣屏。
總結(jié)
var o = new Foo();
// JavaScript 實際上執(zhí)行的是:
var o = new Object();
o.__proto__ = Foo.prototype;
Foo.call(o);
// 然后當你執(zhí)行
o.a
// 它檢查o是否具有a屬性躏升。如果沒有,它會查找 Object.getPrototypeOf(o).a狼忱,如果仍
// 舊沒有膨疏,它會繼續(xù)查找 Object.getPrototypeOf(Object.getPrototypeOf(o)).a。
遵循ECMAScript標準藕赞,someObject.[[Prototype]]
符號是用于指向 someObject
的原型。從 ECMAScript 6 開始卖局,[[Prototype]]
可以通過Object.getPrototypeOf()
和Object.setPrototypeOf()
訪問器來訪問斧蜕。這個等同于 JavaScript 的非標準但許多瀏覽器實現(xiàn)的屬性 __proto__
。