[翻譯] Javascript 中的對(duì)象字面量很酷,你覺得呢钞支?

ECMAScript 2015 之前茫蛹,Javascript 中的對(duì)象字面量(又叫做對(duì)象初始化器)是相當(dāng)簡(jiǎn)單的,它可以定義2種屬性:

  • 成對(duì)的靜態(tài)屬性名和值 { name1: value1 }
  • 通過 getters { get name(){..} }setters { set name(val){..} } 定義的動(dòng)態(tài)計(jì)算屬性值

說來遺憾烁挟,一個(gè)簡(jiǎn)單的例子就可以表示對(duì)象字面量的所有可能性:

    var myObject = {  
      myString: 'value 1',
      get myNumber() {
        return this.myNumber;
      },
      set myNumber(value) {
        this.myNumber = Number(value);
      }
    };
    myObject.myString; // => 'value 1'  
    myObject.myNumber = '15';  
    myObject.myNumber; // => 15  

JavaScript 是一種基于原型繼承的語言婴洼,所以啥都是個(gè)對(duì)象。 所以當(dāng)處理對(duì)象的創(chuàng)建撼嗓、原型的設(shè)置與訪問時(shí)柬采,它必須提供簡(jiǎn)單的構(gòu)造方法欢唾。

定義一個(gè)對(duì)象然后設(shè)置它的原型是普遍流程。我常常覺得原型的設(shè)置應(yīng)該能直接在字面量里用一條語句實(shí)現(xiàn)粉捻。

很不幸匈辱,字面量的限制不允許這樣簡(jiǎn)單直接的實(shí)現(xiàn)方案。你不得不使用 Object.create() 配合字面量來設(shè)置原型:

    var myProto = {  
      propertyExists: function(name) {
        return name in this;    
      }
    };
    var myNumbers = Object.create(myProto);  
    myNumbers['array'] = [1, 6, 7];  
    myNumbers.propertyExists('array');      // => true  
    myNumbers.propertyExists('collection'); // => false  

我認(rèn)為這個(gè)方案很不方便杀迹。 JavaScript 是基于原型的亡脸,為什么設(shè)置對(duì)象的原型要這么痛苦?

幸運(yùn)的是 JavaScript 在進(jìn)化树酪,它許多相當(dāng)令人不舒服的特性正在一步步的被解決浅碾。

這篇文章演示了 ES2015 是如何解決以上描述的難題,并增加了哪些特性來提升對(duì)象字面量的能力:

  • 在對(duì)象構(gòu)造函數(shù)中設(shè)置原型
  • 速寫式方法聲明
  • 進(jìn)行 super 調(diào)用
  • 可計(jì)算的屬性名

還有我們可以展望一下將來续语,看看 (草案2) 里的新提議: 可收集可展開的屬性垂谢。

Infographic

1. 在對(duì)象構(gòu)造函數(shù)中設(shè)置原型

正如你已知的,訪問已創(chuàng)建對(duì)象的原型有一種方式是引用 __proto__ 這個(gè) getter 屬性:

    var myObject = {  
      name: 'Hello World!'
    };
    myObject.__proto__;                         // => {}  
    myObject.__proto__.isPrototypeOf(myObject); // => true  

`myObject.__proto__` 返回 `myObject` 的原型對(duì)象疮茄。

好消息是 ES2015 允許使用 __proto__ 在對(duì)象字面量 { __proto__: protoObject } 中作為屬性名來設(shè)置原型滥朱。

讓我們用 __proto__ 屬性為對(duì)象初始化,看它是如何改進(jìn)介紹中描述的不直觀方案:

    var myProto = {  
      propertyExists: function(name) {
        return name in this;    
      }
    };
    var myNumbers = {  
      __proto__: myProto,
      array: [1, 6, 7]
    };
    myNumbers.propertyExists('array');      // => true  
    myNumbers.propertyExists('collection'); // => false  

myNumbers 是使用了特殊的屬性名 __proto__ 創(chuàng)建的對(duì)象力试,它的原型是 myProto 徙邻。
這個(gè)對(duì)象用了一個(gè)簡(jiǎn)單的聲明來創(chuàng)建,沒有使用類似 Object.create() 的附加函數(shù)畸裳。

如你所見缰犁,使用 __proto__ 非常簡(jiǎn)潔. 我通常推薦簡(jiǎn)潔直觀的解決方案。

