1. 程式人生 > >Javascript ProtoType

Javascript ProtoType

為了實現類似其他語言的繼承的特性, JavaScript中的實現是使用 原型鏈

規則是這樣的

在所有的物件上 都有一個 屬性叫 [[prototype]], 這個屬性屬於隱藏屬性, 即官方並沒有給定它的屬性名, 但目前瀏覽器上 給定的 屬性名是 proto
當訪問一個物件的某個屬性時, 會先在 該物件自己的屬性列表中查詢, 如果沒有 就到 proto 屬性指向的物件中查詢

var obj1 = {
    prop1: "prop1 value"
};
var obj2 = {
    prop2: "prop2 value"
};

這時, 獲取 obj2 的 屬性時, 只能獲取 prop2 的值

obj2.__proto__ = obj1;

這時 再嘗試 獲取 obj2.prop1 時 就能拿到值

上面的程式碼在瀏覽器上是OK的

但是 正規的做法並不是 手動去 修改某個 物件的 proto 值, 更何況 proto 並不是一個標準的屬性名稱, 在 node.js 中可能沒有

正規的做法是 先定義一個 函式

function Type1(){}

函式 也是物件, Type1 也有 proto 屬性, 先不必理會這一點, 所有的函式 還有 另一個屬性 名字是 prototype, 這個屬性 是在定義 每一個函式時, 有Javascript 引擎建立的, 當用戶呼叫 new Type1() 建立一個新的物件時, Javascript 引擎會將這個新物件的 proto

指向 Type1 的 prototype 屬性

function Type1(){};
var obj1 = new Type1();
console.log(obj1.__proto__ === Type1.prototype)
輸出:
true

這樣 obj1 就間接有個 Type1.prototype 物件下的所有屬性

function Type1(){};
Type1.prototype.prop1 = "prop1 value";
var obj1 = new Type1();
var obj2 = new Type1();

這時 obj1 和 obj2 都能訪問到 prop1 的值
但是注意, 這裡使用了類似 copy on write 的技術, 即當 寫入 物件obj1 或 obj2 的 prop1 屬性時,並沒有覆蓋掉 Type1.prototype 下的 prop1 屬性, 而是會在 物件obj1 或 obj2 的擁有的屬性中簡歷一個 prop1 屬性

obj1.prop1 = "other value"

這時 只有 obj1 訪問 prop1 屬性時的值是 “other value”, 而 obj2 以及 Type1.prototype 的 prop1 屬性並沒有變

再解釋一下 prototype 中的屬性 和 this.寫入的屬性的差別

function Type1(){
    this.prop1 = "prop1 value";
};
Type1.prototype.prop2 = "prop2 value";
var obj1 = new Type1();

這時 obj1 中已經直接有了 屬性 prop1, 而 prop2 屬性是間接的由 proto 訪問到的(注: new 的時候由 Javascript 引擎 將 Type1 的 prototype 設定給了 obj1 的 proto)
從記憶體的佔用空間上來說, prop1 是每個物件都有一份, 而 prop2 是所有物件共用一份
prototype 中的屬性 非常適合 只讀屬性 和 函式, 因為沒有必要再每個物件上都儲存一個只讀屬性 和 函式變數, 所以 常規的做法是 將 只讀屬性和函式都定義在 Type1 的 prototype 上

另一種寫法是使用 Object.create 方法, 該方法接受一個引數, 返回一個新建的物件, 並將這個物件的 proto 指向 給定的 引數物件

var Type1 = {
    prop1: "prop1 value",
    func1: function(){}
};
var obj1 = Object.create(Type1);

這時 obj1 中自己沒有任何屬性, 但它的 proto 屬性 已經指向了 Type1