展開運算符與其餘運算符· 從ES6開始的JavaScript學習生活

文章推薦指數: 80 %
投票人數:10人

展開運算符(Spread Operator)與其餘運算符(Rest Operator)是ES6中的其中兩種新特性,雖然這兩種特性的符號是一模一樣的,都是(...)三個點,但使用的情況與意義不同。

我們 ... 從ES6開始的JavaScript學習生活 1.1. 關於本書 1.1.1. 序言 1.1.2. 寫作格式 1.1.3. 更新日誌 1.1.4. 問答與回饋 1.2. 介紹 1.2.1. 歷史 1.2.2. 發展 1.3. 工具 1.3.1. 工具 1.3.2. ES6環境 1.3.3. 輸出 1.3.4. (未完成)除錯 1.3.5. (未完成)單元測試 1.4. 基礎 1.4.1. 變數、常數與命名 1.4.2. 資料類型(值) 1.4.3. 控制流程 1.4.4. 迴圈 1.4.5. 函式與作用域 1.4.6. 陣列 1.4.7. 物件 1.4.8. 錯誤與例外處理 1.4.9. 日期與時間 1.5. 特性 1.5.1. 原型基礎物件導向 1.5.2. this 1.5.3. Callback回調 1.5.4. Closure閉包 1.5.5. 異步程式設計與事件迴圈 1.5.6. 事件處理 1.5.7. 箭頭函式 1.5.8. Promise承諾 1.5.9. 展開運算符與其餘運算符 1.5.10. AJAX與FetchAPI 1.5.11. 解構賦值 1.5.12. 模組系統 1.5.13. (未完成)JSON 1.6. 附錄 1.6.1. (未完成)程式語言符號 1.6.2. (未完成)關鍵字與保留字 PoweredbyGitBook 展開運算符與其餘運算符 展開運算符與其餘運算符 展開運算符(SpreadOperator)與其餘運算符(RestOperator)是ES6中的其中兩種新特性,雖然這兩種特性的符號是一模一樣的,都是(...)三個點,但使用的情況與意義不同。

我們常常在文字敘述或聊天時,這個(...)常用來代表了"無言"、"無窮的想像"或"還有其他更多的"的意思。

簡單摘要一下這個語法的內容: 符號都是三個點(...) 都與陣列有關 一個是展開陣列中的值,一個是集合其餘的值成為陣列 註:三個點符號(...),比較正式的英文字詞是Ellipsis,翻成中文是"省略"符號的意思,不過它有各種形式(全形或半形),也有超過三個點的情況,所以一般要說得明白只有三個點的情況,會用"threedots"與"dot-dot-dot"反而更為明確。

註:目前看到的名詞上並沒有統一的但都是指這種語法。

在ES6標準上的用語是SpreadElement與restparameter,並沒有集合成為一個章節,而是內容散落在各章節中。

在MDN上用Spreadoperator與Spreadsyntax的名詞(標題是syntax,網址列上是operator),以及Restoperator與Restparameters(標題是parameters,但連結是operator)。

註:用於物件上的類似語法並不是ES6中的特性,它是正在制定中的新語法標準,不過在React與Redux中很常見,能透過babel編譯,這稱為ObjectRest/SpreadProperties 展開運算符(SpreadOperator) 展開運算符是把一個陣列展開成個別的值的速寫語法,它只會在陣列字面定義與函式呼叫時使用 展開運算符(SpreadOperator)是把一個陣列展開(expand)成個別值,這個運算符後面必定接著一個陣列。

最常見的是用來組合(連接)陣列,對應的陣列方法是concat,以下是一個簡單的範例: constparams=["hello",true,7] constother=[1,2,...params]//[1,2,"hello",true,7] 展開運算符可以作陣列的淺拷貝,當然陣列的淺拷貝有很多種方式,這是新的一種語法,也是目前語法上最簡單的一種: constarr=[1,2,3] constarr2=[...arr] arr2.push(4)//不會影響到arr 註:淺拷貝(shallow-copy)對於陣列中的陣列值(多維陣列),或是有複雜的物件值情況時,是只會拷貝參照值而已。

