1.构造数组
字面量方式:
let a = []; //最常用的,简洁方便
a.length = 3; // [undefined × 3]
使用构造器:
let b = new Array(); //[]
let c = Array(3); //[undefined × 3]
let d = Array(1,2,3); //[1,2,3]
数组长度最大为Math.pow(2,32)-1 ,即 4294967295 位
两种方式性能PK?
new Array() < []
2.Array.of
Array.of用于将参数依次转化为数组中的一项,然后返回这个新数组,而不管这个参数是数字还是其它。它基本上与Array构造器功能一致,唯一的区别就在单个数字参数的处理上。如下:
Array.of(3); // [3]
Array(3); // [undefined × 3]
参数为多个,或单个参数不是数字时,Array.of 与 Array构造器等同:
Array.of(1, 2); // [1, 2]
Array(1, 2); // [1, 2]
Array.of('8'); // ["8"]
Array('8'); // ["8"]
即使其他版本浏览器不支持也不必担心,由于Array.of与Array构造器的这种高度相似性,实现一个polyfill十分简单。如下:
if (!Array.of){
Array.of = function(){
return Array.prototype.slice.call(arguments);
//return [].slice.call(arguments);
};
}
3.Array.from 语法:Array.from(arrayLike, processingFn, thisArg)
Array.from的设计初衷是快速便捷的基于其他对象创建新数组,准确来说就是从一个类似数组的可迭代对象创建一个新的数组实例
从语法上看,Array.from拥有3个形参,第一个为类似数组的对象,必选。第二个为加工函数,新生成的数组会经过该函数的加工再返回。第三个为this作用域,表示加工函数执行时this的值,后两个参数都是可选的。
var obj = {0: 'a', 1: 'b', 2:'c', length: 3};
Array.from(obj, (value) => value.repeat(2), obj); //["aa","bb","cc"]
生成一个从0到指定数字的新数组,Array.from就可以轻易的做到:
Array.from({length: 10}, (v, i) => i); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
4.Array.isArray 用来判断一个变量是否数组类型
Array.isArray([]); // true
Array.isArray({0: 'a', length: 1}); // false
除了这个方法还有Object.prototype.toString 判断是否为数组
5.方法 unshift、shift、push、pop、splice、reverse、sort,以及两个ES6新增的方法flat、flatMap、copyWithin 和 fill, 以上方法会改变数组本身。
unshift 语法:arr.unshift(element1, …, elementN)
unshift() 方法用于在数组开始处插入一些元素(就像是栈底插入),并返回数组新的长度。
let array = ["red", "green", "blue"];
let length = array.unshift("yellow");
console.log(array); // ["yellow", "red", "green", "blue"]
console.log(length); // 4
shift 语法:arr.shift()
shift()方法删除数组的第一个元素,并返回这个元素。
let array = [1,2,3,4,5];
let item = array.shift();
console.log(array); // [2,3,4,5]
console.log(item); // 1
push 语法:arr.push(element1, …, elementN)
push()方法添加一个或者多个元素到数组末尾,并且返回数组新的长度。
let array = ["football", "basketball", "volleyball"];
let i = array.push("Table tennis");
console.log(array); // ["football", "basketball", "volleyball", "Table tennis"]
console.log(i); // 4
pop 语法:arr.pop()
pop() 方法删除一个数组中的最后的一个元素,并且返回这个元素。
let array = ["cat", "dog", "cow", "chicken", "mouse"];
let item = array.pop();
console.log(array); // ["cat", "dog", "cow", "chicken"]
console.log(item); // mouse
splice 语法:arr.splice(start,deleteCount,[item1, item2, …])
splice()方法用新元素替换旧元素的方式来修改数组。
start 为开始位置,如果超过了数组长度,则从数组末尾开始添加内容;如果是负值,则其指定的索引位置等同于 length+start (length为数组的长度),表示从数组末尾开始的第 -start 位。
deleteCount 指定要删除的元素个数,若等于0,则不删除。这种情况下,至少应该添加一位新元素,若大于start之后的元素总和,则start及之后的元素都将被删除。
itemN 指定新增的元素,如果缺省,则该方法只删除数组元素。
返回值 由原数组中被删除元素组成的数组,如果没有删除,则返回一个空数组。
let array = ["apple","boy"];
let splices = array.splice(-3,1,"cat");
console.log(array); // ["cat", "boy"]
console.log(splices); // ["apple"], 可见即使-start超出数组长度,数组默认从首位开始删除
------
let array = ["apple","boy"];
let splices = array.splice(3,3,"cat");
console.log(array); // ['apple', 'boy', 'cat']
console.log(splices); // []
可见当start超出数组长度,数组没变化,默认从末尾该添加啥添加啥
reverse 语法:arr.reverse()
reverse()方法颠倒数组中元素的位置,第一个会成为最后一个,最后一个会成为第一个,该方法返回对数组的引用。
let array = [1,2,3,4,5];
let array2 = array.reverse();
console.log(array); // [5,4,3,2,1]
console.log(array2===array); // true
sort 语法:arr.sort([comparefn])
sort()方法对数组元素进行排序,并返回这个数组。
comparefn是可选的,如果省略,数组元素将按照各自转换为字符串的Unicode(万国码)位点顺序排序,例如”Boy”将排到”apple”之前。当对数字排序的时候,25将会排到8之前,因为转换为字符串后,”25”将比”8”靠前。例如:
let array = ["Boy","apple","Cat","dog"];
let array2 = array.sort();
console.log(array); // ["Boy", "Cat", "apple", "dog"]
console.log(array2 == array); // true
let array3 = array.sort((a, b) => a.localeCompare(b));
console.log(array3)//["apple", "Boy", "Cat", "dog"]
let array = [10, 1, 3, 20];
let array1 = array.sort();
console.log(array1); // [1, 10, 20, 3]
let array2 = array.sort((a, b) => a-b);
console.log(array2)//[1, 3, 10, 20]
实际上,ECMAscript规范中并未规定具体的sort算法,这就势必导致各个浏览器不尽相同的sort算法,请看sort方法在Chrome浏览器下表现:
let array = [{ n: "a", v: 1 }, { n: "b", v: 1 }, { n: "c", v: 1 }, { n: "d", v: 1 }, { n: "e", v: 1 }, { n: "f", v: 1 }, { n: "g", v: 1 }, { n: "h", v: 1 }, { n: "i", v: 1 }, { n: "j", v: 1 }, { n: "k", v: 1 }, ];
array.sort((a, b) => return a.v - b.v);
for (let i = 0,len = array.length; i < len; i++) {
console.log(array[i].n);
}
由于v值相等,array数组排序前后应该不变,然而Chrome却表现异常,而其他浏览器(如IE 或 Firefox) 表现正常。
这是因为v8引擎为了高效排序(采用了不稳定排序)。即数组长度超过10条时,会调用另一种排序方法(快速排序);而10条及以下采用的是插入排序,此时结果将是稳定的。
解决办法:
// 由于快速排序会打乱值相同的元素的默认排序,因此我们需要先标记元素的默认位置
array.forEach((v, k) =>v.__index = k);
// 由于__index标记了初始顺序,这样的返回才保证了值相同元素的顺序不变,进而使得排序稳定
array.sort((a, b) => return a.v - b.v || a.__index - b.__index);
值得注意的是:
各浏览器的针对sort方法内部算法实现不尽相同,排序函数尽量只返回-1、0、1三种不同的值,不要尝试返回true或false等其它数值,因为可能导致不可靠的排序结果。
let array = [7, 6, 5, 4, 3, 2, 1, 0, 10, 9, 8];
let comparefn = (x, y) => x > y;
array.sort(comparefn);
flat 语法:arr.flat(depth)
flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
var arr1 = [1, 2, [3, 4]];
arr1.flat();
// [1, 2, 3, 4]
var arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat(2);
// [1, 2, 3, 4, 5, 6]
//使用 Infinity 作为深度,展开任意深度的嵌套数组
arr2.flat(Infinity);
// [1, 2, 3, 4, 5, 6]
var arr4 = [1, 2, , 4, 5];
arr4.flat(); //flat() 方法会移除数组中的空项:
// [1, 2, 4, 5]
flatMap 语法:arr.flatMap(fn,thisArg)
flatMap() 方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。它与 map 和 深度值1的 flat 几乎相同,但 flatMap 通常在合并成一种方法的效率稍微高一些。
var arr1 = [1, 2, 3, 4];
arr1.flatMap(x => [x * 2]);
// [2, 4, 6, 8]
// 只会将 flatMap 中的函数返回的数组 “压平” 一层
arr1.flatMap(x => [[x * 2]]);
// [[2], [4], [6], [8]]
copyWithin(ES6) 语法:arr.copyWithin(target, start, end)
copyWithin() 方法基于ECMAScript 2015(ES6)规范,用于数组内元素之间的替换,即替换元素和被替换元素均是数组内的元素。
其中,taget 指定被替换元素的索引,start 指定替换元素起始的索引,end 可选,指的是替换元素结束位置的索引。
如果start为负,则其指定的索引位置等同于length+start,length为数组的长度。end也是如此。
let array = [1,2,3,4,5];
let array2 = array.copyWithin(0,3);
console.log(array===array2,array2); // true [4, 5, 3, 4, 5]
let array = [1,2,3,4,5];
console.log(array.copyWithin(0,3,4)); // [4, 2, 3, 4, 5]
let array = [1,2,3,4,5];
console.log(array.copyWithin(0,-2,-1)); // [4, 2, 3, 4, 5]
fill(ES6) 语法:arr.fill(value, start, end)
fill() 方法基于ECMAScript 2015(ES6)规范,它同样用于数组元素替换,但与copyWithin略有不同,它主要用于将数组指定区间内的元素替换为某个值。
其中,value 指定被替换的值,start 指定替换元素起始的索引,end 可选,指的是替换元素结束位置的索引。
如果start为负,则其指定的索引位置等同于length+start,length为数组的长度。end也是如此。
let array = [1,2,3,4,5];
let array2 = array.fill(10,0,3);
console.log(array===array2,array2); // true [10, 10, 10, 4, 5], 可见数组区间[0,3]的元素全部替换为10
6. concat、join、slice、toString、toLocateString、indexOf、lastIndexOf、未标准的toSource以及ES7新增的方法includes,不会改变自身的方法
concat 语法:arr.concat(value1, value2, …, valueN)
concat() 方法将传入的数组或者元素与原数组合并,组成一个新的数组并返回。
let array = [1, 2, 3];
let array2 = array.concat(4,[5,6],[7,8,9]);
console.log(array2); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log(array); // [1, 2, 3], 可见原数组并未被修改
若concat方法中不传入参数,那么将基于原数组浅复制生成一个一模一样的新数组(指向新的地址空间)。
let array = [{a: 1}];
let array3 = array.concat();
console.log(array3); // [{a: 1}]
console.log(array3 === array); // false
console.log(array[0] === array3[0]); // true,新旧数组第一个元素依旧共用一个同一个对象的引用
join 语法:arr.join([separator = ‘,’]) separator可选,缺省默认为逗号。
join() 方法将数组中的所有元素连接成一个字符串。
let array = ['We', 'are', 'Chinese'];
console.log(array.join()); // "We,are,Chinese"
console.log(array.join('+')); // "We+are+Chinese"
console.log(array.join('')); // "WeareChinese"
slice 语法:arr.slice(start, end)
slice() 方法将数组中一部分元素浅复制存入新的数组对象,并且返回这个数组对象。
参数 start 指定复制开始位置的索引,end如果有值则表示复制结束位置的索引(不包括此位置)。
如果 start 的值为负数,假如数组长度为 length,则表示从 length+start 的位置开始复制,此时参数 end 如果有值,只能是比 start 大的负数,否则将返回空数组。
slice方法参数为空时,同concat方法一样,都是浅复制生成一个新数组。
let array = ["one", "two", "three","four", "five"];
console.log(array.slice()); // ["one", "two", "three","four", "five"]
console.log(array.slice(2,3)); // ["three"]
let array = [{color:"yellow"}, 2, 3];
let array2 = array.slice(0,1);
console.log(array2); // [{color:"yellow"}]
array[0]["color"] = "blue";
console.log(array2); // [{color:"bule"}]
toString 语法:arr.toString()
toString() 方法返回数组的字符串形式,该字符串由数组中的每个元素的 toString() 返回值经调用 join() 方法连接(由逗号隔开)组成。
let array = ['Jan', 'Feb', 'Mar', 'Apr'];
let str = array.toString();
console.log(str); // Jan,Feb,Mar,Apr
toLocaleString 语法:arr.toLocaleString()
toLocaleString() 类似toString()的变型,该字符串由数组中的每个元素的 toLocaleString() 返回值经调用 join() 方法连接(由逗号隔开)组成。
let array= [{name:'zz'}, 123, "abc", new Date()];
let str = array.toLocaleString();
console.log(str); // [object Object],123,abc,2019/3/28 下午12:16:23
toString()和toLocaleString()两点区别:
1.当数字是四位数及以上时
let a=1234.5678
a.toString()//"1234.5678"
a.toLocaleString()//"1,234.568"
2.当目标是标准时间格式时
let sd=new Date()//Thu Mar 07 2019 12:11:55 GMT+0800 (中国标准时间)
sd.toLocaleString()//"2019/3/7 下午12:21:55"
sd.toString()//Thu Mar 07 2019 12:11:55 GMT+0800
indexOf 语法:arr.indexOf(element, fromIndex=0)
indexOf() 方法用于查找元素在数组中第一次出现时的索引,如果没有,则返回-1。
element 为需要查找的元素,fromIndex 为开始查找的位置,缺省默认为0。如果超出数组长度,则返回-1。如果为负值,假设数组长度为length,则从数组的第 length + fromIndex项开始往数组末尾查找,如果length + fromIndex<0 则整个数组都会被查找。
let array = ['abc', 'def', 'ghi','123'];
console.log(array.indexOf('def')); // 1
console.log(array.indexOf('def',-1)); // -1 此时表示从最后一个元素往后查找,因此查找失败返回-1
console.log(array.indexOf('def',-4)); // 1 由于4大于数组长度,此时将查找整个数组,因此返回1
console.log(array.indexOf(123)); // -1, 由于是严格匹配,因此并不会匹配到字符串'123'
lastIndexOf 语法:arr.lastIndexOf(element, fromIndex=length-1)
lastIndexOf() 方法用于查找元素在数组中最后一次出现时的索引,如果没有,则返回-1。并且它是indexOf的逆向查找,即从数组最后一个往前查找。
element 为需要查找的元素,fromIndex 为开始查找的位置,缺省默认为arr.length-1。如果超出数组长度,则返回-1。如果为负值,假设数组长度为length,则从数组的第 length + fromIndex项开始往数组栈底查找,如果length + fromIndex<0 则整个数组都会被查找。
同 indexOf 一样,lastIndexOf 也是严格匹配数组元素。
includes(ES7) 语法:arr.includes(element, fromIndex=0)
includes() 方法基于ECMAScript 2016(ES7)规范,它用来判断当前数组是否包含某个指定的值,如果是,则返回 true,否则返回 false。
element 为需要查找的元素,fromIndex 表示从该索引位置开始查找 element,缺省为0,它是正向查找,即从索引处往数组末尾查找。
let array = [0, 1, 2];
console.log(array.includes(0)); // true
console.log(array.includes(1)); // true
console.log(array.includes(2,-4)); // true
你可能会问,既然有了indexOf方法,为什么又造一个includes方法,arr.indexOf(x)>-1不就等于arr.includes(x)?看起来是的,几乎所有的时候它们都等同,唯一的区别就是includes能够发现NaN,而indexOf不能。
let array = [NaN];
console.log(array.includes(NaN)); // true
console.log(arra.indexOf(NaN)>-1); // false
7. 基于ES6,不会改变自身的方法一共有12个,分别为forEach、every、some、filter、map、reduce、reduceRight 以及ES6新增的方法entries、find、findIndex、keys、values。
forEach 语法:arr.forEach(fn(value, index,array), thisArg)
forEach() 方法指定数组的每项元素都执行一次传入的函数,返回值为undefined。
fn 表示在数组每一项上执行的函数,接受三个参数:
value 当前正在被处理的元素的值
index 当前元素的数组索引
array 数组本身
thisArg 可选,用来当做fn函数内的this对象。
forEach 将为数组中每一项执行一次 fn 函数,那些已删除,新增或者从未赋值的项将被跳过(但不包括值为 undefined 的项)。
let array = [1, 3, 5];
let obj = {name:'cc'};
let sReturn = array.forEach(function(value, index, array){
array[index] = value * value;
console.log(this.name); // cc被打印了三次
},obj);
console.log(array); // [1, 9, 25], 可见原数组改变了
console.log(sReturn); // undefined, 可见返回值为undefined
*forEach无法直接退出循环,只能使用return 来达到for循环中continue的效果
*它总是返回 undefined值,即使你return了一个值。
1. 对于空数组是不会执行回调函数的
2. 对于已在迭代过程中删除的元素,或者空元素会跳过回调函数
3. 遍历次数再第一次循环前就会确定,再添加到数组中的元素不会被遍历。
4. 如果已经存在的值被改变,则传递给 callback 的值是遍历到他们那一刻的值。
eg:
let a = [1, 2, ,3]; // 最后第二个元素是空的,不会遍历(undefined、null会遍历)
let obj = { name: 'obj的名字' };
let result = a.forEach(function (value, index, array) {
a[3] = '改变元素';
a.push('添加到尾端,不会被遍历')
console.log(value, 'forEach传递的第一个参数'); // 分别打印 1 ,2 ,改变元素
console.log(this.name); // 'obj的名字' 打印三次 this绑定在obj对象上
// break; // break会报错
return value; // return只能结束本次回调 会执行下次回调
console.log('不会执行,因为return 会执行下一次循环回调')
}, obj);
console.log(result); // 即使return了一个值,也还是返回undefined
every 语法:arr.every(fn, thisArg)
every()方法检测数组所有元素是否都符合判断条件(参数同上)
方法返回值规则:
1 ,如果数组中检测到有一个元素不满足,则整个表达式返回 false,且剩余的元素不会再进行检测
2. 如果所有元素都满足条件,则返回 true
eg:
function isBigEnough(element, index, array) {
return element >= 10; // 判断数组中的所有元素是否都大于10
}
[12, 5, 8, 130, 44].every(x => x >= 10); // false
[12, 54, 18, 130, 44].every(x => x >= 10); // true
some 语法:arr.some(fn, thisArg)
some()数组中的是否有满足判断条件的元素(参数同上)
方法返回值规则:
1 ,如果有一个元素满足条件,则表达式返回true, 剩余的元素不会再执行检测
2. 如果没有满足条件的元素,则返回false
eg:
function isBigEnough(element, index, array) {
return (element >= 10); //数组中是否有一个元素大于 10
}
let result = [2, 5, 8, 1, 4].some(isBigEnough); // false
let result = [12, 5, 8, 1, 4].some(isBigEnough); // true
filter 语法:arr.filter(fn, thisArg)
filter()过滤原始数组,返回通过所提供函数实现的所有元素组成的新数组(参数同上)
eg:
let a = [32, 33, 16, 40];
let result = a.filter(function (value, index, array) {
return value >= 18; // 返回a数组中所有大于18的元素
});
console.log(result,a);// [32,33,40] [32,33,16,40]
map 语法:arr.map(fn, thisArg)
map()对数组中的每个元素进行处理,返回新的数组(参数同上)
eg:
let a = ['1','2','3','4'];
let result = a.map(function (value, index, array) {
return value + 0
});
console.log(result); //["10", "20", "30", "40"]
reduce 语法:array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
reduce()为数组提供累加器,合并为一个值
1. 如果 initialValue 在调用 reduce 时被提供,那么第一个 total 将等于 initialValue,此时 currentValue 等于数组中的第一个值
2. 如果 initialValue 未被提供,那么 total 等于数组中的第一个值,currentValue 等于数组中的第二个值。此时如果数组为空,那么将抛出 TypeError
3. 如果数组仅有一个元素,并且没有提供 initialValue,或提供了 initialValue 但数组为空,那么回调不会被执行,数组的唯一值将被返回
eg:
// 数组求和
let sum = [0, 1, 2, 3].reduce(function (a, b) {
return a + b;
}, 0);
// 6
------
// 将二维数组转化为一维 将数组元素展开//见flat()
let flattened = [[0, 1], [2, 3], [4, 5]].reduce((a, b) => a.concat(b),[]);
// [0, 1, 2, 3, 4, 5]
reduceRight 语法:array.reduceRight(function(total, currentValue, currentIndex, arr), initialValue)
这个方法除了与reduce执行方向相反外,其他完全与其一致
ES6 keys()&values()&entries() 遍历键名、遍历键值、遍历键名+键值
三个方法都返回一个新的 Array Iterator 对象,对象根据方法不同包含不同的值
eg:
for (let index of ['a', 'b'].keys()) {
console.log(index);
}
// 0
// 1
for (let elem of ['a', 'b'].values()) {
console.log(elem);
}
// 'a'
// 'b'
for (let [index, elem] of ['a', 'b'].entries()) {
console.log(index, elem);
}
// 0 "a"
// 1 "b"
在for..of中如果遍历中途要退出,可以使用break退出循环。
如果不使用for...of循环,可以手动调用遍历器对象的next方法,进行遍历:
let letter = ['a', 'b', 'c'];
let entries = letter.entries();
console.log(entries.next().value); // [0, 'a']
console.log(entries.next().value); // [1, 'b']
console.log(entries.next().value); // [2, 'c']
ES6 find()& findIndex() 根据条件找到数组成员 语法:arr.find(fn, thisArg),arr.findIndex(fn, thisArg)
find()定义:用于找出第一个符合条件的数组成员,并返回该成员,如果没有符合条件的成员,则返回undefined。
findIndex()定义:返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1。
这两个方法都可以识别NaN,弥补了indexOf的不足.
eg:
// find
let a = [1, 4, -5, 10].find((n) => n < 0); // 返回元素-5
let b = [1, 4, -5, 10,NaN].find((n) => Object.is(NaN, n)); // 返回元素NaN
// findIndex
let a = [1, 4, -5, 10].findIndex((n) => n < 0); // 返回索引2
let b = [1, 4, -5, 10,NaN].findIndex((n) => Object.is(NaN, n)); // 返回索引4
8.扩展运算符
扩展运算符(spread)是三个点(...)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列。
console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5
扩展运算符后面可以放置表达式。
console.log((...[1, 2]))
注意,扩展运算符如果放在括号中,JavaScript引擎就会认为这是函数调用。如果这时不是函数调用,就会报错。
// Uncaught SyntaxError: Unexpected number
替代函数的 apply 方法
由于扩展运算符可以展开数组,所以不再需要apply方法,将数组转为函数的参数了。
// ES5 的写法
function f(x, y, z) {
// ...
}
var args = [0, 1, 2];
f.apply(null, args);
// ES6的写法
function f(x, y, z) {
// ...
}
let args = [0, 1, 2];
f(...args);
下面是扩展运算符取代apply方法的一个实际的例子,应用Math.max方法,简化求出一个数组最大元素的写法。
// ES5 的写法
Math.max.apply(null, [14, 3, 77])
// ES6 的写法
Math.max(...[14, 3, 77])
// 等同于
Math.max(14, 3, 77);
另一个例子是通过push函数,将一个数组添加到另一个数组的尾部。
// ES5的 写法
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
Array.prototype.push.apply(arr1, arr2);
// ES6 的写法
let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1.push(...arr2);
还有一个小例子
// ES5
new (Date.bind.apply(Date, [null, 2015, 1, 1]))
// ES6
new Date(...[2015, 1, 1]);
扩展运算符的应用
(1)复制数组
ES5:
const a1 = [1, 2];
const a2 = a1.concat();
a2[0] = 2;
a1 // [1, 2]
使用扩展运算符:
const a1 = [1, 2];
// 写法一
const a2 = [...a1];
// 写法二
const [...a2] = a1;
(2)合并数组
const a1 = [{ foo: 1 }];
const a2 = [{ bar: 2 }];
const a3 = a1.concat(a2);
const a4 = [...a1, ...a2];
这两种方法都是浅拷贝
a3[0] === a1[0] // true
a4[0] === a1[0] // true
(3)与解构赋值结合
// ES5
a = list[0], rest = list.slice(1)
// ES6
[a, ...rest] = list
const [first, ...rest] = [1, 2, 3, 4, 5];
first // 1
rest // [2, 3, 4, 5]
const [first, ...rest] = [];
first // undefined
rest // []
注意:如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。
const [...butLast, last] = [1, 2, 3, 4, 5];
// 报错
const [first, ...middle, last] = [1, 2, 3, 4, 5];
// 报错
(4)字符串
扩展运算符还可以将字符串转为真正的数组。
[...'hello']
// [ "h", "e", "l", "l", "o" ]
'x\uD83D\uDE80y'.length // 4
[...'x\uD83D\uDE80y'].length // 3
上面代码的第一种写法,JavaScript 会将四个字节的 Unicode 字符,
识别为 2 个字符,采用扩展运算符就没有这个问题。
因此,正确返回字符串长度的函数,可以像下面这样写。
function length(str) {
return [...str].length;
}
length('x\uD83D\uDE80y') // 3
(5)实现了 Iterator 接口的对象
任何定义了遍历器(Iterator)接口的对象都可以用扩展运算符转为真正的数组。
下面代码中,先定义了Number对象的遍历器接口,扩展运算符将5自动转成
Number实例以后,就会调用这个接口,就会返回自定义的结果。
Number.prototype[Symbol.iterator] = function*() {
let i = 0;
let num = this.valueOf();
while (i < num) {
yield i++;
}
}
console.log([...5]) // [0, 1, 2, 3, 4]
下面代码中,arrayLike是一个类似数组的对象,但是没有部署Iterator接口
,扩展运算符就会报错。
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
// TypeError: Cannot spread non-iterable object.
let arr = [...arrayLike];
(6)Map 和 Set 结构,Generator 函数
扩展运算符内部调用的是数据结构的 Iterator 接口,
因此只要具有 Iterator 接口的对象,都可以使用扩展运算符,比如 Map 结构。
let map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
let arr = [...map.keys()]; // [1, 2, 3]
Generator 函数运行后,返回一个遍历器对象,因此也可以使用扩展运算符。
const go = function*(){
yield 1;
yield 2;
yield 3;
};
[...go()] // [1, 2, 3]
上面代码中,变量go是一个 Generator 函数,执行后返回的是一个遍历器对象,
对这个遍历器对象执行扩展运算符,就会将内部遍历得到的值,转为一个数组。
9.数组的空位
Array(3) // [, , ,]
空位不是undefined,一个位置的值等于undefined,依然是有值的。空位是没有任何值,in运算符可以说明这一点。
0 in [undefined, undefined, undefined] // true
0 in [, , ,] // false
ES5 对空位的处理,已经很不一致了,大多数情况下会忽略空位。
forEach(), filter(), reduce(), every() 和some()都会跳过空位。
map()会跳过空位,但会保留这个值
join()和toString()会将空位视为undefined,而undefined和null会被处理成空字符串。
// forEach方法
[,'a'].forEach((x,i) => console.log(i)); // 1
// filter方法
['a',,'b'].filter(x => true) // ['a','b']
// every方法
[,'a'].every(x => x==='a') // true
// reduce方法
[1,,2].reduce((x,y) => x+y) // 3
// some方法
[,'a'].some(x => x !== 'a') // false
// map方法
[,'a'].map(x => 1) // [,1]
// join方法
[,'a',undefined,null].join('#') // "#a##"
// toString方法
[,'a',undefined,null].toString() // ",a,,"
ES6 则是明确将空位转为undefined。
Array.from方法会将数组的空位,转为undefined,也就是说,这个方法不会忽略空位。
Array.from(['a',,'b'])
// [ "a", undefined, "b" ]
扩展运算符(...)也会将空位转为undefined。
[...['a',,'b']]
// [ "a", undefined, "b" ]
copyWithin()会连空位一起拷贝
[,'a','b',,].copyWithin(2,0) // [,"a",,"a"]
fill()会将空位视为正常的数组位置。
new Array(3).fill('a') // ["a","a","a"]
for...of循环也会遍历空位。
let arr = [, ,];
for (let i of arr) {
console.log(1);
}
// 1
// 1
entries()、keys()、values()、find()和findIndex()会将空位处理成undefined。
// entries()
[...[,'a'].entries()] // [[0,undefined], [1,"a"]]
// keys()
[...[,'a'].keys()] // [0,1]
// values()
[...[,'a'].values()] // [undefined,"a"]
// find()
[,'a'].find(x => true) // undefined
// findIndex()
[,'a'].findIndex(x => true) // 0
注: 如果与别处相似,纯属巧合~
若有问题或者错误请在下方留言~