一些題外話怖糊,我認(rèn)為有點(diǎn)奇怪的是簡(jiǎn)單可擴(kuò)展的解決方案依賴大量的設(shè)計(jì)和工作帅容。如果一個(gè)方案很簡(jiǎn)潔,你也許認(rèn)為它是容易設(shè)計(jì)的伍伤。然而事實(shí)完全相反:

  • 讓事情變得簡(jiǎn)單直接很復(fù)雜
  • 讓事情變得復(fù)雜難以理解很容易

如果一些事情看起來很復(fù)雜或者很難使用并徘,可能它是沒有被充分考慮過。
關(guān)于返璞歸真扰魂,你怎么看麦乞?(隨意留言評(píng)論)

1.1 特殊的情況下 __proto__ 的使用手冊(cè)

即使 __proto__ 看起來很簡(jiǎn)潔, 這有一些特定的場(chǎng)景你需要注意到阅爽。

Infographic

對(duì)象字面量中 __proto__ 只允許使用 一次 路幸。重復(fù)使用 JavaScript 會(huì)拋出異常:

    var object = {  
      __proto__: {
        toString: function() {
          return '[object Numbers]'
        }
      },
      numbers: [1, 5, 89],
      __proto__: {
        toString: function() {
          return '[object ArrayOfNumbers]'
        }
      }
    };

例子中的對(duì)象字面量聲明了兩個(gè) __proto__ 屬性,這是不允許的付翁。這種情況會(huì)拋出 SyntaxError: Duplicate __proto__ fields are not allowed in object literals 的語法錯(cuò)誤简肴。

JavaScript 有只能使用對(duì)象或 null 作為 __proto__ 屬性值的約束。任何嘗試使用原始類型們 (字符串百侧,數(shù)字砰识,布爾值) 乃至 undefined 會(huì)被忽略掉能扒,不能改變對(duì)象的原型。
讓我們看看這個(gè)限制的例子:

    var objUndefined = {  
      __proto__: undefined
    };
    Object.getPrototypeOf(objUndefined); // => {}  
    var objNumber = {  
      __proto__: 15
    };
    Object.getPrototypeOf(objNumber);    // => {}  

這個(gè)對(duì)象字面量使用了 undefined 和數(shù)字 15 來設(shè)置 __proto__ 的值辫狼。因?yàn)橹挥袑?duì)象或 null 允許被當(dāng)做原型初斑, objUndefinedobjNumber 仍然擁有他們默認(rèn)的原型: JavaScript 空對(duì)象 {}__proto__ 的值被忽略了膨处。

當(dāng)然见秤,嘗試用原始類型去設(shè)置對(duì)象的原型會(huì)挺奇怪。這里的約束符合預(yù)期真椿。

2. 速寫式方法聲明

我們可以在對(duì)象字面量中使用一個(gè)更短的語法來聲明方法鹃答,一個(gè)能省略掉 function 關(guān)鍵字和 : 符號(hào)的方式。它被稱之為速寫式方法聲明突硝。

讓我們使用這個(gè)新的短模式來定義一些方法吧:

    var collection = {  
      items: [],
      add(item) {
        this.items.push(item);
      },
      get(index) {
        return this.items[index];
      }
    };
    collection.add(15);  
    collection.add(3);  
    collection.get(0); // => 15  

add()get()collection 里用這個(gè)短模式定義的方法测摔。

這個(gè)方法聲明的方式還一個(gè)好處是它們都是非匿名函數(shù),這在調(diào)試的時(shí)候會(huì)很方便解恰。 上個(gè)例子執(zhí)行 collection.add.name 返回函數(shù)名 'add'锋八。
譯者注:好像非速寫式聲明的函數(shù)名字也是一樣,調(diào)用堆棧的表現(xiàn)也都一樣护盈,這里不太明白挟纱。

3. 進(jìn)行 super 調(diào)用

一個(gè)有趣的改進(jìn)是可以使用 super 關(guān)鍵字來訪問原型鏈中父類的屬性。瞧瞧下面的這個(gè)例子:

    var calc = {  
      sumArray (items) {
        return items.reduce(function(a, b) {
          return a + b;
        });
      }
    };
    var numbers = {  
      __proto__: calc,
      numbers: [4, 6, 7],
      sumElements() {
        return super.sumArray(this.numbers);
      }
    };
    numbers.sumElements(); // => 17  

calcnumbers 對(duì)象的原型黄琼。在 numberssumElements 方法中可以通過 super 關(guān)鍵字調(diào)用原型的 super.sumArray() 方法樊销。

最終, super 是調(diào)用對(duì)象原型鏈里父類屬性的快捷方式脏款。