註:上述的展開運算符在陣列字面中使用時,並沒有限制位置,或是在個數。

像constarr=[...a,1,...b]這樣的語法都是可以的。

你也可以用來把某個陣列展開,然後傳入函式作為傳入參數值,例如下面這個一個加總函式的範例: functionsum(a,b,c){ returna+b+c } constargs=[1,2,3] sum(…args)//6 對照ES5中的相容語法,則是用apply函式,它的第二個參數也是使用陣列,以下是用ES5語法與上面相同結果的範例程式: functionsum(a,b,c){ returna+b+c; } varargs=[1,2,3]; sum.apply(undefined,args);//6 展開運算符還有一個特別的功能,就是把可迭代(iterable)或與陣列相似(Array-like)的物件轉變為陣列,在JavaScript語言中內建的可迭代(iterable)物件有String、Array、TypedArray、Map與Set物件,而與陣列相似(Array-like)的物件指的是函式中的隱藏物件"arguments"。

下面的範例是轉變字串為字元陣列: constaString="foo" constchars=[...aString]//["f","o","o"] 下面的範例是把函式中的隱藏偽物件"arguments"轉成真正的陣列物件: functionaFunc(x){ console.log(arguments) console.log(Array.isArray(arguments)) //轉為真正的陣列 constarr=[...arguments] console.log(arr) console.log(Array.isArray(arr)) } aFunc(1) 其餘運算符(RestOperator) 其餘運算符是收集其餘的(剩餘的)這些值,轉變成一個陣列。

它會用在函式定義時的傳入參數識別名定義(其餘參數,Restparameters),以及解構賦值時 會用其餘運算符(RestOperator)的主要意義是因為它會用在兩個地方,一個是比較常提及的在函式定義中的傳入參數定義中,稱之為其餘參數(Restparameters)。

另一種情況是用在解構賦值時。

其餘參數(Restparameters) 就像在電影葉問中的台詞:"我要打十個",其餘參數可以讓你一次打剩下的全部,不過葉問會變成一個陣列。

既然是一個參數的語法,當然就是用在函式的傳入參數定義。

其餘參數代表是將"不確定的傳入參數值們"在函式中轉變成為一個陣列來進行運算。

例如下面這個加總的範例: functionsum(…numbers){ constresult=0 numbers.forEach(function(number){ result+=number }) returnresult } sum(1)//1 sum(1,2,3,4,5)//15 特別注意:其餘參數在傳入參數定義中,必定是位於最後一位,並且在參數中只能有一個其餘參數。

其餘參數的值在沒有傳入實際值時,會變為一個空陣列,而不是undefined,以下的範例可以看到這個結果: functionaFunc(x,...y){ console.log('x=',x,',y=',y) } aFunc(1,2,3)//x=1,y=[2,3] aFunc()//x=undefined,y=[] 其餘參數的設計有一個很明確的用途,就是要取代函式中那個隱藏"偽陣列"物件arguments,arguments雖然會包含了所有的函式傳入參數,但它是個類似陣列的物件卻沒有大部份陣列方法,它不太像是個隱藏的密技,比較像是隱藏的陷阱,很容易造成誤解或混亂,完全不建議你使用arguments這個東西。

其餘參數的值是一個真正的陣列,而且它需要在傳入參數宣告才能使用,至少在程式碼閱讀性上勝出太多了。

解構賦值(destructuring)時 解構賦值在另一獨立章節會講得更詳細,這裡只是要說明其餘運算符的另一個使用情況。

解構賦值也是一個ES6中的新特性。

解構賦值是用在"陣列指定陣列"或是"物件指定物件"的情況下,這個時候會根據陣列原本的結構,以類似"鏡子"對映樣式(pattern)來進行賦值。

聽起來很玄但用起來很簡單,這是一種為了讓陣列與物件指定值時更方便所設計的一種語法。

