你不知道的JavasScript上篇·第五章·原型·上
1、[[Prototype]]
JS中的物件有一個特殊的[[Prototype]]內建屬性,其實就是對於其他物件的引用。幾乎所有的物件在建立時這個屬性都被賦予一個非空的值 (proto)
var myObject = {
a:2
}
myObject.a;//2
查詢a的過程:第一步預設[[Get]]操作檢查myObject本身有沒有這個屬性,有的話就直接用自身的,沒有就要繼續訪問物件的[[Prototye]]鏈
使用for...in遍歷物件時,原理和[[Prototype]] 類似,任何可以通過原型鏈訪問到且enumerable為true的屬性都會被列舉;
使用in操作符來檢查屬性在物件中是否存在,檢查物件自身&原型鏈
所有普通的[[Prototype]]鏈最終都會指向內建的Object.prototype。Object.prototype物件包含js中許多通用的功能,如.toString(),.valueOf(),.hasOwnProperty()
2、屬性設定和遮蔽
myObject.foo = 'bar'
這條語句詳細執行過程:
如果myObject中包含名為foo的普通屬性,這條語句就是==修改已有屬性值==
如果foo從未出現過(包含在[[Prototype]]上),則直接==新增在myObject==上
如果foo既出現在原型鏈上也出現在myObject上,myObject中的foo會==遮蔽==原型鏈上的foo
如果foo不直接存在於myObject中而是存在於原型鏈上層時,myObject.foo = 'bar' 出現的三種情況:
- 如果[[Prototype]] 鏈上層存在的foo,並沒有標記為只讀(writable:false),就直接在myObject中新增一個名為foo的屬性,它是==遮蔽屬性==
- 如果[[Prototype]] 鏈上層存在foo,但是被標記為只讀,那麼無法修改已有屬性或者在myObject建立遮蔽屬性。在嚴格模式下還會報錯,否則會被忽略
- 如果[[Prototype]] 鏈上層存在foo,並且是一個setter,那麼foo值會直接更新,不會被新增到myObject上
但是,有些情況下會隱式產生遮蔽,譬如下面的程式碼:
var anotherObject = {
a:2
}
var myObject = Object.create(anotherObject);
anotherObject.a;//2
myObject.a;//2
myObject;//
myObject:
anotherObject.hasOwnProperty('a');true
myObject.hasOwnProperty('a');//false
myObject.a++;
anotherObject.a;//2
myObject.a;//3
myObject.hasOwnProperty('a');//ture
神奇吧,myObjec.a經過++運算後myObject.hasOwnpProperty('a');變成true了
這是因為++操作相當於myObject.a = myObject.a+1;查詢屬性會通過[[Prototype]]找到anotherObject.a的值2,然後給這個值加1
接著用[[Put]]賦給myObject中新建的遮蔽屬性a
3、“類”函式
所有的函式預設都會擁有一個名為prototype的共有並且不可列舉的屬性,指向另一個物件。
這個物件通常被稱為原型
function Foo(){
...
}
Foo.prototype;//{}
一起看下一個例子:
function Foo(){
...
}
var a = new Foo();
Object.getPrototypeOf(a) === Foo.prototype;//true
呼叫new Foo()時,會建立a,其中一步就是將a內部的[[Prototype]]連結到Foo.prototype所指的物件。
Object.getPrototypeof()和Object.setPrototypeof()的用法
Object.getPrototypeOf(目標物件) 獲取目標物件的原型鏈上的值
Object.setPrototypeOf(目標物件,要將原型鏈關聯的物件)
new Foo()這個含糊呼叫實際上並沒有直接建立關聯,只是間接完成了我們的目標:==關聯到其他物件的新物件==
做到這點更加直接的方式:Object.create(目標物件)
4、繼承
繼承意味著複製操作;
差異繼承:
基本原則是在描述物件行為時,使用其不同於普遍描述的特製。 (我的理解是隻用物件自身的而不用普遍繼承的像是toString(),valueOf()這種方法)