上面的例子其實(shí)可以直接用 calc.sumArray() 調(diào)用它的原型。然而因?yàn)?super 基于原型鏈調(diào)用裤园,是一個(gè)更推薦的方式撤师。并且它的存在明確得表示了父類屬性即將被調(diào)用。

3.1 super 的使用限制

super 在對(duì)象字面量中 只能在速寫式方法聲明里 使用拧揽。

如果嘗試在普通的方法聲明 { name: function() {} } 中使用剃盾, JavaScript 會(huì)拋出異常:

    var calc = {  
      sumArray (items) {
        return items.reduce(function(a, b) {
          return a + b;
        });
      }
    };
    var numbers = {  
      __proto__: calc,
      numbers: [4, 6, 7],
      sumElements: function() {
        return super.sumArray(this.numbers);
      }
    };
    // Throws SyntaxError: 'super' keyword unexpected here
    numbers.sumElements();  

這個(gè) sumElements 方法是通過屬性: sumElements: function() {...} 定義的。 因?yàn)?super 只能在速寫式方法聲明中使用淤袜,這種情況下調(diào)用會(huì)拋出 SyntaxError: 'super' keyword unexpected here 的語法錯(cuò)誤痒谴。

這個(gè)約束不太影響對(duì)象字面量的聲明方式,多數(shù)情況下因?yàn)檎Z法更簡(jiǎn)潔铡羡,使用速寫式方法聲明會(huì)更好积蔚。

4. 可計(jì)算的屬性名

在 ES2015 之前, 在對(duì)象字面量初始化中,對(duì)象的屬性名大部分是靜態(tài)的字符串烦周。為了創(chuàng)建一個(gè)經(jīng)過運(yùn)算的屬性名尽爆,你不得不使用訪問器函數(shù)創(chuàng)建屬性怎顾。

    function prefix(prefStr, name) {  
       return prefStr + '_' + name;
    }
    var object = {};  
    object[prefix('number', 'pi')] = 3.14;  
    object[prefix('bool', 'false')] = false;  
    object; // => { number_pi: 3.14, bool_false: false }  

很明顯,這種方式定義屬性有點(diǎn)不那么友好漱贱。

可計(jì)算的屬性名優(yōu)雅的解決了這個(gè)問題槐雾。
當(dāng)你要通過某個(gè)表達(dá)式計(jì)算屬性名,在方括號(hào) {[expression]: value} 里替換對(duì)應(yīng)的代碼幅狮。對(duì)應(yīng)的表達(dá)式會(huì)把計(jì)算結(jié)果作為屬性名募强。

我非常喜歡這個(gè)語法:簡(jiǎn)短又簡(jiǎn)潔。

讓我們改進(jìn)上面的例子:

    function prefix(prefStr, name) {  
       return prefStr + '_' + name;
    }
    var object = {  
      [prefix('number', 'pi')]: 3.14,
      [prefix('bool', 'false')]: false
    };
    object; // => { number_pi: 3.14, bool_false: false }  

[prefix('number', 'pi')] 通過計(jì)算 prefix('number', 'pi') 表達(dá)式設(shè)置了 'number_pi' 這個(gè)屬性名.
相應(yīng)的 [prefix('bool', 'false')] 表達(dá)式設(shè)置了另一個(gè)屬性名 'bool_false' 崇摄。

4.1 Symbol 作為屬性名

Symbols 運(yùn)算也可以作為可計(jì)算的屬性名钻注。只需要保證把它們括在括號(hào)里: { [Symbol('name')]: 'Prop value' }

舉個(gè)栗子配猫,讓我們用 Symbol.iterator 這個(gè)特殊的屬性幅恋,去遍歷對(duì)象的自有屬性名。如下所示:

    var object = {  
       number1: 14,
       number2: 15,
       string1: 'hello',
       string2: 'world',
       [Symbol.iterator]: function *() {
         var own = Object.getOwnPropertyNames(this),
           prop;
         while(prop = own.pop()) {
           yield prop;
         }
       }
    }
    [...object]; // => ['number1', 'number2', 'string1', 'string2']

[Symbol.iterator]: function *() { } 定義了一個(gè)屬性來遍歷對(duì)象的自有屬性泵肄。 展開操作符 [...object] 使用了迭代器來返回自有屬性的數(shù)組捆交。

5. 對(duì)未來的一個(gè)展望: 可收集可展開的屬性

對(duì)象字面量的可收集可展開的屬性 目前是草案第二階段 (stage 2) 中的一個(gè)提議,它將被選入下一個(gè) Javascript 版本腐巢。

