<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>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                [TOC] # 第10章 解構(Destructuring) ## 10.1 概覽 解構(Destructuring) 是一種從數據中提取值的便捷方式,這些數據存儲在(可能嵌套的)對象和數組中。解構可以用在接收數據的地方(比如賦值操作的左邊)。提取的具體方式取決于模式(看后面的例子就明白啦)。 ### 10.1.1 對象解構(Object destructuring) 解構對象: ```js const obj = { first: 'Jane', last: 'Doe' }; const {first: f, last: l} = obj; // f = 'Jane'; l = 'Doe' // {prop} 是 {prop: prop} 的縮寫 const {first, last} = obj; // first = 'Jane'; last = 'Doe' ``` 解構能幫助處理返回值: ```js const obj = { foo: 123 }; const {writable, configurable} = Object.getOwnPropertyDescriptor(obj, 'foo'); console.log(writable, configurable); // true true ``` ### 10.1.2 數組解構(Array destructuring) 數組解構(作用于所有可迭代的值): ```js const iterable = ['a', 'b']; const [x, y] = iterable; // x = 'a'; y = 'b' ``` 解構能幫助處理返回值: ```js const [all, year, month, day] = /^(\d\d\d\d)-(\d\d)-(\d\d)$/ .exec('2999-12-31'); ``` ### 10.1.3 解構可以用在什么地方? 解構可以用在以下地方: ```js // 變量聲明: const [x] = ['a']; let [x] = ['a']; var [x] = ['a']; // 賦值: [x] = ['a']; // 參數定義: function f([x]) { ··· } f(['a']); ``` 你還可以在`for-of`循環里進行解構: ```js const arr = ['a', 'b']; for (const [index, element] of arr.entries()) { console.log(index, element); } // Output: // 輸出: // 0 a // 1 b ``` ## 10.2 背景:構造數據 vs 提取數據 為了充分理解什么是解構,我們先看看它所在的更廣義的上下文環境。 JavaScript 有以下操作來構造數據: ```js const obj = {}; obj.first = 'Jane'; obj.last = 'Doe'; ``` 并且有以下操作來提取數據: ```js const f = obj.first; const l = obj.last; ``` 注意,我們提取數據的時候用了跟構造數據時一樣的語法。 還有更好的構造語法——對象字面量: ```js const obj = { first: 'Jane', last: 'Doe' }; ``` 在ES6之前,沒有相應的提取數據的機制。在 ECMAScript 6 里解構允許使用同樣的語法提取數據,這種語法在提取數據時叫做"對象模式"。如下例,賦值符的左邊: ```js const { first: f, last: l } = obj; ``` 就像對象字面量允許同時創建多個屬性一樣,對象模式允許我們同時提取多個屬性。 你也可以通過模式解構數組: ```js const [x, y] = ['a', 'b']; // x = 'a'; y = 'b' ``` ## 10.3 模式(Patterns) 以下是解構相關的兩個部分: * 解構源(Destructuring source): 被解構的數據。比如,解構賦值的右邊。 * 解構目標(Destructuring target): 解構的目標。比如,解構賦值的左邊。 解構目標有以下三種模式: * 賦值目標(Assignment target)。 比如: x * 在變量聲明和參數定義里,只允許對變量的引用。在解構賦值里,你有更多選擇,稍后會進行解釋。 * 對象模式(Object pattern)。 比如:{ first: ?pattern?, last: ?pattern? } * 一個對象模式的組成部分是屬性,屬性的值還是模式(可遞歸)。 * 數組模式(Array pattern)。 比如: [ ?pattern?, ?pattern? ] * 數組模式的組成部分是元素,元素還是模式(可遞歸)。 這意味著能夠以任意的深度嵌套模式: ```js const obj = { a: [{ foo: 123, bar: 'abc' }, {}], b: true }; const { a: [{foo: f}] } = obj; // f = 123 ``` ### 10.3.1 按需挑選模式 假如你要解構一個對象,你只需要寫你想要的屬性: ```js const { x: x } = { x: 7, y: 3 }; // x = 7 ``` 假如你要解構一個數組,你可以選擇只提取前面的部分: ```js const [x,y] = ['a', 'b', 'c']; // x='a'; y='b'; ``` ## 10.4 模式是如何訪問值的內部結構的? 在 `pattern = someValue` 這個賦值里, `pattern` 是如何訪問 `someValue` 內部的呢? ### 10.4.1 對象模式強制將值轉化成對象處理 對象模式在訪問屬性之前會強制將解構源轉化成對象。這意味著它能處理原始類型值(primitive values): ```js const {length : len} = 'abc'; // len = 3 const {toString: s} = 123; // s = Number.prototype.toString ``` #### 10.4.1.1 有時候,無法對值進行對象解構 強制轉化成對象的操作并不是通過 `Object()` 實現的,而是通過內部操作 `ToObject()`。 `Object()` 永遠都不會失敗: ```js > typeof Object('abc') 'object' > var obj = {}; > Object(obj) === obj true > Object(undefined) {} > Object(null) {} ``` 當遇到 undefined 或 null 時, ToObject() 會拋一個 TypeError 錯誤。因此,下面的解構在訪問任何屬性之前就失敗了: ```js const { prop: x } = undefined; // TypeError const { prop: y } = null; // TypeError ``` 所以,你可以使用空對象模式 `{}` 檢查一個值能否強制轉換成對象。我們已經知道,只有 `undefined` 和 `null` 不能: ```js ({} = [true, false]); // OK,數組強制轉換成對象 ({} = 'abc'); // OK,字符串強制轉換成對象 ({} = undefined); // TypeError ({} = null); // TypeError ``` 以上表達式外面的圓括號是必須的,因為在 JavaScript 里,聲明不可以用花括號開始([細節稍后解釋](###))。 ### 10.4.2 數組模式對可迭代的值都可以生效 數組解構使用了迭代器來獲取解構源的元素。因此,你可以數組解構任何可迭代的值。我們來看幾個可迭代值的例子。 字符串是可迭代的: ```js const [x,...y] = 'abc'; // x='a'; y=['b', 'c'] ``` 別忘了,字符串的迭代器返回的是代碼點(“Unicode 字符”, 21 位),而不是代碼單元(“JavaScript 字符”, 16 位)。(更多關于 Unicode 的信息,參考“Speaking Javascript”這本書里的“**第 24 章 Unicode 與 JavaScript**”。) 比如: ```js const [x,y,z] = 'a\uD83D\uDCA9c'; // x='a'; y='\uD83D\uDCA9'; z='c' ``` 你不能通過索引訪問 `Set` 的元素,但是你可以通過迭代器來訪問。因此,數組解構也支持 `Set`: ```js const [x,y] = new Set(['a', 'b']); // x='a'; y='b’; ``` `Set` 迭代器按照插入順序返回元素,這也是解釋了為什么上面的解構每次返回的結果都相同。 #### 10.4.2.1 有時候,無法對值進行數組解構 當一個值有 `Symbol.iterator` 方法,且這個方法能返回一個對象時,它就是可迭代的。假如被解構的值不可迭代,數組解構就會拋出 `TypeError` 的錯誤: ```js let x; [x] = [true, false]; // OK,數組是可迭代的 [x] = 'abc'; // OK, 字符串是可迭代的 [x] = { * [Symbol.iterator]() { yield 1 } }; // OK,可迭代 [x] = {}; // TypeError,空對象不可迭代 [x] = undefined; // TypeError,不可迭代 [x] = null; // TypeError,不可迭代 ``` 訪問數組元素之前,就會拋出 TypeError ,這意味著你可以用空數組模式 [] 檢查一個值是不是可迭代的: ```js [x] = {}; // TypeError,空對象不可迭代 [x] = undefined; // TypeError,不可迭代 [x] = null; // TypeError,不可迭代 ``` ## 10.5 默認值(Default values) 默認值(Default values) 是模式的一個特性: 假如有一部分(一個對象屬性或者一個數組元素)在解構源中沒有匹配到,那么它就會被匹配成: 1. 它的 默認值 (如果指定了的話) 2. `undefined` (沒指定時) 也就是說,提供一個默認值是可選的操作。 來看一個例子。在下面的解構中,下標為 0 的元素在右邊沒有匹配值。因此,解構會繼續將 x 匹配成 3,結果就是 x 被設置成了 3。 ```js const [x=3, y] = []; // x = 3; y = undefined ``` 你也可以在對象模式中使用默認值: ```js const {foo: x=3, bar: y} = {}; // x = 3; y = undefined ``` ### 10.5.1.1 undefined 會觸發默認值 當某一部分有匹配值,并且匹配值是 `undefined` 時,也會最終匹配到默認值: ```js const [x=1] = [undefined]; // x = 1 const {prop: y=2} = {prop: undefined}; // y = 2 ``` 這一行為的根本原因將會在**下一章的參數默認值一節**中解釋。 ### 10.5.1.2 默認值是按需計算的 默認值本身只在需要時(即被觸發時)進行計算。也就是說,下面的解構: ```js const {prop: y=someFunc()} = someValue; ``` 等價于: ```js let y; if (someValue.prop === undefined) { y = someFunc(); } else { y = someValue.prop; } ``` 假如你用 console.log()的話,可以觀察到這一點: ```js > function log(x) { console.log(x); return 'YES' } > const [a=log('hello')] = []; hello > a 'YES' > const [b=log('hello')] = [123]; > b 123 ``` 在第二個解構中,默認值不會被觸發,log() 不會被調用。 ### 10.5.1.3 默認值可以指向模式中的其它變量 默認值可以指向任何變量,包括同一模式下的其它變量: ```js const [x=3, y=x] = []; // x=3; y=3 const [x=3, y=x] = [7]; // x=7; y=7 const [x=3, y=x] = [7, 2]; // x=7; y=2 ``` 但是,要注意順序:變量 x 和 y 是從左到右聲明的,假如在變量聲明之前訪問它,就會產生 ReferenceError: ```js const [x=y, y=3] = []; // ReferenceError ``` ### 10.5.1.4 模式的默認值 目前我們只看到了變量的默認值,其實也可以給模式設定默認值: ```js const [{ prop: x } = {}] = []; ``` 這樣做的意義是什么呢?我們來回顧一下默認值的規則: > 假如在解構源中沒有匹配到,解構會繼續匹配默認值[…]。 因為匹配不到下標為 0 的元素,解構就會繼續下面的匹配: ```js const { prop: x } = {}; // x = undefined ``` 如果把模式 { prop: x} 替換成變量 pattern,就更容易理解了: ```js const [pattern = {}] = []; ``` ### 10.5.5 更復雜的默認值 我們進一步探索模式的默認值。在下面的例子里,通過默認值 `{ prop: 123 }` 給 `x` 賦值: 因為下標為 0 的數組元素在右側沒有匹配,解構就會繼續下面的匹配,最終 `x` 被設為 `123`。 ```js const { prop: x } = { prop: 123 }; // x = 123 ``` 但是,在下面這種情況下,即使右側的默認值里有下標為 0 的元素,x 也不會被賦值。因為這個默認值不會被觸發。 ```js const [{ prop: x } = { prop: 123 }] = [{}]; ``` 在這種情況下,解構會繼續下面的匹配: ```js const { prop: x } = {}; // x = undefined ``` 因此,假如你希望無論是對象還是屬性缺失,`x` 都默認為 `123` 的話,你需要給 `x 本身指定一個默認值: ```js const [{ prop: x=123 } = {}] = [{}]; ``` 這樣的話,解構就會像下面這樣繼續進行,無論右側是 `[{}]` 還是 `[]`。 ```js const { prop: x=123 } = {}; // x = 123 ``` ## 還有疑問? [稍后會有一節](###) 從另一個角度——算法角度——來解釋解構。也許能讓你有新的見解 ## 10.6 對象解構的更多特性 ### 10.6.1 屬性值縮寫 屬性值縮寫是對象字面量的一個特性:假如屬性值用變量表示,且變量與屬性的鍵同名,你就可以省略鍵。對于解構,也同樣適用: ```js const { x, y } = { x: 11, y: 8 }; // x = 11; y = 8 ``` 上述聲明等價于: ```js const { x: x, y: y } = { x: 11, y: 8 }; ``` 你也可以將默認值與屬性值縮寫結合起來: ```js const { x, y = 1 } = {}; // x = undefined; y = 1 ``` ### 10.6.2 可計算的屬性鍵(Computed property keys) 可計算的屬性鍵是對象字面量的另一個特點,這也同樣適用于解構。通過將表達式放進方括號中,你可以將一個屬性的鍵指定為這個表達式: ```js const FOO = 'foo'; const { [FOO]: f } = { foo: 123 }; // f = 123 ``` 可計算的屬性鍵允許解構鍵為 symbol 類型的屬性: ```js // 創建并解構一個屬性,屬性的鍵是一個 symbol const KEY = Symbol(); const obj = { [KEY]: 'abc' }; const { [KEY]: x } = obj; // x = 'abc' // 提取 Array.prototype[Symbol.iterator] const { [Symbol.iterator]: func } = []; console.log(typeof func); // function ``` ## 10.7 數組解構的更多特性 ### 10.7.1 省略(elision) 省略(elision)允許在解構時使用數組的“空洞”來跳過不關心的元素: ```js const [,, x, y] = ['a', 'b', 'c', 'd']; // x = 'c'; y = 'd' ``` ### 10.7.2 10.7.2 剩余操作符(rest operator, ...) 剩余操作符(rest operator) 允許將數組的剩余元素提取到一個數組中。你只能把剩余操作符當作數組模式的最后一部分來使用: ```js const [x, ...y] = ['a', 'b', 'c']; // x='a'; y=['b', 'c'] ``` > [展開操作符](###)(spread operator)具有與剩余運算符完全相同的語法 - `...`。但它們是不同的:前者向對象字面量和函數調用提供數據,而后者則用于解構和提取數據。 如果剩余操作符找不到任何元素,就會將運算元(operand)匹配到空數組。也就是說,它不會產生 `undefined` 或者 `null`。比如: ```js const [x, y, ...z] = ['a']; // x='a'; y=undefined; z=[] ``` 剩余操作符的運算元不一定是變量,還可以是模式: ```js const [x, ...[y, z]] = ['a', 'b', 'c']; // x = 'a'; y = 'b'; z = 'c' ``` 剩余操作符將觸發以下解構: ```js [y, z] = ['b', 'c'] ``` ## 10.8 不止可以給變量賦值 使用解構賦值時,每個賦值目標可以是一個正常賦值的左側所允許的任何內容。 例如,對`property(obj.prop)`的引用: ```js const obj = {}; ({ foo: obj.prop } = { foo: 123 }); console.log(obj); // {prop:123} ``` 或者引用一個數組元素(`arr[0]`): ```js const arr = []; ({ bar: arr[0] } = { bar: true }); console.log(arr); // [true] ``` 您還可以通過剩余操作符`(...)`將對象屬性和數組元素分配: ```js const obj = {}; [first, ...obj.prop] = ['a', 'b', 'c']; // first = 'a'; obj.prop = ['b', 'c'] ``` 假如你通過解構來聲明變量或者定義參數,必須使用簡單標識符,而不能是對象屬性和數組元素的引用。 ## 10.9 解構的陷阱 在使用解構時要注意以下兩點: 1. 聲明不要以花括號開始。 2. 解構期間,要么聲明變量,要么給變量賦值,但是不能同時進行。 下面詳細解釋。 ### 10.9.1 聲明不要以花括號開始。 因為代碼塊是以花括號開始的,所以聲明不能這樣。 當在賦值操作中使用對象解構時,很不巧,會出現這種情況: ```js { a, b } = someObject; // SyntaxError ``` 解決辦法是給整個表達式加上圓括號: ```js ({ a, b } = someObject); // OK ``` 下面的是錯誤示例: ```js ({ a, b }) = someObject; // SyntaxError ``` 如果前面帶上 `let`, `var` 和 `const` 的話,則可以放心使用花括號: ```js const { a, b } = someObject; // OK ``` ### 10.9.2 不要同時進行聲明和對已有變量的賦值操作 在解構變量聲明中,解構源的每個變量都會被聲明。下面的例子里,我們試圖聲明變量 b,以及引用變量 f,然而并不會成功。 ```js let f; ··· let { foo: f, bar: b } = someObject; // 解析階段(在運行代碼之前): // SyntaxError: Duplicate declaration, f ``` 修復的方法是在解構中只進行賦值操作,并且預先聲明變量 b: ```js let f; ··· let b; ({ foo: f, bar: b } = someObject); ``` ## 10.10 解構的例子 先看幾個小例子。 for-of 循環支持解構: ```js const map = new Map().set(false, 'no').set(true, 'yes'); for (const [key, value] of map) { console.log(key + ' is ' + value); } ``` 你可以用解構來交換值。JavaScript 引擎會優化這個操作,所以不會額外創建數組。 ```js [a, b] = [b, a]; ``` 你還可以用解構來切分數組: ```js const [first, ...rest] = ['a', 'b', 'c']; // first = 'a'; rest = ['b', 'c'] ``` ### 10.10.1 解構返回的數組 一些內置的 JavaScript 操作會返回數組。解構能幫忙處理它們: ```js const [all, year, month, day] = /^(\d\d\d\d)-(\d\d)-(\d\d)$/ .exec('2999-12-31'); ``` 假如你只想得到正則里的分組(而不是匹配的整體, all),你可以使用省略,略過下標為 0 的數組元素: ```js const [, year, month, day] = /^(\d\d\d\d)-(\d\d)-(\d\d)$/ .exec('2999-12-31'); ``` 假如正則表達式不能成功匹配,`exec()` 會返回 `null`。遺憾的是,由于返回 `null`無法給變量設置默認值,所以此時需要用或操作符(||): ```js const [, year, month, day] = /^(\d\d\d\d)-(\d\d)-(\d\d)$/ .exec(someStr) || []; ``` `Array.prototype.split()` 會返回一個數組。因此,假如你只關心元素而不關心數組的話,可以用解構: ```js const cells = 'Jane\tDoe\tCTO' const [firstName, lastName, title] = cells.split('\t'); console.log(firstName, lastName, title); ``` ### 10.10.2 解構返回的對象 解構可用于從函數或者方法返回的對象中提取數據。比如,迭代器方法 `next()` 返回一個對象,該對象有兩個屬性, done 和 value。下面的代碼通過迭代器 iter 打印出數組 arr 的所有元素。行 `A` 使用了解構: ```js const arr = ['a', 'b']; const iter = arr[Symbol.iterator](); while (true) { const {done,value} = iter.next(); // (A) if (done) break; console.log(value); } ``` ### 10.10.3 數組解構(array-destructuring)可迭代的值 數組解構可以作用于任何可迭代的值。有時候會比較有用: ```js const [x,y] = new Set().add('a').add('b'); // x = 'a'; y = 'b' const [a,b] = 'foo'; // a = 'f'; b = 'o' ``` ### 10.10.4 多重返回值 為了證明多重返回值的好處,我們實現一個函數 findElement(a, p),這個函數用于查找數組 a 中,第一個使得函數 p 返回 true 的元素。問題來了:函數 findElement(a, p) 應該返回什么?有時候我們只需要返回元素本身,有時候只需要其下標,有時候兩者都需要。下面的實現返回了兩者。 ```js function findElement(array, predicate) { for (const [index, element] of array.entries()) { // (A) if (predicate(element)) { return { element, index }; // (B) } } return { element: undefined, index: -1 }; } ``` 在行 A 中,數組方法 entries() 返回一個可迭代的 `[index,element]` 對。每次迭代將解構一個`[index,element]` 對。在行 B 中,我們使用屬性值縮寫返回了對象 `{ element: element, index: index }`。 接下來使用 `findElement()`。 ```js const arr = [7, 8, 6]; const {element, index} = findElement(arr, x => x % 2 === 0); // element = 8, index = 1 ``` > 有幾個 ECMAScript 6 的功能讓我們可以寫更多的簡潔的代碼:回調函數是一個箭頭函數,返回值是從一個屬性值縮寫的對象模式中解構出來的。 由于 `index` 和 `element` 都指向屬性名,所以可以不分先后順序: ```js const {index, element} = findElement(···); ``` 以上例子滿足了同時返回下標和元素的需求。假如我們只關心其中一個返回值呢?好在ECMAScript 6 有解構功能,上面的實現也可以滿足單個返回值的需求。而且,跟單個返回值的函數相比,這種實現方式的句法開銷是最小的。 ```js const a = [7, 8, 6]; const {element} = findElement(a, x => x % 2 === 0); // element = 8 const {index} = findElement(a, x => x % 2 === 0); // index = 1 ``` 我們每次只提取需要的屬性值。 ## 10.11 解構的算法 這一節將從另一個角度審視解構:遞歸模式匹配算法。 > 這一角度特別有助于理解默認值。假如你覺得自己還沒完全理解默認值,請接著看。 在這最后一節里,我會使用算法來解釋下面兩個函數聲明的區別。 ```js function move({x=0, y=0} = {}) { ··· } function move({x, y} = { x: 0, y: 0 }) { ··· } ``` ### 10.11.1 算法 一個解構賦值看起來是這樣的: ~~~ ?pattern? = ?value? ~~~ 我們要使用 `pattern` 從 `value` 中提取數據。我先描述一下實現這個功能的算法,在函數式編程中該算法叫做 模式匹配(簡稱:匹配)。該算法將一個操作符 ← (“匹配”)指定給解構賦值,這個解構賦值會用一個 `pattern` 去匹配一個 `value` ,同時賦值給變量: ~~~ ?pattern? ← ?value? ~~~ 該算法是通過一些迭代的規則來定義的,這些規則會分別解析 ← 操作符兩邊的運算元。你可能還不習慣這個聲明符號,但是這個符號能讓算法的定義更簡潔。每個迭代規則由以下兩部分構成: * 頭部(head)指明了規則所操作的運算元。 * 主體部分(body)指定了下一步要執行的動作。 讓我們來看一個例子: (2c) `{key: ?pattern?, ?properties?}` ← `obj` ~~~ ?pattern? ← obj.key {?properties?} ← obj ~~~ (2e) `{}` ← `obj` (no properties left) ~~~ // Nothing to do ~~~ 在規則(2c)中,頭意味著,如果存在至少一個屬性和零個或多個屬性的對象模式,則執行該規則。該模式與`obj`匹配。此規則的作用是繼續執行屬性值模式與obj.key匹配,剩下的屬性與obj匹配。 在規則(2e)中,頭意味著如果空對象模式{}與值obj匹配,則執行該規則。 每當調用算法時,規則都會被從上到下的檢查,并且只有第一個適用的規則被執行。 這里只展示解構賦值的算法。解構變量聲明和解構參數定義的算法跟這個算法很相似。 我也不會介紹更高級的特性(計算屬性鍵;屬性值縮寫;賦值目標的對象屬性和數組元素)。這里只介紹基礎知識。 #### 10.11.1.1 模式 一個模式可能是以下三種情況之一: * 一個變量:`x` * 一個對象模式: `{?properties?}` * 一個數組模式: `[?elements?]` 下面的小節里會分別介紹這三種情況。 #### 10.11.1.2 變量 (1) `x ← value` (包括 `undefined` 和 `null`) ```js x = value ``` #### 10.11.1.3 對象模式 * (2a) `{?properties?} ← undefined` ```js throw new TypeError(); ``` * (2b) `{?properties?} ← null` ```js throw new TypeError(); ``` * (2c) `{key: ?pattern?, ?properties?} ← obj` ```js ?pattern? ← obj.key {?properties?} ← obj ``` * (2d) `{key: ?pattern? = default_value, ?properties?} ← obj` ```js const tmp = obj.key; if (tmp !== undefined) { ?pattern? ← tmp } else { ?pattern? ← default_value } {?properties?} ← obj ``` * (2e) `{} ← obj` ``` // Nothing to do ``` #### 10.11.1.4 數組模式 **數組模式和可迭代值** 數組解構算法以數組模式和一個迭代器開始: * (3a) `[?elements?] ← non_iterable` `assert(!isIterable(non_iterable))` ```js throw new TypeError(); ``` * (3b) `[?elements?] ← iterable` `assert(isIterable(iterable))` ```js const iterator = iterable[Symbol.iterator](); ?elements? ← iterator ``` 輔助函數(Helper function:): ```js function isIterable(value) { return (value !== null && typeof value === 'object' && typeof value[Symbol.iterator] === 'function'); } ``` **數組元素和迭代器**。 接下來,算法要處理模式里的元素(箭頭左側)以及從可迭代值里得到的迭代器(箭頭右側)。 * (3c) `?pattern?, ?elements? ← iterator` ~~~ ?pattern? ← getNext(iterator) // 最后一個元素之后就是 undefined ?elements? ← iterator ~~~ * (3d) `?pattern? = default_value, ?elements? ← iterator` ~~~ const tmp = getNext(iterator); // 最后一個元素之后就是 undefined if (tmp !== undefined) { ?pattern? ← tmp } else { ?pattern? ← default_value } ?elements? ← iterator ~~~ * (3e) `, ?elements? ← iterator `(“空洞”, 省略) ~~~ getNext(iterator); // 略過 ?elements? ← iterator ~~~ * (3f) `...?pattern? ← iterator` (一定是數組最后一部分!) ~~~ const tmp = []; for (const elem of iterator) { tmp.push(elem); } ?pattern? ← tmp ~~~ * (3g) ← iterator ~~~ // 沒有元素了,什么也不做 ~~~ ### 10.11.2 應用算法 在ECMAScript 6中,如果調用者使用一個對象字面量,并且被調用者使用解構,您可以模擬命名的參數(named parameters)。這個模擬在[參數處理的章節](###)中有詳細的解釋。 ```js function move1({x=0, y=0} = {}) { // (A) return [x, y]; } move1({x: 3, y: 8}); // [3, 8] move1({x: 3}); // [3, 0] move1({}); // [0, 0] move1(); // [0, 0] ``` 在行A中有三個默認值: - 前兩個默認值允許您省略`x`和`y`。 - 第三個默認值允許您不帶參數調用`move1()`(類似在最后一行)。 可為什么非要像上面的代碼那樣定義參數呢?為什么不是下面這種方式?下面的代碼也是完全合法的 ES6 代碼呀。 ```js function move2({x, y} = { x: 0, y: 0 }) { return [x, y]; } ``` 要解釋為什么 `move1()` 是正確的做法,我們可以通過在兩個例子里使用這兩種函數進行比較。不過在此之前,先看看匹配過程是如何解釋參數傳遞的。 #### 10.11.2.1 背景:通過匹配傳參 對于函數調用,實參(在函數調用里)會去匹配形參(在函數定義里)。舉個例子,下面就是函數定義和函數調用。 ```js function func(a=0, b=0) { ··· } func(1, 2); ``` 參數 a 和 b 會按照類似于下面的解構進行賦值。 ```js [a=0, b=0] ← [1, 2] ``` #### 10.11.2.2 使用 `move2()` 讓我們看看對于 `move2()`,解構是如何進行的。 **例 1:** `move2() `的解構過程如下: ```js [{x, y} = { x: 0, y: 0 }] ← [] ``` 左側唯一的數組元素在右側沒有找到對應的匹配值,所以 {x,y} 匹配了默認值,而不是匹配右側的數據(規則 3b, 3d): ```js {x, y} ← { x: 0, y: 0 } ``` 左側包含了 屬性值縮寫,展開如下: ```js {x: x, y: y} ← { x: 0, y: 0 } ``` 解構進行了以下兩個賦值操作(規則 2c,1): ```js x = 0; y = 0; ``` 不過,只有像 `move2()` 這樣不傳參數的函數調用才會用到默認值。 **例 2:** 函數調用 `move2({z:3})` 的解構過程如下: ```js [{x, y} = { x: 0, y: 0 }] ← [{z:3}] ``` 右側數組有下標為 0 的元素。所以,會忽略默認值,下一步是(規則 3d): ```js {x, y} ← { z: 3 } ``` 這會把`x` 和 `y` 都設置成 `undefined`,這可不是我們想要的結果。 #### 10.11.2.3 使用 `move1()` 試試 `move1()`。 **例 1:** `move1()` ```js [{x=0, y=0} = {}] ← [] ``` 右側沒有下標為0的數組元素,所以使用默認值(規則 3d): ```js {x=0, y=0} ← {} ``` 左側包含了 屬性值縮寫,展開如下: ```js {x: x=0, y: y=0} ← {} ``` x 和 y 在右側都沒有匹配值。因此,會使用默認值,于是會進行下面的解構(規則 2d): ```js x ← 0 y ← 0 ``` 接下來執行如下的賦值操作(規則 1): ```js x = 0 y = 0 ``` **例 2: **`move1({z:3})` ```js [{x=0, y=0} = {}] ← [{z:3}] ``` 數組模式的第一個元素在右側有匹配值,使用該匹配值進行解構(規則 3d): ```js {x=0, y=0} ← {z:3} ``` 跟例 1 相似,右側不存在屬性 x 和 y ,因此使用默認值: ```js x = 0 y = 0 ``` #### 10.11.2.4 總結 以上例子展示了默認值屬于模式部分(對象屬性或者數組元素)的一個特性。假如模式的某一部分沒有匹配值或者匹配了 undefined,那么就會使用默認值。換句話說,模式匹配了默認值。
                  <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>

                              哎呀哎呀视频在线观看