Javacript 中有一個思想:萬物皆物件,幾個基礎型別(String, Number, Boolean, Null, Undefined, Symbol),幾個引用型別(Array, Date, Function, Object, Regexp)本質上都是物件。
那麼物件之間如何實現關聯(也就是OO裡面的繼承)呢?js中通過prototype(原型)來實現。每個物件都有一個__proto__指標,指向上一個原型,這就像是一個連結串列。最頂端的object的原型指向null, 代表終結。 所以,當我們定義一個變數:
let name = new String('allen')
//let name = 'allen'
console.log('name:', name)
console.log('name.__proto__:', name.__proto__)
console.log('name.__proto__.__proto__:',name.__proto__.__proto__)
console.log('name.__proto__.__proto__.__proto__:', name.__proto__.__proto__.__proto__)
複製程式碼
輸出:
name: [String: 'allen']
name.__proto__: [String: '']
name.__proto__.__proto__: {}
name.__proto__.__proto__.__proto__: null
[Finished in 0.1s]
複製程式碼
我們要實現繼承(至少看上去是繼承的樣子)有三種方法:
- Object.create(),
- Function.prototype={},
- class extend
1. Object.create()
看例子:
let dog = {
name: 'dog'
}
let mardDog = Object.create(dog)
複製程式碼
這裡 mardDog
是一個新的空物件,存有一個指標__proto__
,指向的是 dog
物件, dog
物件中也有一個__proto__
,指向的是Object
,Object
中也有一個__proto__
,指向的是null
。
這就是原型鏈。利用Object.create(),我們可以建立多個物件,dog
物件都是他們的原型,那麼他們可不可以改變dog裡面的屬性呢。答案是可以 的,但不建議。程式碼如下:
mardDog.name = 'mardDog'
/*
dog ====> {name: 'dog'}
mardDog ====> {name: 'mardDog'}
*/
複製程式碼
可以看到,我們並沒有能改變原型中的屬性。而下面這種方式:
mardDog.__proto__.name = 'xxxDog'
/*
dog ====> {name: 'xxxDog'}
mardDog ====> {name: 'mardDog'}
*/
複製程式碼
我們成功改變了上一層的屬性。 那麼,為什麼我們不推薦使用__proto__去改變原型中的共有屬性呢?因為這種方法非常慢,並且會嚴重影響程式。事實上,__proto__從來沒有被寫進規範,但是瀏覽器廠商都實現了它。
2. Function.prototype = {}
這個方法其實是利用建構函式來實現 先看例子:
function dog (){
this.name = 'dog'
this.age = 1
}
let dog1 = new dog()
function mardDog (){
this.yiel = function (){
console.log(this.name)
}
}
mardDog.prototype = new dog()
let mardDog1 = new mardDog()
mardDog1.yiel()
複製程式碼
輸出:
dog
[Finished in 0.1s]
複製程式碼
可以看到,我們成功實現了mardDog 對 dog的繼承
3. class extend
es6中實現了 class
這個關鍵字,雖然只是語法糖,本質上是方法二的封裝,但這種思路對熟悉OO的開發者是很友好的,並且把js中令人迷惑的原型封裝了起來,使它變得更容易開發。
例子:
class dog {
constructor () {
this.name = 'dog'
this.age = 1
}
}
class mardDog extends dog {
constructor () {
super()
}
yiel () {
console.log(this.name)
}
}
let mardDog1 = new mardDog()
mardDog1.yiel()
複製程式碼
輸出:
dog
複製程式碼
這樣我們成功地實現了繼承
總結
由於es6 是大勢所趨,建議在工程環境中使用clas來實現物件的繼承。當然原型以及原型鏈的原理是必須掌握的。es6中還有一些方法也是十分有用。以下:
Object.getPrototypeOf(childobj) 顧名思義,該方法得到childobj的prototype,也可以理解為父類。
Object.setPrototypeOf(childObj, obj)這個方法是將childObj設為obj的繼承類。由於前面提到過,對prototype的操作十分微妙,所以這個方法還是能不用就不用,可以用Object.create(obj)來代替
其他方法參照MDN上的解釋