JavaScript:繼承和原型鏈(譯)

原文:Inheritance and the prototype chain

譯者:youngsterxyf

對於具備基於類的編程語言(如Java或C++)經驗的程序員來說,JavaScript有點混亂,因爲它是一種動態語言,並且不提供class的實現(雖然關鍵字class是保留的,不可用作變量名)。

說到繼承,JavaScript只有一種結構:對象。每個對象都有一個內部鏈接指向另一個對象,這個對象稱爲原型(prototype)。那個原型對象也有自己的原型,如此直到某個對象以null作爲其原型。null,根據定義,沒有原型,作爲這種原型鏈的最後一環而存在。

以原型鏈實現繼承

繼承屬性

JavaScript對象可看作是動態地裝載屬性(這裏指自有屬性)的”包包”,並且每個對象都有一個鏈指向一個原型對象。如下即爲當嘗試訪問一個屬性時發生的事情:

// 假設有個對象o,其原型鏈如下所示:
// {a: 1, b: 2} ---> {b: 3, c: 4} ---> null
// 'a'和'b'是o的自有屬性。

// 本例中,someObject.[[Prototype]]指定someObject的原型。
// 這完全是一種標記符號(基於ECMAScript標準中所使用的),不可用於腳本中。

console.log(o.a);   // 1
// o有一個自有屬性'a'嗎?是的,其值爲1
 
console.log(o.b);   // 2
// o有自有屬性'b'嗎?是的,其值爲2
// o的原型也有一個屬性'b',但是這裏不會被訪問。這被稱爲“屬性隱藏”(property shadowing)

console.log(o.c);   // 4
// o有自有屬性'c'嗎?沒有,檢查它的原型
// o.[[Prototype]]有自有屬性'c'嗎?是的,其值爲4。

console.log(o.d);   // undefined
// o有自有屬性'd'嗎?沒有,檢查其原型
// o.[[Prototype]]有自有屬性'd'嗎?沒有,檢查其原型
// o.[[Prototype]].[[Prototype]]爲null,停止搜索,沒有找到屬性,返回undefined。

將一個屬性分配給一個對象會創建一個自有屬性。對於獲取和設置屬性的行爲規則,唯一的例外是當一個繼承而來的屬性帶有一個屬性值獲取器或設置器

繼承”方法”

JavaScript沒有以基於類的編程語言定義方法的形式出現的”方法”。JavaScript中,任何函數都可以作爲一個屬性被添加到一個對象。一個繼承而來的函數,操作起來與任何其他屬性相同,包括如上所示的屬性隱藏(在這裏,稱爲方法覆蓋)。

當執行一個繼承而來的函數時,this的值指向繼承對象,而不是原型對象,該函數是原型對象的自有屬性。

var o = {
    a: 2,
    m: function(b) {
        return this.a + 1;
    }
};
 
console.log(o.m()); // 3
// 這裏當調用o.m時,'this'引用o

var p = Object.create(o);
// p是一個繼承自o的對象
 
p.a = 12;   // 爲p創建一個自有屬性'a'
console.log(p.m()); // 13
// 這裏調用p.m時,'this'引用p
// 因此,當p繼承了o的函數m,'this.a'意味着p.a,p的自有屬性'a'

創建對象的不同方式,以及由此產生的原型鏈

以語法結構創建對象

var o = {a: 1};

// 新創建的對象o有Object.prototype作爲其[[Prototype]]
// o沒有名爲'hasOwnProperty'的自有屬性
// hasOwnProperty是Object.prototype的自有屬性。因此o從Object.prototype繼承了hasOwnProperty
// Object.prototype以null爲其prototype。
// o ---> Object.prototype ---> null
 
var a = ["yo", "whadup", "?"];

// 數組繼承自Array.prototype(它具有indexOf, forEach等方法)。
// 該原型鏈如下所示:
// a ---> Array.prototype ---> Object.prototype ---> null

function f() {
    return 2;
}

// 函數繼承自Function.prototype(它具有call,bind等方法):
// f ---> Function.prototype ---> Object.prototype ---> null

使用構造器

JavaScript中,”構造器””就”是一個恰好以new操作符調用的函數。

function Graph() {
    this.vertexes = [];
    this.edges = [];
}

Graph.prototype = {
    addVertex: function(v) {
        this.vertexes.push(v);
    }
};

var g = new Graph();
// g是一個帶有自有屬性'vertexes'和'edges'的對象。
// 執行new Graph()後,g.[[Prototype]]是Graph.prototype的值。

使用Object.create

ECMAScript 5引入了一個新方法:Object.create。調用這個方法會創建一個新對象。這個對象的原型是該函數的第一個參數:

var a = {a: 1};
// a ---> Object.prototype ---> null

var b = Object.create(a);
// b ---> a ---> Object.prototype ---> null
console.log(b.a);   // 1 (繼承而來)

var c = Object.create(b);
// c ---> b ---> a ---> Object.prototype ---> null

var d = Object.create(null);
// d ---> null
console.log(d.hasOwnProperty);  // undefined,因爲d並不繼承自Object.prototype
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章