例如以下的範例: const[x,y,z]=[1,2,3] console.log(x)//1 像這個例子就是最簡單的陣列解構賦值的範例,x當然會被指定為1,y與z你應該用腳底板也想得到是被指定了什麼值。

當使用其餘運算符之後,就可以用像其餘參數的類似概念來進行解構賦值,例如以下的範例: const[x,...y]=[1,2,3] console.log(x)//1 console.log(y)//[2,3] 當右邊的值與左邊數量不相等時,"鏡子對映的樣式"就會有些沒對到,用了其餘運算符的那個識別名稱,就會變成空陣列。

就會像下面這個例子一樣: const[x,y,...z]=[1] console.log(x)//1 console.log(y)//undefined console.log(z)//[] 在函式傳入參數中作解構賦值,這個例子會的確也是一種解構賦值的語法,而且加了上一節的函式中的其餘參數的用法。

例子出自MDN的這裡: functionf(...[a,b,c]){ returna+b+c; } f(1)//NaN(bandcareundefined) f(1,2,3)//6 f(1,2,3,4)//6(thefourthparameterisnotdestructured) 你可以回頭再看一下"其餘參數"的使用情況,是不是與解構賦值時很相似。

特別注意:在使用解構賦值時一樣只能用一個其餘運算符,位置也只能放在最後一個。

ES7+標準的其餘屬性(RestProperties)與展開屬性(SpreadProperties) 上面都只有談到與陣列搭配使用,但你可能會看到在物件上也會使用類似語法與符號(...),尤其是在React與Redux中。

這些都是還在制定中的ES7之後草案標準,稱為其餘屬性(RestProperties)與展開屬性(SpreadProperties)。

例如下面這樣的範例,來自這裡: //RestProperties let{x,y,...z}={x:1,y:2,a:3,b:4} console.log(x)//1 console.log(y)//2 console.log(z)//{a:3,b:4} //SpreadProperties letn={x,y,...z} console.log(n)//{x:1,y:2,a:3,b:4} 有些新式的框架或函式庫中已經開始使用了,babel轉換工具可以支援轉換這些語法,但使用時要注意要額外加裝babel-plugin-transform-object-rest-spread外掛。

撰寫風格建議 不要使用函式中的arguments,總是使用其餘參數語法來取代它。

(Airbnb7.6,Google5.5.5.2,eslint:prefer-rest-params). 不要在展開運算符與其餘運算符後面有空格,也就是與後面的識別名稱(傳入參數名稱、陣列名稱)之間要緊接著。

(Google5.2.5/5.5.5.2,eslint:rest-spread-spacing) 用展開運算符的語法來作拷貝陣列。

(Airbnb4.3) 用展開運算符的語法來取代函式中的apply的語法,作不定個數傳入參數的函式呼叫。

(Airbnb7.14,eslint:prefer-spread) 用展開運算符的語法來取代slice與concat方法的語法。

(Google5.2.5) 優先使用物件展開運算符(objectspreadoperator)的語法取代Object.assign,來作物件的淺拷貝。

(Airbnb3.8)(ES7+標準) 結論 展開運算符比較容易理解,它是把已有(看得到)的陣列值"展開"為一個一個單獨的值。

其餘運算符都是用在函式定義或指定值時,它是要收集其餘的(剩餘的)值,形成一個陣列再來進行運算,你可能還對展開運算符與其餘運算符的分別還有點混亂,因為符號都是相同的三個點符號(...),只是它們在不同的使用情況下功用是不太相同的,所以要區分它們要從使用情況來區分: 展開運算符:用在陣列的字面文字定義裡面(例如[1,...b]),或是函式呼叫時(例如func(...args)) 其餘運算符:用在函式的定義,裡面的傳入參數名稱定義時(例如functionfunc(x,...y))。

或是在解構賦值時(例如const[x,...y]=[1,2,3]) 參考資源 Spreadsyntax(MDN) Restparameters ExploringES6-10.7.2Restoperator ExploringES6-11.8Thespreadoperator ES6 — default+rest+spread ES6SpreadandButterinDepth resultsmatching"" Noresultsmatching""



請為這篇文章評分?