學習JavaScript"原型"

在上一節屢次提到[[Prototype]]鏈,但沒解釋它究竟是什麼,那麼這一節咱們來詳細介紹一下。學習

[[prototype]]

認識[[prototype]]

JavaScript中的每一個對象都擁有一個原型對象。其實就是對於其餘對象的引用。幾乎全部的對象在建立時[[Prototype]]屬性都會被賦予一個非空的值。原型對象也可能擁有原型,並從中繼承方法和屬性,一層一層、以此類推。這種關係常被稱爲原型鏈。(mdn有詳細的說明)ui

爲了學習體驗,咱們打開控制檯而後輸入如下代碼:this

var object = {
    name:"tom"
};

object.__proto__
複製代碼

能夠看到控制檯打印了一堆屬性和方法,這個咱們後續說明。spa

思考下面的代碼:prototype

var object = {
    name:"tom"
}

var newObject = Object.create(object);

newObject.name; // "tom"
複製代碼

這裏[[Prototype]]引用就發揮了做用,當咱們試圖訪問newObject對象的name屬性時會觸發[[Get]]操做,[[Get]]操做的第一步是檢查對象自己是否有這個屬性,若是有的話就使用它。但若是name不在newObject中,就要使用對象的[[Prototype]]鏈了。設計

如今newObject對象的[[Prototype]]關聯到了object。顯然newObject.name並不存在,可是它在 object中找到了name屬性。可是若是object中也找不到name而且[[Prototype]]鏈不爲空,就會繼續查找下去。3d

這個過程會持續到找到匹配的屬性名或者查找完整條[[Prototype]]鏈。若是是後者的話,[[Get]]操做的返回值是undefined。(for..in遍歷對象時原理和查找[[Prototype]]鏈相似)code

[[prototype]]的盡頭

從圖中,能夠看出來[[Prototype]]鏈最終都會指向內置的Object.prototype,這上面有許多咱們熟悉的功能,好比toString()valueOf()hasOwnProperty(..).isPrototypeOf(..)等等。cdn

對象屬性設置

給一個對象設置屬性並不單單是添加一個新屬性或者修改已有的屬性值。對象

如今咱們來學習一下這個過程:

var object = {};

object.name = "tom";
複製代碼
  • 若是object對象中包含名爲name的數據訪問屬性,那麼這條賦值語句只會修改已有的屬性值。
  • 若是name不在object中,[[Prototype]]鏈就會被遍歷,相似[[Get]]操做。若是原型鏈上找不到namename就會被直接添加到object上。
  • 若是屬性名name既出如今obejct中也出如今obejct[[Prototype]]鏈上,那麼就會發生屏蔽。obejct中包含的name屬性會屏蔽原型鏈上的全部name屬性,所以object.name老是會選擇原型鏈中最底層的name屬性。
  • 最後一種狀況略微複雜咱們單獨說明一下,若是name僅存在於原型鏈上層,那麼賦值語句object.name = "tom"的行爲就會有些不一樣。

下面分析一下若是name是存在於原型鏈上時object.name = "tom"會出現的三種狀況。

  1. 若是在[[Prototype]]鏈上存在名爲name的數據訪問屬性而且沒有被標記爲只讀(writable:false),那就會直接在obejct中添加一個名爲name的新屬性,它是屏蔽屬性。
var object = {};

var other = Object.create(object);

other.name = "tom"; //默認other的defineProperty的writable屬性爲true

console.log(object.name); //tom
複製代碼
  1. 若是在[[Prototype]]鏈上存在name,可是它被標記爲只讀(writable:false),那麼沒法修改已有屬性或者在obejct上建立屏蔽屬性。
var object = {};

var other = Object.create(object);

other.name = "tom"; 

Object.defineProperty(other,"name",{
    writable:false
});

console.log(object.name); //undefined
複製代碼
  1. 若是在[[Prototype]]鏈上存在name而且他是一個Setter,那就必定會調用這個Settername不會被添加到object上,也不會從新定義name這個Setter
var object = {};

var other = Object.create(object);

Object.defineProperty(other,"name",{
    get:function(){
      return this._name;  
    },
    set:function(){
        this._name = "tom";
    }
});

console.log(object.name); //undefined
複製代碼
  • 注意屬性產生的隱式屏蔽。代碼以下:
var object = {
    num: 6
};

var newObject = Object.create(object);

console.log(newObject.num);  //6
console.log(object.num);  //6

console.log(newObject.hasOwnProperty("num"));  //false
console.log(object.hasOwnProperty("num"));  //true

newObject.num++;

console.log(newObject.num);  //7
console.log(object.num);  //6
複製代碼

這裏其實也比較好說明,執行++操做至關於newObject.num=newObject.num+1。所以++運算首先會經過[[Get]]操做在[[Prototype]]查找屬性num並從object.name獲取當前屬性值,而後給這個值加1,接着用[[Set]]將值7賦給newObject中新建的屏蔽屬性name

修改委託屬性時必定要當心。惟一的辦法是object.num++

小結

到此簡單的介紹了一下原型的基本概念。下一節將介紹原型更多的特性。

參考

相關文章
相關標籤/搜索