<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ## 25.單個對象 > 原文: [http://exploringjs.com/impatient-js/ch_single-objects.html](http://exploringjs.com/impatient-js/ch_single-objects.html) > > 貢獻者:[阿純](https://github.com/rechun) 在本書中,JavaScript 的面向對象編程(OOP)風格分四步介紹。本章介紹步驟 1,[下一章](ch_proto-chains-classes.html)涵蓋步驟 2-4。步驟是(圖 [7](#fig:oop_steps1) ): 1. **單個對象:** _ 對象 _,JavaScript 的基本 OOP 構建塊如何獨立工作? 2. 原型鏈:每個對象都有一個零個或多個 _ 原型對象鏈 _。原型是 JavaScript 的核心繼承機制。 3. 類:JavaScript 的 _ 類 _ 是對象的工廠。類及其實例之間的關系基于原型繼承。 4. 子類化:_ 子類 _ 與其 _ 超類 _ 之間的關系也基于原型繼承。 ![Figure 7: This book introduces object-oriented programming in JavaScript in four steps.](https://img.kancloud.cn/7c/84/7c84f54969e70c3efeb137f55fa29c34.svg) Figure 7: This book introduces object-oriented programming in JavaScript in four steps. ### 25.1。 JavaScript 中對象的兩個角色 在 JavaScript 中,對象是一組稱為 _ 屬性 _ 的鍵值條目。 對象在 JavaScript 中扮演兩個角色: * 記錄:作為記錄的對象具有固定數量的屬性,其密鑰在開發時是已知的。它們的值可以有不同的類型。本章首先介紹了這種使用對象的方法。 * 字典:Objects-as-dictionaries 具有可變數量的屬性,其密鑰在開發時是未知的。它們的所有值都具有相同的類型。通常最好將映射用作詞典而不是對象(本章稍后將對此進行介紹)。 ### 25.2。對象作為記錄 #### 25.2.1。對象字面值:屬性 作為記錄的對象是通過所謂的 _ 對象字面值 _ 創建的。對象字面值是 JavaScript 的一個突出特點:它們允許您直接創建對象。不需要上課!這是一個例子: ```js const jane = { first: 'Jane', last: 'Doe', // optional trailing comma }; ``` 在這個例子中,我們通過一個對象字面值創建了一個對象,它以花括號`{}`開頭和結尾。在其中,我們定義了兩個 _ 屬性 _(鍵值條目): * 第一個屬性具有鍵`first`和值`'Jane'`。 * 第二個屬性具有鍵`last`和值`'Doe'`。 如果以這種方式編寫它們,則屬性鍵必須遵循 JavaScript 變量名稱的規則,但允許使用保留字。 訪問屬性如下: ```js assert.equal(jane.first, 'Jane'); // get property .first jane.first = 'John'; // set property .first assert.equal(jane.first, 'John'); ``` #### 25.2.2。對象字面值:屬性值縮寫 每當通過變量名定義屬性的值并且該名稱與鍵相同時,您可以省略該鍵。 #### 25.2.3。術語:屬性鍵,屬性名稱,屬性符號 鑒于屬性鍵可以是字符串和符號,因此進行以下區分: ```js const x = 4; const y = 1; assert.deepEqual( { x, y }, { x: x, y: y } ); ``` * 如果屬性鍵是字符串,則它也稱為 _ 屬性名稱 _。 * 如果屬性鍵是符號,則它也稱為 _ 屬性符號 _。 此術語用于 JavaScript 標準庫(“自己”表示“未繼承”,將在下一章中介紹): * `Object.keys(obj)`:返回`obj`的所有屬性鍵 * `Object.getOwnPropertyNames(obj)` * `Object.getOwnPropertySymbols(obj)` #### 25.2.4。獲得屬性 這就是你 _ 得到 _(讀)財產的方式: ```js obj.propKey ``` 如果`obj`沒有其鍵為`propKey`的屬性,則此表達式求值為`undefined`: ```js const obj = {}; assert.equal(obj.propKey, undefined); ``` #### 25.2.5。設置屬性 這就是你 設置 (寫入)屬性的方式: ```js obj.propKey = value; ``` 如果`obj`已經有一個鍵為`propKey`的屬性,則此語句將更改該屬性。否則,它會創建一個新屬性: ```js const obj = {}; assert.deepEqual( Object.keys(obj), []); obj.propKey = 123; assert.deepEqual( Object.keys(obj), ['propKey']); ``` #### 25.2.6。對象字面值:方法 以下代碼顯示如何通過對象字面值創建方法`.describe()`: ```js const jane = { first: 'Jane', // data property says(text) { // method return `${this.first} says “${text}”`; // (A) }, // comma as separator (optional at end) }; assert.equal(jane.says('hello'), 'Jane says “hello”'); ``` 在方法調用`jane.says('hello')`期間,`jane`被稱為方法調用的 _ 接收器 _,并被分配給特殊變量`this`。這使方法`.says()`能夠訪問 A 行中的兄弟屬性`.first`。 #### 25.2.7。對象字面值:訪問者 JavaScript 中有兩種訪問器: * _getter_ 是 調用 (讀取)屬性調用的方法。 * _setter_ 是由 設置 (寫入)屬性調用的方法。 ##### 25.2.7.1。getter 通過在方法定義前添加關鍵字`get`來創建 getter: ```js const jane = { first: 'Jane', last: 'Doe', get full() { return `${this.first} ${this.last}`; }, }; assert.equal(jane.full, 'Jane Doe'); jane.first = 'John'; assert.equal(jane.full, 'John Doe'); ``` ##### 25.2.7. setter 通過在方法定義前添加關鍵字`set`來創建 setter: ```js const jane = { first: 'Jane', last: 'Doe', set full(fullName) { const parts = fullName.split(' '); this.first = parts[0]; this.last = parts[1]; }, }; jane.full = 'Richard Roe'; assert.equal(jane.first, 'Richard'); assert.equal(jane.last, 'Roe'); ``` ![](https://img.kancloud.cn/3e/d5/3ed5755d562179ae6c199264f5e21157.svg) **練習:通過對象字面值創建對象** `exercises/single-objects/color_point_object_test.js` ### 25.3。傳播到對象字面值(`...`) 我們已經看到在函數調用中使用擴展(`...`),它將迭代的內容轉換為參數。 在對象字面值內部,擴展屬性 將另一個對象的屬性添加到當前對象: ```js > const obj = {foo: 1, bar: 2}; > {...obj, baz: 3} { foo: 1, bar: 2, baz: 3 } ``` 如果屬性鍵發生沖突,則上次提到的屬性為“wins”: ```js > const obj = {foo: 1, bar: 2, baz: 3}; > {...obj, foo: true} { foo: true, bar: 2, baz: 3 } > {foo: true, ...obj} { foo: 1, bar: 2, baz: 3 } ``` #### 25.3.1。傳播的用例:復制對象 您可以使用 spread 來創建對象的副本`original`: ```js const copy = {...original}; ``` 警告 - 復制是 淺 :`copy`是一個新對象,帶有`original`的所有屬性(鍵值對)的副本。但是如果屬性值是對象,那么不會復制它們;它們在`original`和`copy`之間共享。以下代碼演示了這意味著什么。 ```js const original = { a: 1, b: {foo: true} }; const copy = {...original}; // The first level is a true copy: assert.deepEqual( copy, { a: 1, b: {foo: true} }); original.a = 2; assert.deepEqual( copy, { a: 1, b: {foo: true} }); // no change // Deeper levels are not copied: original.b.foo = false; // The value of property `b` is shared // between original and copy. assert.deepEqual( copy, { a: 1, b: {foo: false} }); ``` ![](https://img.kancloud.cn/20/ae/20ae605d8efee727618ed3803e87a6e5.svg) ** JavaScript 不做深拷貝** _ 對象的深拷貝 _(所有級別都被復制)是眾所周知的難以做到的。因此,JavaScript 沒有內置的操作(暫時)。如果您需要這樣的操作,則必須自己實現。 #### 25.3.2。傳播的用例:缺失屬性的默認值 如果代碼的其中一個輸入是包含數據的對象,則可以在為其指定默認值時使屬性成為可選屬性。這樣做的一種技術是通過其屬性包含默認值的對象。在以下示例中,該對象是`DEFAULTS`: ```js const DEFAULTS = {foo: 'a', bar: 'b'}; const providedData = {foo: 1}; const allData = {...DEFAULTS, ...providedData}; assert.deepEqual(allData, {foo: 1, bar: 'b'}); ``` 結果,對象`allData`是通過創建`DEFAULTS`的副本并用`providedData`覆蓋其屬性來創建的。 但是您不需要對象來指定默認值,您也可以單獨在對象字面值中指定它們: ```js const providedData = {foo: 1}; const allData = {foo: 'a', bar: 'b', ...providedData}; assert.deepEqual(allData, {foo: 1, bar: 'b'}); ``` #### 25.3.3。用例傳播:非破壞性變化的屬性 到目前為止,我們遇到了一種更改對象屬性的方法:我們 _ 設置 _ 并改變對象。也就是說,這種改變屬性的方式是 _ 破壞性 _ 通過傳播,您可以非破壞性地更改屬性:您可以復制屬性具有不同值的對象。 例如,此代碼非破壞性地更新屬性`.foo`: ```js const obj = {foo: 'a', bar: 'b'}; const updatedObj = {...obj, foo: 1}; assert.deepEqual(updatedObj, {foo: 1, bar: 'b'}); ``` ![](https://img.kancloud.cn/3e/d5/3ed5755d562179ae6c199264f5e21157.svg) **練習:通過傳播(固定密鑰)非破壞性更新屬性** `exercises/single-objects/update_name_test.js` ### 25.4。方法 #### 25.4.1。方法是值為函數的屬性 讓我們重溫一下用于介紹方法的示例: ```js const jane = { first: 'Jane', says(text) { return `${this.first} says “${text}”`; }, }; ``` 有些令人驚訝的是,方法是函數: ```js assert.equal(typeof jane.says, 'function'); ``` 這是為什么?請記住,在[關于可調用實體](ch_callables.html#roles-of-ordinary-functions)的章節中,我們了解到普通函數扮演了幾個角色。 _ 方法 _ 是其中一個角色。因此,在引擎蓋下,`jane`大致如下所示。 ```js const jane = { first: 'Jane', says: function (text) { return `${this.first} says “${text}”`; }, }; ``` #### 25.4.2。 `.call()`:顯式參數`this` 請記住,每個函數`someFunc`也是一個對象,因此有方法。一種這樣的方法是`.call()` - 它允許您在明確指定`this`時調用函數: ```js someFunc.call(thisValue, arg1, arg2, arg3); ``` ##### 25.4.2.1。方法和`.call()` 如果進行方法調用,`this`始終是隱式參數: ```js const obj = { method(x) { assert.equal(this, obj); // implicit parameter assert.equal(x, 'a'); }, }; obj.method('a'); // Equivalent: obj.method.call(obj, 'a'); ``` 順便說一句,這意味著實際上有兩個不同的點運算符: 1. 一個用于訪問屬性:`obj.prop` 2. 一個用于進行方法調用:`obj.prop()` 它們的不同之處在于(2)不僅僅是(1),后面是函數調用運算符`()`。相反,(2)另外指定`this`的值(如前面的例子所示)。 ##### 25.4.2.2。功能和`.call()` 但是,如果函數調用普通函數,`this`也是一個隱式參數: ```js function func(x) { assert.equal(this, undefined); // implicit parameter assert.equal(x, 'a'); } func('a'); // Equivalent: func.call(undefined, 'a'); ``` 也就是說,在函數調用期間,普通函數具有`this`,但它被設置為`undefined`,這表示它在這里沒有真正的用途。 接下來,我們將研究使用`this`的缺陷。在我們能夠做到這一點之前,我們還需要一個工具:函數的方法`.bind()`。 #### 25.4.3。 `.bind()`:預填充`this`和功能參數 `.bind()`是函數對象的另一種方法。調用此方法如下。 ```js const boundFunc = someFunc.bind(thisValue, arg1, arg2, arg3); ``` `.bind()`返回一個新函數`boundFunc()`。調用該函數調用`someFunc()`并將`this`設置為`thisValue`并且這些參數:`arg1`,`arg2`,`arg3`,然后是`boundFunc()`的參數。 也就是說,以下兩個函數調用是等效的: ```js boundFunc('a', 'b') someFunc.call(thisValue, arg1, arg2, arg3, 'a', 'b') ``` 另一種預填`this`和參數的方法是通過箭頭功能: ```js const boundFunc2 = (...args) => someFunc.call(thisValue, arg1, arg2, arg3, ...args); ``` 因此,`.bind()`可以實現為如下的實際功能: ```js function bind(func, thisValue, ...boundArgs) { return (...args) => func.call(thisValue, ...boundArgs, ...args); } ``` ##### 25.4.3.1。示例:綁定實際函數 將`.bind()`用于實際功能有點不直觀,因為你必須為`this`提供一個值。該值通常是`undefined`,反映了函數調用期間發生的情況。 在下面的示例中,我們通過將`add()`的第一個參數綁定到`8`來創建`add8()`,這是一個具有一個參數的函數。 ```js function add(x, y) { return x + y; } const add8 = add.bind(undefined, 8); assert.equal(add8(1), 9); ``` ##### 25.4.3.2。示例:綁定方法 在下面的代碼中,我們將方法`.says()`轉換為獨立函數`func()`: ```js const jane = { first: 'Jane', says(text) { return `${this.first} says “${text}”`; // (A) }, }; const func = jane.says.bind(jane, 'hello'); assert.equal(func(), 'Jane says “hello”'); ``` 通過`.bind()`將`this`設置為`jane`至關重要。否則,`func()`將無法正常工作,因為在行 A 中使用了`this`。 #### 25.4.4。 `this`陷阱:提取方法 我們現在對函數和方法有了很多了解,并準備好了解涉及方法和`this`的最大缺陷:如果你不小心,函數調用從對象中提取的方法可能會失敗。 在下面的例子中,當我們提取方法`jane.says()`時,我們失敗,將它存儲在變量`func`和函數調用`func()`中。 ```js const jane = { first: 'Jane', says(text) { return `${this.first} says “${text}”`; }, }; const func = jane.says; // extract the method assert.throws( () => func('hello'), // (A) { name: 'TypeError', message: "Cannot read property 'first' of undefined", }); ``` A 行中的函數調用相當于: ```js assert.throws( () => jane.says.call(undefined, 'hello'), // `this` is undefined! { name: 'TypeError', message: "Cannot read property 'first' of undefined", }); ``` 那么我們如何解決這個問題呢?我們需要使用`.bind()`來提取方法`.says()`: ```js const func2 = jane.says.bind(jane); assert.equal(func2('hello'), 'Jane says “hello”'); ``` 當我們調用`func()`時,`.bind()`確保`this`始終為`jane`。 您還可以使用箭頭函數來提取方法: ```js const func3 = text => jane.says(text); assert.equal(func3('hello'), 'Jane says “hello”'); ``` ##### 25.4.4.1。示例:提取方法 以下是您在實際 Web 開發中可能看到的代碼的簡化版本: ```js class ClickHandler { constructor(elem) { elem.addEventListener('click', this.handleClick); // (A) } handleClick(event) { alert('Clicked!'); } } ``` 在 A 行中,我們沒有正確提取方法`.handleClick()`。相反,我們應該這樣做: ```js elem.addEventListener('click', this.handleClick.bind(this)); ``` ![](https://img.kancloud.cn/3e/d5/3ed5755d562179ae6c199264f5e21157.svg) **練習:提取方法** `exercises/single-objects/method_extraction_exrc.js` #### 25.4.5。 `this`陷阱:意外遮蔽`this` 如果使用普通功能,意外遮蔽`this`只是一個問題。 請考慮以下問題:當您在普通函數內部時,您無法訪問周圍范圍的`this`,因為普通函數有自己的`this`。換句話說:內部作用域中的變量將變量隱藏在外部作用域中。這被稱為 _ 陰影 _。以下代碼是一個示例: ```js const obj = { name: 'Jane', sayHiTo(friends) { return friends.map( function (friend) { // (A) return `${this.name} says hi to ${friend}`; // (B) }); } }; assert.throws( () => obj.sayHiTo(['Tarzan', 'Cheeta']), { name: 'TypeError', message: "Cannot read property 'name' of undefined", }); ``` 為什么錯誤? B 行中的`this`不是`.sayHiTo()`的`this`,它是從 B 行開始的普通函數的`this`。 有幾種方法可以解決這個問題。最簡單的方法是使用箭頭函數 - 它沒有自己的`this`,因此陰影不是問題。 ```js const obj = { name: 'Jane', sayHiTo(friends) { return friends.map( (friend) => { return `${this.name} says hi to ${friend}`; }); } }; assert.deepEqual( obj.sayHiTo(['Tarzan', 'Cheeta']), ['Jane says hi to Tarzan', 'Jane says hi to Cheeta']); ``` #### 25.4.6。避免`this`的陷阱 我們已經看到了兩個與`this`相關的重大陷阱: 1. [提取方法](ch_single-objects.html#this-pitfall-extracting-methods) 2. [意外遮蔽`this`](ch_single-objects.html#this-pitfall-shadowing) 一個簡單的規則有助于避免第二個陷阱: > “避免關鍵字`function`”:絕不使用普通函數,只使用箭頭函數(用于實際函數)和方法定義。 讓我們打破這個規則: * 如果所有實際函數都是箭頭函數,則第二個陷阱永遠不會發生。 * 使用方法定義意味著您只能在方法中看到`this`,這使得此功能不那么混亂。 但是,即使我不使用(普通)函數 _ 表達式 _,我還是在語法上喜歡函數 _ 聲明 _。如果您沒有參考其中的`this`,您可以安全地使用它們。檢查工具 ESLint 有[規則](https://eslint.org/docs/rules/no-invalid-this),有助于此。 唉,第一個陷阱沒有簡單的方法:每當你提取一個方法時,你必須小心并正確地做到這一點。例如,通過綁定`this`。 #### 25.4.7。 `this`在各種情況下的值 `this`在各種情況下的值是多少? 在可調用實體中,`this`的值取決于調用可調用實體的方式以及它是什么類型的可調用實體: * 功能調用: * 普通功能:`this === undefined` * 箭頭功能:`this`與周圍范圍相同(詞匯`this`) * 方法調用:`this`是呼叫接收方 * `new`:`this`是指新創建的實例 您還可以在所有常見的頂級范圍中訪問`this`: * `<script>`元素:`this === window` * ES 模塊:`this === undefined` * CommonJS 模塊:`this === module.exports` 但是,我喜歡假裝您無法訪問頂級作用域中的`this`,因為頂級`this`令人困惑且沒有用處。 ### 25.5。對象作為詞典 對象最適合作為記錄。但在 ES6 之前,JavaScript 沒有字典的數據結構(ES6 帶來了映射)。因此,必須將對象用作字典。因此,鍵必須是字符串,但值可以是任意類型。 我們首先看一下與字典相關的對象的特征,但偶爾也可用于對象作為記錄。本節最后提供了實際使用對象作為詞典的提示(劇透:如果可以,請避免使用映射)。 #### 25.5.1。任意固定字符串作為屬性鍵 從對象作為記錄到對象作為字典時,一個重要的變化是我們必須能夠使用任意字符串作為屬性鍵。本小節解釋了如何實現固定字符串鍵。下一小節將介紹如何動態計算任意鍵。 到目前為止,我們只看到合法的 JavaScript 標識符作為屬性鍵(符號除外): ```js const obj = { mustBeAnIdentifier: 123, }; // Get property assert.equal(obj.mustBeAnIdentifier, 123); // Set property obj.mustBeAnIdentifier = 'abc'; assert.equal(obj.mustBeAnIdentifier, 'abc'); ``` 兩種技術允許我們使用任意字符串作為屬性鍵。 首先 - 當通過對象字面值創建屬性鍵時,我們可以引用屬性鍵(帶單引號或雙引號): ```js const obj = { 'Can be any string!': 123, }; ``` 第二 - 獲取或設置屬性時,我們可以使用帶有字符串的方括號: ```js // Get property assert.equal(obj['Can be any string!'], 123); // Set property obj['Can be any string!'] = 'abc'; assert.equal(obj['Can be any string!'], 'abc'); ``` 您還可以引用方法的鍵: ```js const obj = { 'A nice method'() { return 'Yes!'; }, }; assert.equal(obj['A nice method'](), 'Yes!'); ``` #### 25.5.2。計算屬性鍵 到目前為止,我們受到了對象字面值內部屬性鍵的限制:它們總是固定的,它們總是字符串。如果我們將表達式放在方括號中,我們可以動態計算任意鍵: ```js const obj = { ['Hello world!']: true, ['f'+'o'+'o']: 123, [Symbol.toStringTag]: 'Goodbye', // (A) }; assert.equal(obj['Hello world!'], true); assert.equal(obj.foo, 123); assert.equal(obj[Symbol.toStringTag], 'Goodbye'); ``` 計算鍵的主要用例是將符號作為屬性鍵(行 A)。 請注意,用于獲取和設置屬性的方括號運算符適用于任意表達式: ```js assert.equal(obj['f'+'o'+'o'], 123); assert.equal(obj['==> foo'.slice(-3)], 123); ``` 方法也可以有計算屬性鍵: ```js const methodKey = Symbol(); const obj = { [methodKey]() { return 'Yes!'; }, }; assert.equal(obj[methodKey](), 'Yes!'); ``` 我們現在切換回固定屬性鍵,但如果需要計算屬性鍵,則可以始終使用方括號。 ![](https://img.kancloud.cn/3e/d5/3ed5755d562179ae6c199264f5e21157.svg) **練習:通過傳播非破壞性更新屬性(計算密鑰)** `exercises/single-objects/update_property_test.js` #### 25.5.3。 `in`運算符:是否存在具有給定鍵的屬性? `in`運算符檢查對象是否具有給定鍵的屬性: ```js const obj = { foo: 'abc', bar: false, }; assert.equal('foo' in obj, true); assert.equal('unknownKey' in obj, false); ``` ##### 25.5.3.1。通過真實性檢查財產是否存在 您還可以使用真實性檢查來確定屬性是否存在: ```js assert.equal( obj.unknownKey ? 'exists' : 'does not exist', 'does not exist'); assert.equal( obj.foo ? 'exists' : 'does not exist', 'exists'); ``` 之前的檢查有效,因為讀取不存在的屬性會返回`undefined`,這是假的。因為`obj.foo`是真實的。 但是,有一個重要的警告:如果屬性存在,則真實性檢查失敗,但具有假值(`undefined`,`null`,`false`,`0`,`""`等): ```js assert.equal( obj.bar ? 'exists' : 'does not exist', 'does not exist'); // should be: 'exists' ``` #### 25.5.4。刪除屬性 您可以通過`delete`運算符刪除屬性: ```js const obj = { foo: 123, }; assert.deepEqual(Object.keys(obj), ['foo']); delete obj.foo; assert.deepEqual(Object.keys(obj), []); ``` #### 25.5.5。字典陷阱 如果使用普通對象(通過對象字面值創建)作為字典,則必須注意兩個陷阱。 第一個缺陷是`in`運算符還找到了繼承的屬性: ```js const dict = {}; assert.equal('toString' in dict, true); ``` 我們希望`dict`被視為空,但`in`運算符會檢測它從原型`Object.prototype`繼承的屬性。 第二個缺陷是你不能使用屬性鍵`__proto__`,因為它具有特殊的權力(它設置了對象的原型): ```js const dict = {}; dict['__proto__'] = 123; // No property was added to dict: assert.deepEqual(Object.keys(dict), []); ``` 那么我們如何解決這些陷阱呢? * 只要你可以,使用映射。它們是詞典的最佳解決方案。 * 如果你不能:將庫用于可以安全地完成所有操作的對象字典。 * 如果你不能:使用沒有原型的對象。這消除了現代 JavaScript 中的兩個陷阱。 ```js const dict = Object.create(null); // no prototype assert.equal('toString' in dict, false); dict['__proto__'] = 123; assert.deepEqual(Object.keys(dict), ['__proto__']); ``` ![](https://img.kancloud.cn/3e/d5/3ed5755d562179ae6c199264f5e21157.svg) **練習:使用對象作為字典** `exercises/single-objects/simple_dict_test.js` #### 25.5.6。列出屬性鍵 Table 19: Standard library methods for listing _own_ (non-inherited) property keys. All of them return Arrays with strings and/or symbols. | | 枚舉 | 沒有。 | 串 | 符號 | | --- | --- | --- | --- | --- | | `Object.keys()` | `?` | | `?` | | | `Object.getOwnPropertyNames()` | `?` | `?` | `?` | | | `Object.getOwnPropertySymbols()` | `?` | `?` | | `?` | | `Reflect.ownKeys()` | `?` | `?` | `?` | `?` | tbl 中的每個方法。 [19](#tbl:listing-property-keys) 返回一個帶有參數自身屬性鍵的數組。在方法的名稱中,您可以看到我們之前討論過的屬性鍵(字符串和符號),屬性名稱(僅字符串)和屬性符號(僅符號)之間的區別。 可枚舉性是屬性的 _ 屬性 _。默認情況下,屬性是可枚舉的,但有一些方法可以改變它(在下一個示例中顯示,[稍后將詳細描述](ch_single-objects.html#property-attributes))。 例如: ```js const enumerableSymbolKey = Symbol('enumerableSymbolKey'); const nonEnumSymbolKey = Symbol('nonEnumSymbolKey'); // We create the enumerable properties via an object literal const obj = { enumerableStringKey: 1, [enumerableSymbolKey]: 2, } // For the non-enumerable properties, // we need a more powerful tool: Object.defineProperties(obj, { nonEnumStringKey: { value: 3, enumerable: false, }, [nonEnumSymbolKey]: { value: 4, enumerable: false, }, }); assert.deepEqual( Object.keys(obj), [ 'enumerableStringKey' ]); assert.deepEqual( Object.getOwnPropertyNames(obj), [ 'enumerableStringKey', 'nonEnumStringKey' ]); assert.deepEqual( Object.getOwnPropertySymbols(obj), [ enumerableSymbolKey, nonEnumSymbolKey ]); assert.deepEqual( Reflect.ownKeys(obj), [ 'enumerableStringKey', 'nonEnumStringKey', enumerableSymbolKey, nonEnumSymbolKey ]); ``` #### 25.5.7。通過`Object.values()`列出屬性值 `Object.values()`列出對象的所有可枚舉屬性的值: ```js const obj = {foo: 1, bar: 2}; assert.deepEqual( Object.values(obj), [1, 2]); ``` #### 25.5.8。通過`Object.entries()`列出屬性條目 `Object.entries()`列出了可枚舉屬性的鍵值對。每對編碼為一個雙元素數組: ```js const obj = {foo: 1, bar: 2}; assert.deepEqual( Object.entries(obj), [ ['foo', 1], ['bar', 2], ]); ``` ![](https://img.kancloud.cn/3e/d5/3ed5755d562179ae6c199264f5e21157.svg) **練習:`Object.entries()`** `exercises/single-objects/find_key_test.js` #### 25.5.9。確定性地列出屬性 對象的自有(非繼承)屬性始終按以下順序列出: 1. 具有整數索引的屬性(例如,數組索引) * 按升序數字順序 1. 帶字符串鍵的剩余屬性 * 按照添加順序 1. 帶符號鍵的屬性 * 按照添加順序 以下示例演示如何根據以下規則對屬性鍵進行排序: ```js > Object.keys({b:0,a:0, 2:0,1:0}) [ '1', '2', 'b', 'a' ] ``` ([您可以在規格中查找詳細信息。](https://tc39.github.io/ecma262/#sec-ordinaryownpropertykeys)) #### 25.5.10。通過`Object.fromEntries()`組裝對象 給定[key,value]對可迭代,`Object.fromEntries()`創建一個對象: ```js assert.deepEqual( Object.fromEntries([['foo',1], ['bar',2]]), { foo: 1, bar: 2, } ); ``` 它與 [`Object.entries()`](ch_single-objects.html#Object.entries) 相反。 接下來,我們將使用`Object.entries()`和`Object.fromEntries()`從庫 [Underscore](https://underscorejs.org) 中實現多個工具功能。 ##### 25.5.10.1。示例:`pick(object, ...keys)` [`pick()`](https://underscorejs.org/#pick) 從`object`中刪除其鍵不在`keys`中的所有屬性。刪除 _ 非破壞性 _:`pick()`創建修改后的副本并且不會更改原始文件。例如: ```js const address = { street: 'Evergreen Terrace', number: '742', city: 'Springfield', state: 'NT', zip: '49007', }; assert.deepEqual( pick(address, 'street', 'number'), { street: 'Evergreen Terrace', number: '742', } ); ``` 我們可以按如下方式實現`pick()`: ```js function pick(object, ...keys) { const filteredEntries = Object.entries(object) .filter(([key, _value]) => keys.includes(key)); return Object.fromEntries(filteredEntries); } ``` ##### 25.5.10.2。示例:`invert(object)` [`invert()`](https://underscorejs.org/#invert) 非破壞性地交換鍵和對象的值: ```js assert.deepEqual( invert({a: 1, b: 2, c: 3}), {1: 'a', 2: 'b', 3: 'c'} ); ``` 我們可以像這樣實現它: ```js function invert(object) { const mappedEntries = Object.entries(object) .map(([key, value]) => [value, key]); return Object.fromEntries(mappedEntries); } ``` ##### 25.5.10.3。 `Object.fromEntries()`的簡單實現 `Object.fromEntries()`可以實現如下(我省略了幾個檢查): ```js function fromEntries(iterable) { const result = {}; for (const [key, value] of iterable) { let coercedKey; if (typeof key === 'string' || typeof key === 'symbol') { coercedKey = key; } else { coercedKey = String(key); } Object.defineProperty(result, coercedKey, { value, writable: true, enumerable: true, configurable: true, }); } return result; } ``` 筆記: * `Object.defineProperty()`在本章后面中解釋[。](ch_single-objects.html#property-attributes) * 官方 polyfill 可通過 [npm 包`object.fromentries`](https://github.com/es-shims/Object.fromEntries) 獲得。 ![](https://img.kancloud.cn/3e/d5/3ed5755d562179ae6c199264f5e21157.svg) **練習:`Object.entries()`和`Object.fromEntries()`** `exercises/single-objects/omit_properties_test.js` ### 25.6。標準方法 `Object.prototype`定義了幾個可以覆蓋的標準方法。兩個重要的是: * `.toString()` * `.valueOf()` 粗略地說,`.toString()`配置對象如何轉換為字符串: ```js > String({toString() { return 'Hello!' }}) 'Hello!' > String({}) '[object Object]' ``` 并且`.valueOf()`配置對象如何轉換為數字: ```js > Number({valueOf() { return 123 }}) 123 > Number({}) NaN ``` ### 25.7。高級主題 以下小節簡要概述了超出本書范圍的主題。 #### 25.7.1。 `Object.assign()` `Object.assign()`是一種工具方法: ```js Object.assign(target, source_1, source_2, ···) ``` 這個表達式(破壞性地)將`source_1`合并到`target`,然后`source_2`等。最后,它返回`target`。例如: ```js const target = { foo: 1 }; const result = Object.assign( target, {bar: 2}, {baz: 3, bar: 4}); assert.deepEqual( result, { foo: 1, bar: 4, baz: 3 }); // target was changed! assert.deepEqual(target, result); ``` `Object.assign()`的用例類似于傳播屬性的用例。在某種程度上,它破壞性地傳播。 有關`Object.assign()`的更多信息,請參閱[“探索 ES6”](http://exploringjs.com/es6/ch_oop-besides-classes.html#Object_assign)。 #### 25.7.2。凍結對象 `Object.freeze(obj)`使`obj`不可變:您無法更改或添加屬性或更改`obj`的原型。 例如: ```js const frozen = Object.freeze({ x: 2, y: 5 }); assert.throws( () => { frozen.x = 7 }, { name: 'TypeError', message: /^Cannot assign to read only property 'x'/, }); ``` 有一點需要注意:`Object.freeze(obj)`淺薄地凍結。也就是說,只凍結`obj`的屬性,而不凍結屬性中存儲的對象。 有關`Object.freeze()`的更多信息,請參閱[“Speaking JavaScript”](http://speakingjs.com/es5/ch17.html#freezing_objects)。 #### 25.7.3。屬性屬性和屬性描述符 就像對象由屬性組成一樣,屬性由 _ 屬性 _ 組成。也就是說,您可以配置的不僅僅是屬性的值 - 這只是幾個屬性中的一個。其他屬性包括: * `writable`:是否可以更改屬性的值? * `enumerable`:`Object.keys()`是否列出了該屬性? 當您使用其中一個操作來訪問屬性屬性時,通過 _ 屬性描述符 _ 指定屬性:每個屬性代表一個屬性的對象。例如,這是您閱讀屬性`obj.foo`的屬性的方法: ```js const obj = { foo: 123 }; assert.deepEqual( Object.getOwnPropertyDescriptor(obj, 'foo'), { value: 123, writable: true, enumerable: true, configurable: true, }); ``` 這就是你設置屬性`obj.bar`的屬性的方法: ```js const obj = { foo: 1, bar: 2, }; assert.deepEqual(Object.keys(obj), ['foo', 'bar']); // Hide property `bar` from Object.keys() Object.defineProperty(obj, 'bar', { enumerable: false, }); assert.deepEqual(Object.keys(obj), ['foo']); ``` 有關屬性屬性和屬性描述符的更多信息,請參閱[“Speaking JavaScript”](http://speakingjs.com/es5/ch17.html#property_attributes)。 ![](https://img.kancloud.cn/ff/a8/ffa8e16628cad59b09c786b836722faa.svg) **測驗** 參見[測驗應用程序](ch_quizzes-exercises.html#quizzes)。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看