白話JavaScript原型鏈和繼承

Claiyre發表於2021-08-07

原型基礎

  1. 每個函式都有一個prototype屬性,指向函式的原型物件
  2. 每個物件都一個私有屬性 __proto__, 預設指向其建構函式的prototype
  3. 在JS中所有函式都是Function構造出來的一種特殊物件,包括Function本身;因此所有函式的__proto__,指向Function.prototype
  4. 除函式外的所有物件都是由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

相關文章