理解對象的擴展運算符其實很簡單,只要記住一句話就可以:
對象中的擴展運算符(...)用于取出參數(shù)對象中的所有可遍歷屬性,拷貝到當前對象之中
let bar = { a: 1, b: 2 };
let baz = { ...bar }; // { a: 1, b: 2 }
上述方法實際上等價于:
let bar = { a: 1, b: 2 };
let baz = Object.assign({}, bar); // { a: 1, b: 2 }
Object.assign方法用于對象的合并,將源對象(source)的所有可枚舉屬性,復制到目標對象(target)。
Object.assign方法的第一個參數(shù)是目標對象,后面的參數(shù)都是源對象。(如果目標對象與源對象有同名屬性,或多個源對象有同名屬性,則后面的屬性會覆蓋前面的屬性)。
同樣,如果用戶自定義的屬性,放在擴展運算符后面,則擴展運算符內(nèi)部的同名屬性會被覆蓋掉。
let bar = {a: 1, b: 2};
let baz = {...bar, ...{a:2, b: 4}}; // {a: 2, b: 4}
利用上述特性就可以很方便的修改對象的部分屬性。在redux中的reducer函數(shù)規(guī)定必須是一個純函數(shù)(如果不是很清楚什么是純函數(shù)的可以參考這里),reducer中的state對象要求不能直接修改,可以通過擴展運算符把修改路徑的對象都復制一遍,然后產(chǎn)生一個新的對象返回。
這里有點需要注意的是擴展運算符對對象實例的拷貝屬于一種淺拷貝??隙ㄓ腥艘獑柺裁词菧\拷貝?我們知道javascript中有兩種數(shù)據(jù)類型,分別是基礎(chǔ)數(shù)據(jù)類型和引用數(shù)據(jù)類型。
基礎(chǔ)數(shù)據(jù)類型是按值訪問的,常見的基礎(chǔ)數(shù)據(jù)類型有Number、String、Boolean、Null、Undefined,這類變量的拷貝的時候會完整的復制一份;引用數(shù)據(jù)類型比如Array,在拷貝的時候拷貝的是對象的引用,當原對象發(fā)生變化的時候,拷貝對象也跟著變化,
比如:
let obj1 = { a: 1, b: 2};
let obj2 = { ...obj1, b: '2-edited'};
console.log(obj1); // {a: 1, b: 2}
console.log(obj2); // {a: 1, b: "2-edited"}
上面這個例子擴展運算符拷貝的對象是基礎(chǔ)數(shù)據(jù)類型,因此對obj2的修改并不會影響obj1,如果改成這樣
let obj1 = { a: 1, b: 2, c: {nickName: 'd'}};
let obj2 = { ...obj1};
obj2.c.nickName = 'd-edited';
console.log(obj1); // {a: 1, b: 2, c: {nickName: 'd-edited'}}
console.log(obj2); // {a: 1, b: 2, c: {nickName: 'd-edited'}}
這里可以看到,對obj2的修改影響到了被拷貝對象obj1,原因上面已經(jīng)說了,因為obj1中的對象c是一個引用數(shù)據(jù)類型,拷貝的時候拷貝的是對象的引用。
數(shù)組的擴展運算符
擴展運算符同樣可以運用在對數(shù)組的操作中。
簡單用法
將數(shù)組分成一個個元素
console.log(...[1, 2, 3]);//1,2,3 將數(shù)組分成一個個元素
console.log(...[{a:1,d:1},{b:2}, {c:3}]);//{a:1,d:1},{b:2} ,{b:2} ,{c:3}
該運算符主要用于函數(shù)調(diào)用,可以將數(shù)組轉(zhuǎn)換為參數(shù)序列
function add(x, y) {
return x + y;
}
const numbers = [4, 38];
add(...numbers) // 42
復制數(shù)組
如果直接通過下列的方式進行數(shù)組復制是不可取的:
const arr1 = [1, 2];
const arr2 = arr1;
arr2[0] = 2;
arr1 // [2, 2]
//這樣就是就淺拷貝了
還是記住那句話:擴展運算符(…)用于取出參數(shù)對象中的所有可遍歷屬性,拷貝到當前對象之中,這里參數(shù)對象是個數(shù)組,數(shù)組里面的所有對象都是基礎(chǔ)數(shù)據(jù)類型,將所有基礎(chǔ)數(shù)據(jù)類型重新拷貝到新的數(shù)組中。
// //復制數(shù)組屬于深拷貝
const a1 = [1, 2];
// // 寫法一
const a2 = [...a1];
// // 寫法二
const [...a2] = a1;
合并數(shù)組:
// //合并數(shù)組
// ES5 的合并數(shù)組
arr1.concat(arr2, arr3);
// [ 'a', 'b', 'c', 'd', 'e' ]
const arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3 = ['d', 'e'];
[...arr1, ...arr2, ...arr3]
數(shù)組去重·、;
方法一:利用展開運算符和Set成員的唯一性
let arr = [1, 2, 3, 2, 1];
function unique(arr){
return [...new Set(arr)];
}
console.log(unique(arr)) // [1, 2, 3]
擴展運算符可以與解構(gòu)賦值結(jié)合起來,用于生成數(shù)組
const [first, ...rest] = [1, 2, 3, 4, 5];
first // 1
rest // [2, 3, 4, 5]
// ES5
a = list[0], rest = list.slice(1)
// ES6
[a, ...rest] = list
下面是另外一些例子。
const [first, ...rest] = [];
first // undefined
rest // []
const [first, ...rest] = ["foo"];
first // "foo"
rest // []
需要注意的一點是:
如果將擴展運算符用于數(shù)組賦值,只能放在參數(shù)的最后一位,否則會報錯
const [...rest, last] = [1, 2, 3, 4, 5];
// 報錯
const [first, ...rest, last] = [1, 2, 3, 4, 5];
// 報錯
擴展運算符還可以將字符串轉(zhuǎn)為真正的數(shù)組
[...'hello']
// [ "h", "e", "l", "l", "o" ]
Array.from() 將類似組轉(zhuǎn)化為真正的數(shù)組
注意類數(shù)組一定要有以下倆點:
1.有l(wèi)ength屬性
2.屬性Key為0,1,2...等number
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
// ES5的寫法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']
// [類數(shù)組轉(zhuǎn)數(shù)組Array.prototype.slice.call(arrayLike)
//首先Array.prototype.slice.call(arrayLike)的結(jié)果是將arrayLike對象轉(zhuǎn)換成一個Array對象。所以其后面可以直接調(diào)用數(shù)組具//有的方法。譬如
//Array.prototype.slice.call(arrayLike).forEach(function(element,index){ //可以隨意操作每一個element了 })
// ES6的寫法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
實際應(yīng)用中,常見的類似數(shù)組的對象是 DOM 操作返回的 NodeList 集合,以及函數(shù)內(nèi)部的arguments對象。Array.from都可以將它們轉(zhuǎn)為真正的數(shù)組。
// NodeList對象
let ps = document.querySelectorAll('p');
Array.from(ps).filter(p => {
return p.textContent.length > 100;
});
// arguments對象
function foo() {
var args = Array.from(arguments);
// ...
}
這里講講arguments對象的實例使用
arguments 是一個對應(yīng)于傳遞給函數(shù)的參數(shù)的類數(shù)組對象。
function abc(a,b,c){
//看看arguments是什么
console.log(arguments);
}
abc(1,2,3) ; //[1,2,3] 這不是數(shù)組嗎
function abc(a,b,c){
//看看arguments是什么類型的
console.log(typeof arguments)
}
abc(1,2,3) ; //object 這是對象,不是數(shù)組
看看,是不是有點意思,似數(shù)組非數(shù)組 lg:那它怎么用呢~ 這個問題問的好,看下面代碼:
function abc(a,b,c){
var len = arguments.length;
console.log(len);
console.log(arguments[0]);
console.log(arguments[1]);
}
abc(1,2,3); // 3 , 1 , 2
//長度為3,第一個值為1,第二個值為2... 看來是具有對應(yīng)關(guān)系的
它有一個非常實用的功能,就是在函數(shù)中,無需明確指出參數(shù)名(無需形參),arguments能直接訪問它:
function abc(){
var a = arguments;
var num = arguments[0] + arguments[1] + arguments[2]; //這里可以用循環(huán),為方便查看,拆開了寫,
console.log(num);
}
abc(1,2,3); // 6 說明可以獲取
//正常來說,函數(shù)沒有參數(shù),應(yīng)該是要報錯的,如果能成功運行,說明它具備這個神級
arguments對象中有一個非常有用的屬性:calle,arguments.callee返回此arguments對象所在的當前函數(shù)引用。在使用函數(shù)遞歸調(diào)用時推薦使用arguments.callee代替函數(shù)名本身。舉個例子:
function abc(a){
if(a==1){
console.log(arguments.callee); //直接輸出當前函數(shù)
return 1;
}
return a + arguments.callee(--a);
}
var mm = abc(10);
console.log(mm); // 55<br><br>//arguments.callee(--a) 執(zhí)行了遞歸調(diào)用,這樣就完成了1~9的累加
如果參數(shù)是一個真正的數(shù)組,Array.from會返回一個一模一樣的新數(shù)組。
Array.from([1, 2, 3])
// [1, 2, 3]
Array.from還可以接受第二個參數(shù),作用類似于數(shù)組的map方法,用來對每個元素進行處理,將處理后的值放入返回的數(shù)組。
Array.from(arrayLike, x => x * x);
// 等同于
Array.from(arrayLike).map(x => x * x);
Array.from([1, 2, 3], (x) => x * x)
// [1, 4, 9]
注意箭頭函數(shù)簡寫的話只需要一個簡單的表達式,會附加返回值,但是在塊體重必須聲明
//下面的例子將數(shù)組中布爾值為false的成員轉(zhuǎn)為0。
Array.from([1, , 2, , 3], (n) => n || 0)
// [1, 0, 2, 0, 3]
Array.of()
Array.of方法用于將一組值,轉(zhuǎn)換為數(shù)組。
Array.of總是返回參數(shù)值組成的數(shù)組。如果沒有參數(shù),就返回一個空數(shù)組。
Array.of(3, 11, 8) // [3,11,8]
Array.of(3) // [3]
Array.of(3).length // 1
數(shù)組實例的 copyWithin()
數(shù)組實例的copyWithin()方法,在當前數(shù)組內(nèi)部,將指定位置的成員復制到其他位置(會覆蓋原有成員),然后返回當前數(shù)組。也就是說,使用這個方法,會修改當前數(shù)組。
Array.prototype.copyWithin(target, start = 0, end = this.length)
它接受三個參數(shù)。
target(必需):從該位置開始替換數(shù)據(jù)。如果為負值,表示倒數(shù)。
start(可選):從該位置開始讀取數(shù)據(jù),默認為 0。如果為負值,表示從末尾開始計算。
end(可選):到該位置前停止讀取數(shù)據(jù),默認等于數(shù)組長度。如果為負值,表示從末尾開始計算。
這三個參數(shù)都應(yīng)該是數(shù)值,如果不是,會自動轉(zhuǎn)為數(shù)值。
[1, 2, 3, 4, 5].copyWithin(0, 3)
// [4, 5, 3, 4, 5]
上面代碼表示將從 3 號位直到數(shù)組結(jié)束的成員(4 和 5),復制到從 0 號位開始的位置,結(jié)果覆蓋了原來的 1 和 2。
下面是更多例子。
// 將3號位復制到0號位
[1, 2, 3, 4, 5].copyWithin(0, 3, 4)
// [4, 2, 3, 4, 5]
// -2相當于3號位,-1相當于4號位
[1, 2, 3, 4, 5].copyWithin(0, -2, -1)
// [4, 2, 3, 4, 5]
// 將3號位復制到0號位
[].copyWithin.call({length: 5, 3: 1}, 0, 3)
// {0: 1, 3: 1, length: 5}
// 將2號位到數(shù)組結(jié)束,復制到0號位
let i32a = new Int32Array([1, 2, 3, 4, 5]);
i32a.copyWithin(0, 2);
// Int32Array [3, 4, 5, 4, 5]
// 對于沒有部署 TypedArray 的 copyWithin 方法的平臺
// 需要采用下面的寫法
[].copyWithin.call(new Int32Array([1, 2, 3, 4, 5]), 0, 3, 4);
// Int32Array [4, 2, 3, 4, 5]
對象的解構(gòu)與數(shù)組有一個重要的不同。數(shù)組的元素是按次序排列的,變量的取值由它的位置決定;而對象的屬性沒有次序,變量必須與屬性同名,才能取到正確的值。
具體參照阮一峰老師es6入門寶典 http://es6.ruanyifeng.com/#docs/array