原型基礎
- 每個函式都有一個prototype屬性,指向函式的原型物件
- 每個物件都一個私有屬性
__proto__
, 預設指向其建構函式的prototype - 在JS中所有函式都是
Function
構造出來的一種特殊物件,包括Function
本身;因此所有函式的__proto__
,指向Function.prototype
- 除函式外的所有物件都是由Object構造的,函式的原型物件也是;所以其
__proto__
指向Object.protptype。但Object這個函式的原型物件比較特殊,其__proto__
指向null
通過以上,我們可以推出,以下幾個表示式的結果都是true
function Car () {}
c1 = new Car()
/** 物件的__proto__指向建構函式的prototype */
c1.__proto__ === Car.prototype
/** 函式的__proto__指向Function的prototype */
Car.__proto__ === Function.prototype
Object.__proto__ === Function.prototype
Function.__proto__ === Function.prototype
/** 原型物件的__proto__指向Object.prototype */
Car.prototype.__proto__ === Object.prototype
Function.prototype.__proto__ === Object.prototype
/** Object這個建構函式的原型物件,的__proto__指向null */
Object.prototype.__proto__ === null
__proto__
是一個非標準的,但是很多瀏覽器都實現了的屬性,在新的語言標準中,用Object.get/setPrototypeOf()
代替物件的獲取/賦值操作
由於現代的的JS引擎後優化屬性訪問,如更改物件原型,會拖慢新效能,所以通常不建議更改原型,而是直接在函式建立時,就定義好原型
Object.create(proto, propertyObject)
function Car() {}
Car.prototype.say = function () {console.log('hello')}
const customCar = Object.create(Car.prototype, {
name: { value: 'carname1', writable:true },
price: {
get: ()=> 10,
set: () => {}
}
})
原型鏈
當操作物件的屬性時,如果在其自身找不到,就會去其原型物件 __proto__
找, 如果還沒有找到,就會去原型物件的原型物件上找,直至找到,或者到達Object.prototype
這就是JS的原型鏈機制。
有了原型鏈機制,物件就可以直接訪問原型鏈上的所有屬性,因此在JS中物件的屬性可以分為 自身屬性和原型屬性兩種
繼承
說白了繼承就是,一個物件擁有(繼承)了另一個物件的所有方法和屬性,通過原型鏈機制,可以很方便在JS中實現繼承
1、直接操作例項物件的 obj.__proto__
,指向另一個原型物件,支援非微軟版本瀏覽器,IE11+,且只能設定物件為原型
2、直接操作建構函式的prototype,指向一個例項物件, 相容性最好
3、用Object.create() 建立物件,建立時就指定好物件的原型: 相容IE9+,且可以一次性處理好原型,有助於優化,還可以設定null
4、用set/getPrototypeof,支援IE9+,能夠靈活的設定原型,還可以設定null