它們等價(jià)于展開和收集操作符 品追,已經(jīng)可以在 ECMAScript 2015 中被數(shù)組所使用。

可收集的屬性 允許收集一個(gè)對(duì)象在解構(gòu)賦值后剩下的屬性們冯丙。
下面這個(gè)例子收集了 object 解構(gòu)后留下的屬性:

    var object = {  
      propA: 1,
      propB: 2,
      propC: 3
    };
    let {propA, ...restObject} = object;  
    propA;      // => 1  
    restObject; // => { propB: 2, propC: 3 }  

可展開的屬性 允許從一個(gè)源對(duì)象拷貝它的自有屬性到另一個(gè)對(duì)象字面量中肉瓦。這個(gè)例子中對(duì)象字面量的其它屬性合集是從 source 對(duì)象中展開的:

    var source = {  
      propB: 2,
      propC: 3
    };
    var object = {  
      propA: 1,
      ...source
    }
    object; // => { propA: 1, propB: 2, propC: 3 }  

6. 總結(jié)

JavaScript 正在大步前進(jìn)。

即使一個(gè)相當(dāng)小的對(duì)象字面量改進(jìn)都會(huì)在 ECMAScript 2015 里考慮胃惜。以及很多草案里的新特性提議泞莉。

你可以在對(duì)象初始化時(shí)直接通過 __proto__ 屬性名設(shè)置其原型。比用 Object.create() 簡(jiǎn)單很多船殉。

現(xiàn)在方法聲明有個(gè)更簡(jiǎn)潔的模式鲫趁,所以你不必輸入 function 關(guān)鍵字。而且在速寫式聲明里利虫,你可以使用 super 關(guān)鍵字挨厚,它允許你十分容易得通過對(duì)象的原型鏈訪問父類屬性。

如果屬性名需要在運(yùn)行時(shí)計(jì)算糠惫,現(xiàn)在你可以用可計(jì)算的屬性名 [expression] 來初始化對(duì)象疫剃。

對(duì)象字面量現(xiàn)在確實(shí)很酷!
你覺得呢硼讽?隨意留言評(píng)論巢价。

本文鏈接:
http://zhangchen91.cn/post/why-object-literals-in-javascript-are-cool.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蹄溉,更是在濱河造成了極大的恐慌咨油,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,294評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件柒爵,死亡現(xiàn)場(chǎng)離奇詭異役电,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)棉胀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,493評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門法瑟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人唁奢,你說我怎么就攤上這事霎挟。” “怎么了麻掸?”我有些...
    開封第一講書人閱讀 157,790評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵酥夭,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我脊奋,道長(zhǎng)熬北,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,595評(píng)論 1 284
  • 正文 為了忘掉前任诚隙,我火速辦了婚禮讶隐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘久又。我一直安慰自己巫延,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,718評(píng)論 6 386
  • 文/花漫 我一把揭開白布地消。 她就那樣靜靜地躺著炉峰,像睡著了一般。 火紅的嫁衣襯著肌膚如雪犯建。 梳的紋絲不亂的頭發(fā)上讲冠,一...
    開封第一講書人閱讀 49,906評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音适瓦,去河邊找鬼。 笑死谱仪,一個(gè)胖子當(dāng)著我的面吹牛玻熙,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播疯攒,決...
    沈念sama閱讀 39,053評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼嗦随,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起枚尼,我...
    開封第一講書人閱讀 37,797評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤贴浙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后署恍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體崎溃,經(jīng)...
    沈念sama閱讀 44,250評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,570評(píng)論 2 327
  • 正文 我和宋清朗相戀三年盯质,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了袁串。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,711評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡呼巷,死狀恐怖囱修,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情王悍,我是刑警寧澤破镰,帶...
    沈念sama閱讀 34,388評(píng)論 4 332
  • 正文 年R本政府宣布,位于F島的核電站压储,受9級(jí)特大地震影響鲜漩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜渠脉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,018評(píng)論 3 316
  • 文/蒙蒙 一宇整、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧芋膘,春花似錦鳞青、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,796評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至习寸,卻和暖如春胶惰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背霞溪。 一陣腳步聲響...
    開封第一講書人閱讀 32,023評(píng)論 1 266
  • 我被黑心中介騙來泰國打工孵滞, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鸯匹。 一個(gè)月前我還...
    沈念sama閱讀 46,461評(píng)論 2 360
  • 正文 我出身青樓坊饶,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親殴蓬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子匿级,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,595評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容