[翻譯]JavaScript秘密花園 – Object, Prototype

來源:互聯網
上載者:User
文章目錄
  • 關於作者(The authors)
  • 貢獻者(Contributors)
  • 許可(License)
  • 中文翻譯(Chinese Translation)
  • 對象作為資料類型(Objects as a data type)
  • 訪問屬性(Accessing properties)
  • 刪除屬性(Deleting properties)
  • 屬性名稱的文法(Notation of keys)
  • 屬性尋找(Property lookup)
  • 原型屬性(The prototype property)
  • 效能(Performance)
  • 擴充內建類型的原型(Extension of native prototypes)
  • 總結(In conclusion)

簡介(Intro)

JavaScript秘密花園是一個不斷更新的文檔,主要關心JavaScript一些古怪用法。
對於如何避免常見的錯誤,難以發現的問題,以及效能問題和不好的實踐給出建議,
初學者可以籍此深入瞭解JavaScript的語言特性。

JavaScript秘密花園不是用來教你JavaScript。為了更好的理解這篇文章的內容,
你需要事先學習JavaScript的基礎知識。在Mozilla開發人員網路中有一系列非常棒的JavaScript學習嚮導。

關於作者(The authors)

這篇文章的作者是兩位Stack Overflow的使用者, Ivo Wetzel
(寫作) 和 Zhang Yi Jiang (設計)。

貢獻者(Contributors)
  • Caio Rom?o (拼字檢查)
  • Andreas Blixt (語言修正)
許可(License)

JavaScript花園在MIT license許可協議下發布,並存放在開源社區GitHub。
如果你發現錯誤或者打字錯誤,請file an issue或者pull request。
你也可以在Stack Overflow的聊天室JavaScript room找到我們。

中文翻譯(Chinese Translation)
  • JavaScript Garden - 原文
  • JavaScript Garden - 中文翻譯
  • 譯作者:三生石上

本中文翻譯由三上石上原創,部落格園首發,轉載請註明出處。

對象(Objects) #top

JavaScript中所有變數都是對象,除了兩個例外nullundefined

false.toString() // 'false'
[1, 2, 3].toString(); // '1,2,3'

function Foo(){}
Foo.bar = 1;
Foo.bar; // 1

一個常見的誤解是數位字面值(literal)不是對象。這是因為JavaScript解析器的一個錯誤,
它試圖將點操作符解析為浮點數字面值的一部分。

2.toString(); // 出錯:SyntaxError

有很多變通方法可以讓數位字面值看起來像對象。

2..toString(); // 第二個點號可以正常解析
2 .toString(); // 注意點號前面的空格
(2).toString(); // 2先被計算
對象作為資料類型(Objects as a data type)

JavaScript的對象可以作為雜湊表使用,主要用來儲存命名的鍵與值的對應關係。

使用對象的字面文法 - {} - 可以建立一個簡單對象。這個新建立的對象從Object.prototype
繼承下面,沒有任何自訂屬性。

var foo = {}; // 一個Null 物件

// 一個新對象,擁有一個值為12的自訂屬性'test'
var bar = {test: 12};
訪問屬性(Accessing properties)

有兩種方式來訪問對象的屬性,點操作符或者中括弧操作符。

var foo = {name: 'Kitten'}
foo.name; // kitten
foo['name']; // kitten

var get = 'name';
foo[get]; // kitten

foo.1234; // SyntaxError
foo['1234']; // works

兩種文法是等價的,但是中括弧操作符在下面兩種情況下依然有效
- 動態設定屬性
- 屬性名稱不是一個有效變數名(譯者註:比如屬性名稱中包含空格,或者屬性名稱是JS的關鍵詞)
(譯者註:在JSLint文法偵查工具中,點操作符是推薦做法)

刪除屬性(Deleting properties)

刪除屬性的唯一方法是使用delete操作符;設定屬性為undefined或者null並不能真正的刪除屬性,
僅僅是移除了屬性和值的關聯。

var obj = {
bar: 1,
foo: 2,
baz: 3
};
obj.bar = undefined;
obj.foo = null;
delete obj.baz;

for(var i in obj) {
if (obj.hasOwnProperty(i)) {
console.log(i, '' + obj[i]);
}
}

上面的輸出結果有bar undefinedfoo null - 只有baz被真正的刪除了,所以從輸出結果中消失。

屬性名稱的文法(Notation of keys)
var test = {
'case': 'I am a keyword so I must be notated as a string',
delete: 'I am a keyword too so me' // 出錯:SyntaxError
};

對象的屬性名稱可以使用字串或者一般字元聲明。但是由於JavaScript解析器的另一個錯誤設計,
上面的第二種聲明方式在ECMAScript 5之前會拋出SyntaxError的錯誤。

