1. 程式人生 > >你不知道的JavasScript上篇·第五章·原型·上

你不知道的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' 出現的三種情況:

  1. 如果[[Prototype]] 鏈上層存在的foo,並沒有標記為只讀(writable:false),就直接在myObject中新增一個名為foo的屬性,它是==遮蔽屬性==
  2. 如果[[Prototype]] 鏈上層存在foo,但是被標記為只讀,那麼無法修改已有屬性或者在myObject建立遮蔽屬性。在嚴格模式下還會報錯,否則會被忽略
  3. 如果[[Prototype]] 鏈上層存在foo,並且是一個setter,那麼foo值會直接更新,不會被新增到myObject上

但是,有些情況下會隱式產生遮蔽,譬如下面的程式碼:

    var anotherObject = {
        a:2
    }
    var myObject = Object.create(anotherObject);
    anotherObject.a;//2
    myObject.a;//2
    myObject;//

myObject:
image.png

    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(目標物件) 獲取目標物件的原型鏈上的值

image.png

Object.setPrototypeOf(目標物件,要將原型鏈關聯的物件)
image.png

new Foo()這個含糊呼叫實際上並沒有直接建立關聯,只是間接完成了我們的目標:==關聯到其他物件的新物件==
做到這點更加直接的方式:Object.create(目標物件)

4、繼承

繼承意味著複製操作;

差異繼承:
基本原則是在描述物件行為時,使用其不同於普遍描述的特製。 (我的理解是隻用物件自身的而不用普遍繼承的像是toString(),valueOf()這種方法)