js?中以?...?為前綴的幾種用法詳解
ES6 開始,js 新增了剩余參數(shù)語法、展開語法等,它們有個共同之處就是都以 ... 這么個符號為前綴,好像很多地方都可以用到,但實際上又不是同一回事,容易讓初學者暈頭轉向。本篇就對目前筆者已知的 3 種關于 ... 用法的總結歸納。
用法一:函數(shù)的剩余參數(shù)(Rest parameters)
在定義函數(shù)的時候,形參當中,最后一個參數(shù)(theArgs)如果以 ... 作為前綴,那么 theArgs 就會變?yōu)橐粋€數(shù)組,調用該函數(shù)時,如果傳入的實參數(shù)量大于等于定義函數(shù)時形參的數(shù)量,比如下面的例 1.1,形參定義了 3 個,卻傳入了 5 個實參,那么從 3 開始(包括),之后的實參都會被放入到 theArgs 中:
// 例 1.1
function fn(a, b, ...theArgs) {
console.log(theArgs)
}
fn(1, 2, 3, 4, 5) // [ 3, 4, 5 ]
請注意,...theArgs 必須作為最后一個形參,不然會報語法錯誤:“SyntaxError: Rest parameter must be last formal parameter”。 除箭頭函數(shù)外,函數(shù)內都有個 arguments 可以用來獲取傳遞給函數(shù)的參數(shù)。那么它和剩余參數(shù)有什么區(qū)別呢?
- 剩余參數(shù),比如例 1.1 中的
theArgs是個真數(shù)組,可以直接使用數(shù)組的方法,而 arguments 是一個類數(shù)組(array-like)對象,想要運用數(shù)組方法還得轉成數(shù)組; - 剩余參數(shù)只包含那些沒有對應形參的實參,比如例 1.1 中的 3、4 和 5,而 arguments 包含了傳給函數(shù)的所有實參。
- arguments 可以通過 callee 屬性獲取當前 arguments 所在的函數(shù)。
其實 arguments 是早期的規(guī)范中為了方便獲取傳入函數(shù)的參數(shù)而存在的,ES6 之后,我們應當盡量使用剩余參數(shù)這種語法而不是 arguments。
用法二:展開語法(Spread syntax)
展開語法又可以在 3 個地方使用:
1)調用函數(shù)時
剩余參數(shù)是在函數(shù)定義時形參里使用的,展開語法的用處之一則是在函數(shù)調用時,傳入的實參里使用。具體說就是將數(shù)組表達式或者 string 在語法層面展開作為實參傳給函數(shù)。比如有如下代碼:
// 例 2.1
const arr = [1, 2, 3]
const string = 'Jay'
function fn(a, b, c) {
console.log(a, b, c)
}
fn 接收 3 個參數(shù),如果我們想把數(shù)組 arr 中的每個元素傳給 fn,在 ES6 之前可以利用 apply 實現(xiàn):
// 例 2.1.1 fn.apply(null, arr) // 1 2 3
有了展開語法,則可以直接將作為參數(shù)的數(shù)組 arr 展開,這樣可以提高代碼的可讀性 :
// 例 2.1.2 fn(...arr) // 1 2 3
展開語法還可以將字符串展開:
// 例 2.1.3 fn(...string) // J a y
2)構造數(shù)組時
通過字面量方式構造數(shù)組時,也可以使用展開語法:
// 例 2.2 const arr1 = [1, 2] const arr2 = [3, 4, 5] // 構造字面量數(shù)組 const newArr = [...arr1, ...arr2] console.log(newArr) // [ 1, 2, 3, 4, 5 ]
3)構造字面量對象時
除了構造數(shù)組,ES2018(ES9) 添加了新的特性使得構造對象的時候也可以運用展開語法,被展開的對象表達式將按 key-value 的方式展開 :
// 例 2.3.1
const obj = { name: 'Jay' }
const newObj = { ...obj, age: 40 }
console.log(newObj) // { name: 'Jay', age: 40 }
在構造對象字面量時,還可以把數(shù)組也放進去展開,比如在 MDN 上關于展開語法的說明中有這么個例子:
// 例 2.3.2
const obj1 = { foo: 'bar', x: 42 }
const obj2 = { foo: 'baz', y: 13 }
const merge = (...objects) => ({ ...objects })
const mergedObj = merge(obj1, obj2)
console.log(mergedObj) // { '0': { foo: 'bar', x: 42 }, '1': { foo: 'baz', y: 13 } }
乍看上去迷迷糊糊的,可能的難點在于第 4 行代碼的理解,它定義了一個函數(shù) const merge = (...objects) => ({ ...objects }, 其中=> 左邊的 ...objects 為函數(shù)的剩余參數(shù)語法,所以 objects 就是一個由 obj1 和 obj2 這兩個對象組成的數(shù)組:[ { foo: 'bar', x: 42 }, { foo: 'baz', y: 13 } ];=>右邊的 ...objects 用到的是展開語法,用于構造字面量對象,只不過因為 objects 是個數(shù)組,所以展開數(shù)組得到的對象的 key 為數(shù)組的下標,即 { ...objects } 得到的對象為:{ '0': { foo: 'bar', x: 42 }, '1': { foo: 'baz', y: 13 } }。
注意
在調用函數(shù)或構造數(shù)組時使用展開語法只能用于可迭代對象,比如不能在構造數(shù)組時把一個對象放進去使用展開語法。
展開語法其實是一種淺拷貝
在 MDN 文檔還能看到這么一句話:
實際上, 展開語法和 Object.assign() 行為一致, 執(zhí)行的都是淺拷貝(只遍歷一層)。
也就是說類似下面的例 2.4:
// 例 2.4
const obj = {
a: 'a',
b: {
c: 'd'
}
}
const newObj = { ...obj }
newObj.b.c = '我變了'
console.log(obj.b.c) // 我變了
我們改變通過展開語法得到的 newObj 的深層屬性 newObj.b.c = '我變了' 時,會導致 obj.b.c 的值一同被修改。原因可以通過畫內存表現(xiàn)圖說明:

const newObj = { ...obj } 生成的 newObj,只是把堆內存中 obj 指向的地址為 0x100 這個對象整個復制了一份,生成了地址為 0x300 的新對象(注意這個新對象里,b 的值是一個內存地址),然后讓 newObj 指向它。如果我們更改了 newObj 的 a 屬性或 b 屬性的值,當然不會影響到 obj 對象,但是如果我們改變的是 newObj.b.c,就會找到 0x200 這個對象并修改,自然會影響到 obj 對象。
用法三:解構數(shù)組時
在對數(shù)組進行解構賦值時,我們可以使用剩余模式,將剩余數(shù)組賦值給一個變量,效果上類似于函數(shù)的剩余參數(shù)。比如下面例 3.1 中的 c:
// 例 3.1 const arr = [1, 2, 3, 4, 5] const [a, b, ...c] = arr console.log(c) // [ 3, 4, 5 ]
以上就是js 中以 ... 為前綴的幾種用法詳解的詳細內容,更多關于js ... 前綴用法的資料請關注腳本之家其它相關文章!
相關文章
詳談for循環(huán)里面的break和continue語句
下面小編就為大家?guī)硪黄斦刦or循環(huán)里面的break和continue語句。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-07-07
JavaScript優(yōu)化以及前段開發(fā)小技巧
隨著前端技術的發(fā)展,前端業(yè)務越來越繁重,這大大增加了JS代碼量。因此,要提高Web的性能,我們不僅需要關注頁面加載的時間,還要注重在頁面上操作的響應速度。那么,接下來我們討論幾種能夠提高JavaScript效率的方法。2017-02-02
一文帶你搞懂Electron如何優(yōu)雅的進行進程間通訊
這篇文章主要為大家詳細介紹了Electron是如何優(yōu)雅的進行進程間通訊的,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下2024-11-11