這個錯誤的原因是delete是JavaScript語言的一個關鍵詞;因此為了在更低版本的JavaScript引擎下也能正常運行,
必須使用字串字面值聲明方式。

原型(The prototype) #top

JavaScript不包含傳統的類繼承模型,而是使用prototypical原型模型。

雖然這經常被當作是JavaScript的缺點被提及,其實基於原型的繼承模型比傳統的類繼承還要強大。
實現傳統的類繼承模型是很簡單,但是實現JavaScript中的原型繼承則要困難的多。
(It is for example fairly trivial to build a classic model on top of it, while the
other way around is a far more difficult task.)

由於JavaScript是唯一一個被廣泛使用的基於原型繼承的語言,所以理解兩種繼承模式的差異是需要一定時間的。

第一個不同之處在於JavaScript使用原型鏈的繼承方式。

注意: 簡單的使用Bar.prototype = Foo.prototype將會導致兩個對象共用相同的原型。
因此,改變任意一個對象的原型都會影響到另一個對象的原型,在大多數情況下這不是希望的結果。

function Foo() {
this.value = 42;
}
Foo.prototype = {
method: function() {}
};

function Bar() {}

// 設定Bar的prototype屬性為Foo的執行個體對象
Bar.prototype = new Foo();
Bar.prototype.foo = 'Hello World';

// 修正Bar.prototype.constructor為Bar本身
Bar.prototype.constructor = Bar;

var test = new Bar() // 建立Bar的一個新執行個體

// 原型鏈
test [Bar的執行個體]
Bar.prototype [Foo的執行個體]
{ foo: 'Hello World' }
Foo.prototype
{method: ...};
Object.prototype
{toString: ... /* etc. */};

上面的例子中,test對象從Bar.prototypeFoo.prototype繼承下來;因此,
它能否訪問Foo的原型方法method。但是它不能訪問Foo的執行個體屬性value
因為這個屬性在Foo的建構函式中定義。
(But it will not have access to the property value of a
Foo instance, since that property gets defined in the constructor
of Foo. But this constructor has to be called explicitly.)

(譯者註:我認為這個描述是錯誤的,test.value是可以訪問的。
因為在設定Bar.prototype = new Foo();時,value也就成為Bar.prototype上的一個屬性。
如果你有不同觀點,可以到我的部落格評論。)

注意: 不要使用Bar.prototype = Foo,因為這不會執行Foo的原型,而是指向函數Foo
因此原型鏈將會回溯到Function.prototype而不是Foo.prototype,因此method將不會在Bar的原型鏈上。

屬性尋找(Property lookup)

當尋找一個對象的屬性時,JavaScript會向上遍曆原型鏈,直到找到給定名稱的屬性為止。

到尋找到達原型鏈的頂部 - 也就是Object.prototype - 但是仍然沒有找到指定的屬性,就會返回undefined。

原型屬性(The prototype property)

當原型屬性用來建立原型鏈時,可以把任何類型的值賦給它(prototype)。
然而將原子類型賦給prototype的操作將會被忽略。

function Foo() {}
Foo.prototype = 1; // no effect

而將對象賦值給prototype,正如上面的例子所示,將會動態建立原型鏈。

效能(Performance)

如果一個屬性在原型鏈的上端,則對於尋找時間將帶來不利影響。特別的,試圖擷取一個不存在的屬性將會遍曆整個原型鏈。

並且,當使用for-in迴圈遍曆對象的屬性時,原型鏈上的所有屬性都將被訪問。

擴充內建類型的原型(Extension of native prototypes)

一個錯誤特性被經常使用,那就是擴充Object.prototype或者其他內建類型的原型對象。

這種技術被稱之為monkey patching並且會破壞封裝。雖然它被廣泛的應用到一些JS類庫中比如Prototype,
但是我仍然不認為為內建類型添加一些非標準的函數是個好主意。

擴充內建類型的唯一理由是為了和新的JavaScript保持一致,比如Array.forEach
(譯者註:這是編程領域常用的一種方式,稱之為Backport,也就是將新的補丁添加到老版本中。)
The only good reason for extending a built-in prototype is to backport
the features of newer JavaScript engines; for example,
Array.forEach.

總結(In conclusion)

在寫複雜的JavaScript應用之前,充分理解原型鏈繼承的工作方式是每個JavaScript程式員必修的功課。
要提防原型鏈過長帶來的效能問題,並知道如何通過縮短原型鏈來提高效能。
更進一步,絕對不要擴充內建類型的原型,除非是為了和新的JavaScript引擎相容。

本中文翻譯由三上石上原創,部落格園首發,轉載請註明出處。

JavaScript Garden - 中文翻譯

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.