创建数组
创建数组是基本功
const arr = [1,2,3] // 数组字面量
const arr1 = [,,,] // 三元素空位数组(hole array)
const arr2 = new Array(4) // [,,,,]
const arr3 = new Array(4,2) // [4,2]
const arr3=new Array(1,2,3) // [1, 2, 3]
字面量创建是没有问题,但new Array() 方法,参数个数不同,导致的结果也不一样,
Array.of()基本上可以用来替代Array()或new Array(),并且不存在由于参数不同而导致的重载。它的行为非常统一。
Array.of() // []
Array.of(undefined) // [undefined]
Array.of(1) // [1]
Array.of(1, 2) // [1, 2]
总结:建议用字面量或Array.of() 方法创建
改变原数组方法
1.push()
在数组最后一位 添加一个或多个元素,并返回新数组的长度,改变原数组.(添加多个元素用逗号隔开)
var arr = [1, 2, "c"];
var rel = arr.push("A", "B");
console.log(arr); // [1, 2, "c", "A", "B"]
console.log(rel); // 5 (数组长度)
2.unshift()
在数组第一位 添加一个或多个元素,并返回新数组的长度,改变原数组。(添加多个元素用逗号隔开)
var arr = [1, 2, "c"];
var rel = arr.unshift("A", "B");
console.log(arr); // [ "A", "B",1, 2, "c"]
console.log(rel); // 5 (数组长度)
3. pop()
删除数组的最后一位,并且返回删除的数据,会改变原来的数组。(该方法不接受参数,且每次只能删除最后一个)
var arr = [1, 2, "c"];
var rel = arr.pop();
console.log(arr); // [1, 2]
console.log(rel); // c
4. shift()
删除数组的第一位数据,并且返回被删除的数据,会改变原来的数组。(该方法同pop();一样不接受参数,且每次只能删除数组第一个)
var arr = ["a","b", "c"];
var rel = arr.shift();
console.log(arr); // ['b', "c"]
console.log(rel); // a
5. reverse()
将数组的数据进行反转,并且返回反转后的数组,会改变原数组
var arr = [1, 2, 3, "a", "b", "c"];
var rel = arr.reverse();
console.log(arr); // ["c", "b", "a", 3, 2, 1] 原数组已经改变了
console.log(rel); // ["c", "b", "a", 3, 2, 1]
6. sort()
用于对数组的元素进行排序,并返回数组。默认排序顺序是根据字符串Unicode码点。
var arr1 = [10, 1, 5, 2, 3];
arr1.sort();
console.log(arr1); //结果是 [1,10,2,3,5]
结果并不是我们想要的排序结果,因为它是根据unicode编码来排序的,这也显示了其不稳定性。
语法: arr.sort(function(a,b))
参数: function可选。用来指定按某种顺序进行排列的函数。如果省略,元素按照转换为的字符串的诸个字符的Unicode位点进行排序。
具体用法:
- 如果 function(a, b) {return: a - b;} ,=> a - b > 0 那么 a 会被排列到 b 之前; (从小到大排序)
- 如果 function(a, b) {return: b - a;} ,=> b - a > 0 那么b会被排列到 a 之前; (从大到小排序)
var arr = [10, 1, 5, 2, 3];
arr.sort(function (a, b) {
return a - b;
});
console.log(arr); //[ 1,2,3,5,10]
元素为对象时(可按其中某个属性来排序):
var arr1 = [
{ name: "老八", age: "38" },
{ name: "赵日天", age: "28" },
{ name: "龙傲天", age: "48" },
];
arr1.sort(function (a, b) {
return b.age - a.age;
});
console.log(arr1);
7. splice()
向数组中添加,或从数组删除,或替换数组中的元素,然后返回被删除/替换的元素所组成的数组。可以实现数组的增删改;
语法: array.splice(index,howmany,item1,…,itemX)
参数:
例,删除arr()中第三个元素并添加 ”add1“ "add2"元素
var arr = ["a", "b", "c", 2, 3, 6];
var rel = arr.splice(2, 1, "add1", "add2");
console.log(arr); //原数组 ['a', 'b', 'add1', 'add2', 2, 3, 6]
console.log(rel); // ['c'] 返回的是删除的元素
8. fill()
用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。 (ES6新增)
语法: arr.fill(value[, start[, end]])
参数
value 用来填充数组元素的值。
start (可选) 起始索引,默认值为0。
end (可选) 终止索引,默认值为 this.length。
1.`fill`方法使用给定值,填充一个数组。
['a', 'b', 'c'].fill(7)
[7, 7, 7]
new Array(3).fill(7)
// [7, 7, 7]
上面代码表明,`fill`方法用于空数组的初始化非常方便。数组中已有的元素,会被全部抹去。
2.`fill`方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置
['a', 'b', 'c'].fill(7, 1, 2)
// ['a', 7, 'c']
上面代码表示,`fill`方法从 1 号位开始,向原数组填充 7,到 2 号位之前结束。
3. a = [1, 2, 3, 4, 5]
console.log(a.fill(1, 2)) // [1, 2, 1, 1, 1]
只有start时,从索引start开始填充到数组末位
4.a = [1, 2, 3, 4, 5]
console.log(a.fill(1, -2)) // [1, 2, 3, 1, 1]
只有start且为负数时,从倒数|start|位开始填充到数组末位
5.注意,如果填充的类型为对象,那么被赋值的是同一个内存地址的对象,而不是深拷贝对象。
let arr = new Array(3).fill({name: "Mike"});
arr[0].name = "Ben";
arr
// [{name: "Ben"}, {name: "Ben"}, {name: "Ben"}]
let arr = new Array(3).fill([]);
arr[0].push(5);
arr
// [[5], [5], [5]]
9.copyWithin()
数组实例的copyWithin()方法,在当前数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),然后返回当前数组。也就是说,使用这个方法,会修改当前数组
`
Array.prototype.copyWithin(target, start = 0, end = this.length)
` 它接受三个参数。
-
target(必需):从该位置开始替换数据。如果为负值,表示倒数。 (截取到的数据在放在什么位置)
-
start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示从末尾开始计算。( 要截取的数据从哪里开始)
-
end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示从末尾开始计算。
-
这三个参数都应该是数值,如果不是,会自动转为数值。 (要截取的数据从哪里结束)
如果只有2位参数,默认截取到数组末尾
[1, 2, 3, 4, 5].copyWithin(0, 3)
// [4, 5, 3, 4, 5]
这个从下标3截取,因为只有2位参数,那么截取的元素是4,5,再把 4,5 从下标0开始覆盖,
// 将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号位到数组结束,复制到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]
不改变自身的数组方法
1. concat()
用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组
- 如果拼接的是数组 则将数组展开,之后将数组中的每一个元素放到新数组中.
- 如果是其他类型, 直接放到新数组中
- 另外,如果不给该方法任何参数,将返回一个和原数组一样的数组(复制数组)
var arr1=[1,2]
var arr2=['a','b']
var str='c'
var obj={name:'zs',age:20}
var arr3=[99,88,[77,66]]
var res=arr1.concat(arr2,str,obj,arr3)
console.log(res);
打印的结果
2.join()
用特定的字符,将数组拼接形成字符串 (默认",")
如果省略(),数组元素用逗号分隔。默认为 ","
如果separator是空字符串(""),则所有元素之间都没有任何字符。
var list = ["a", "b", "c", "d"]; // "a-b-c-d"
var result = list.join("-"); //"a-b-c-d"
var result = list.join("/"); //"a/b/c/d"
var result = list.join(""); //"abcd"
var result = list.join(); // a,b,c,d
console.log(result);
//所有的数组元素被转换成字符串,再用一个分隔符将这些字符串连接起来。如果元素是undefined 或者null, 则会转化成空字符串。
let num = [1,null,3];
let str1 = num.join(); // 1,,3
//如果数组中的元素是数组,会将里面的数组也调用join()
let num = [[1,2],3];
let str1 = num.join('-'); // 1,2-3
// 如果数组中的元素是对象,对象会被转为[object Object]字符串
let num = [{num: 1},2,3];
let str1 = num.join('-'); // [object Object]-2-3
简单应用
// 扁平化简单的二维数组
const arr = [11, [22, 33], [44, 55], 66];
const flatArr = arr.join().split(','); // ["11", "22", "33", "44", "55", "66"]
3.slice()
slice() 方法返回一个从开始到结束(不包括结束)选择的数组的一部分浅拷贝到一个新数组对象。且原始数组不会被修改。
同concat() 方法 slice() 如果不传参数,会使用默认值,得到一个与原数组元素相同的新数组 (复制数组)
var arr1=[1,2,{age:20}]
var arr2=arr1.concat()
console.log(arr2);
arr2[0]='ss'
arr2[2].age=99
console.log(arr1,arr2);
只是浅复制,
var arr1=[1,2,{age:20}]
var arr2=arr1.slice()
console.log(arr2);
arr2[1]='ss'
arr2[2].age=99
console.log(arr1,arr2);
还是浅复制
语法: arr.slice(startIndex,endIndex)
startIndex: 如果该参数为负数,则表示从原数组中的倒数第几个元素开始提取,slice(-2)表示提取原数组中的倒数第二个元素到最后一个元素(包含最后一个元素)。
endIndex (可选):
- 如果 end 被省略,则slice 会一直提取到原数组末尾。
- 如果该参数为负数, 则它表示在原数组中的倒数第几个元素结束抽取。 slice(-2,-1)表示抽取了原数组中的倒数第二个元素到最后一个元素(不包含最后一个元素,也就是只有倒数第二个元素)。
- slice会提取原数组中索引从 begin 到 end 的所有元素(包含begin,但不包含end)。 slice(1,4) 提取原数组中的第二个元素开始直到第四个元素的所有元素 (索引为 1, 2, 3的元素)。
- 如果 end 大于数组长度,slice 也会一直提取到原数组末尾。
let arr = [1,2,3,4,5];
let arr1 = arr.slice(1,3); // arr是[1,2,3,4,5], arr1是[2,3]
let arr2 = arr.slice(-2,-1); // arr是[1,2,3,4,5], arr2是[4]
// 开始位置在结束位置后面,得到的数组是空
let arr3 = arr.slice(-2, -3); // arr是[1,2,3,4,5], arr3是[]
let arr4 = arr.slice(2, 1); // arr是[1,2,3,4,5], arr4是[]
//如果元素是个对象引用 (不是实际的对象),slice 会拷贝这个对象引用到新的数组里。两个对象引用都引用了同一个对象。如果被引用的对象发生改变,则新的和原来的数组中的这个元素也会发生改变。
let arr = [{name: 'xiaoming'}];
let arr1 = arr.slice(); // arr是[{name: xiaoming}],arr1是[{name: 'xiaoming'}]
arr1[0].name = 'xiaogang'; // arr是[{name: 'xiaogang'}],arr1是[{name: 'xiaogang'}]
// 对于字符串、数字及布尔值来说(不是 String、Number 或者 Boolean 对象),slice 会拷贝这些值到新的数组里。在别的数组里修改这些字符串或数字或是布尔值,将不会影响另一个数组。
let arr = [1,2,3];
let arr1 = arr.slice(); // arr是[1,2,3],arr1是[1,2,3]
arr1[1] = "two"; // arr是[1,2,3],arr1是[1,"two",3]
// 当然,如果向两个数组任一中添加了新元素(简单或者引用类型),则另一个不会受到影响。
var arr1=[1,2,3]
var arr2=arr1.slice()
arr2.push('ss')
arr2.unshift({name:'ss'})
console.log(arr2); //[{…}, 1, 2, 3, 'ss']
console.log(arr1); //[1,2,3]
4. toString()
返回数组的原始值(一般情况下其实就是数组自身),不改变原数组
[1,2,3].toString(); // 1,2,3
[1,[2,'c']].toString(); //1,2,c
// 以上与不使用任何参数调用join()方法返回的字符串是一样的。
//
[{a:1},1,new Date()].toString() //"[object Object],1,Sat Jul 07 2018 18:43:45 GMT+0800 (中国标准时间)"
// undefined,null 没有toString方法,默认转为空
let res= [1,2,undefined,null,[3,4],{name:'ss'},true,'yy'].toString()
console.log(res); // 1,2,,,3,4,[object Object],true,yy
//无论多少层,都可以直接转换为字符串
let res2=[ 1,2,[3,4,['ss','ss',[true,function fn(){}]]]].toString()
console.log(res2); //1,2,3,4,ss,ss,true,function fn(){}
注意: 当数组和字符串操作的时候,js 会调用这个方法将数组自动转换成字符串
[1,2,3]+'abc' //1,2,3abc 返回一个字符串表示数组中的元素
应用
// 扁平化简单的二维数组
const arr = [11, [22, 33], [44, 55], 66];
const flatArr = arr.toString().split(','); // ["11", "22", "33", "44", "55", "66"]
但只针对数据为基本类型( 数字,字符串,布尔,函数)
5. valueOf()
返回数组的原始值(一般情况下其实就是数组自身)
var list = [1, 2, 3, 4];
var rel = list.valueOf();
console.log(list); // [1, 2, 3, 4]
console.log(rel); // [1, 2, 3, 4]
6. Array.isArray() 用于确定传递的值是否是一个 Array
// 下面的函数调用都返回
Array.isArray([]); //true
Array.isArray([1]); //true
Array.isArray(new Array());//true
// 这里注意:Array.prototype 也是一个数组,一个属性值不是索引的数组。[constructor: ƒ, concat: ƒ, find: ƒ, findIndex: ƒ, pop: ƒ, …]
Array.isArray(Array.prototype);
// 下面的函数调用都返回 false
Array.isArray();
Array.isArray({});
Array.isArray(null);
Array.isArray(undefined);
Array.isArray(17);
Array.isArray('Array');
Array.isArray(true);
Array.isArray(false);
Array.isArray({ __proto__: Array.prototype });
返回值: 如果对象是 Array,则为true; 否则为false。
//判断数组的历程
// step one: 使用constructor
var a = [1];
console.log(a.constructor === Array) // true
// 但是原型的contructor属性是可以被改写的,例如在原型继承的时候,我们都是要把继承过来的prototype的constructor改写成我们当前的
var a = [1];
a.__proto__.constructor = '1';
console.log(a.constructor === Array) // false
// step two : 使用instanceof
var a = [1];
console.log(a instanceof Array) // true
constructor 为原型上的属性,指向构造函数
7. flat
用于将嵌套的数组"拉平",变成一维的数组。该方法返回一个新数组,对原数据没有影响。
默认拉平一次 如果想自定义拉平此处 需要手动传参
const arr = [1, 2, [3, 4]]
const arr1=arr.flat() // arr1= [1, 2, 3, 4]
const arr1 = [1, 2, [3, 4, [5, 6]]]
arr1.flat() // [1, 2, 3, 4, [5, 6]]
const arr2 = [1, 2, [3, 4, [5, 6]]]
arr2.flat(2) // [1, 2, 3, 4, 5, 6]
// 使用infinity,可展开任意深度的嵌套数组
const arr3 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
arr3.flat(Infinity) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// flat方法会移除数组中的空项
const arr4 = [1, 2, , 4, 5];
arr4.flat() // [1, 2, 4, 5]
8.flatMap
flat()和map()的组合版 , 先通过map()返回一个新数组,再将数组拉平( 只能拉平一次 )
语法:const result = arr.flatMap(function(item,index,array){},this)
callback:遍历数组中每个元素的回调函数,可以传入三个参数
item:当前正在处理的数组元素
index:(可选)当前正在处理的元素的下标
array:(可选)被调用的数组
this:(可选)执行回调函数时指定的this值
返回值:一个新的数组,其中每个元素都是回调函数的结果,并且结构深度depth为1
const arr = [1, 2, 3, 4, 5]
arr.flatMap(item => [item * 2]) // [1, 2, 3, 4, 5]
arr.flatMap(item => [[item * 2]]) // [[2], [4], [6], [8], [10]]
数组遍历、映射、过滤、检测、简化等方法
1.indexOf()
查询某个元素在数组中第一次出现的位置 存在该元素,返回下标,不存在 返回 -1 (可以通过返回值 变相的判断是否存在该元素)
语法:array.indexOf( searchElement,fromIndex)
searchElement 要查找的元素
fromIndex (可选)开始查找的位置。
let array = [2, 5, 9];
array.indexOf(2) // 0
array.indexOf(7) // -1
如果该索引值大于或等于数组长度,意味着不会在数组里查找,返回-1。
array.indexOf(9, 2) // 2
array.indexOf(9, 3) // -1
如果该索引值是负值,代表相对数组末尾的偏移量,即-1表示从最后一个元素开始查找,-2表示从倒数第二个元素开始查找,**注意的是,这并不改变其查找顺序,查找顺序仍然是从前向后查询数组。**
array.indexOf(2, -1) // -1
array.indexOf(5, -3) // 1
如果该索引值是负值,其绝对值大于数组长度,则整个数组都将会被查询。其默认值为0。
array.indexOf(2, -4) // 0
indexOf 使用严格相等(即 ===)比较 searchElement 和数组中的元素。而且indexOf()不能识别 `NaN`
let array1 = [1,2,NaN];
array1.indexOf(NaN) // -1
2. lastIndexOf()
lastIndexOf() 跟indexOf()查找方向相反,方法返回指定元素在数组中的最后一个的索引,如果不存在则返回 -1。从数组的后面向前查找
语法:array.lastIndexOf( searchElement,fromIndex)
searchElement 要查找的元素
fromIndex (可选)开始查找的位置
lastIndexOf 使用严格相等(即 ===)比较 searchElement 和数组中的元素。而且lastIndexOf()不能识别 NaN
let array = [2,5,9,2];
//fromIndex默认为数组的长度减 1,即整个数组都被查找
array.lastIndexOf(9) // 2
array.lastIndexOf('9') // -1 严格相等
array.lastIndexOf(7) // -1
//如果该值(fromIndex)大于或等于数组的长度,则整个数组会被查找。
array.lastIndexOf(2,4) // 3
array.lastIndexOf(2,3) // 3 从下标3的位置往前找
array.lastIndexOf(2,2) // 0 从下标2的位置往前找
//即使该值为负,数组仍然会被从后向前查找。 如果该值为负时,其绝对值大于数组长度,则方法返回 -1
array.lastIndexOf(2,-1) // 3
array.lastIndexOf(2,-2) // 0
array.lastIndexOf(2,-4) // 0
array.lastIndexOf(2,-5) // -1
3.includes()
Array.prototype.includes方法返回一个布尔值,表示某个数组是否包含给定的值,与字符串的includes方法类似。ES2016 引入了该方法
[1, 2, 3].includes(2) // true
[1, 2, 3].includes(4) // false
[1, 2, NaN].includes(NaN) // true 可以查找到NaN
该方法的第二个参数表示搜索的起始位置,默认为0。
如果第二个参数为负数,则表示倒数的位置,如果这时它大于数组长度(比如第二个参数为-4,但数组长度为3),则会重置为从0开始。
[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true
没有该方法之前,我们通常使用数组的indexOf方法,检查是否包含某个值。
if (arr.indexOf(el) !== -1) {
// ...
}
indexOf方法有两个缺点,一是不够语义化,它的含义是找到参数值的第一个出现位置,所以要去比较是否不等于-1,表达起来不够直观。二是,它内部使用严格相等运算符(===)进行判断,这会导致对NaN的误判。
[NaN].indexOf(NaN)
// -1
includes使用的是不一样的判断算法,就没有这个问题。
[NaN].includes(NaN)
// true
下面代码用来检查当前环境是否支持该方法,如果不支持,部署一个简易的替代版本
const contains = (() =>
Array.prototype.includes
? (arr, value) => arr.includes(value)
: (arr, value) => arr.some(el => el === value)
)();
contains(['foo', 'bar'], 'baz'); // => false
4. find(),findIndex(),findLast(),findLastIndex()
数组实例的find()方法,用于找出第一个符合条件的数组成员。它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,则返回undefined
语法: Array.find(callback(element,index,array){},thisArg)
callback 在数组每一项上执行的函数,接收 3 个参数:
element当前遍历到的元素。index当前遍历到的索引。array数组本身。
thisArg 可选,指定 callback 的 this 参数
[1, 4, -5, 10].find((n) => n < 0)
// -5
[1, 5, 10, 15].find(function(value, index, arr) {
return value > 16;
}) // undefined
数组实例的findIndex()方法的用法与find()方法非常类似,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1
[1, 5, 10, 15].findIndex(function(value, index, arr) {
return value > 9;
}) // 2
注意:1. 这两个方法对数组中的每一项元素执行一次 callback 函数,直至有一个 callback 返回 true。在稀疏数组中,即使对于数组中不存在的条目的索引也会调用回调函数。 这意味着对于稀疏数组来说,该方法的效率要低于那些只遍历有值的索引的方法。
2.这两个方法都不会修改所调用的数组
这两个方法都可以接受第二个参数,用来绑定回调函数的this对象。
function f(v){
return v > this.age;
}
let person = {name: 'John', age: 20};
[10, 12, 26, 15].find(f, person); // 26
上面的代码中,`find()`函数接收了第二个参数`person`对象,回调函数中的`this`对象指向`person`对象。
另外,这两个方法都可以发现NaN,弥补了数组的indexOf()方法的不足。
[NaN].indexOf(NaN)
// -1
[NaN].findIndex(y => Object.is(NaN, y))
// 0
上面代码中,`indexOf()`方法无法识别数组的`NaN`成员,但是`findIndex()`方法可以借助`Object.is()`方法做到。
find()和findIndex()都是从数组的0号位,依次向后检查。ES2022 新增了两个方法findLast()和findLastIndex(),从数组的最后一个成员开始,依次向前检查,其他都保持不变。
const array = [
{ value: 1 },
{ value: 2 },
{ value: 3 },
{ value: 4 }
];
array.findLast(n => n.value % 2 === 1); // { value: 3 }
array.findLastIndex(n => n.value % 2 === 1); // 2
上面示例中,`findLast()`和`findLastIndex()`从数组结尾开始,寻找第一个`value`属性为奇数的成员。结果,该成员是`{ value: 3 }`,位置是2号位。
知识点 不要用 find() 代替 some(),通常混用是这种场景,find 返回第一个符合条件的值,直接拿这个值做 if 判断是否存在,但是这个符合条件的值也有可能恰好为 0。 find 是找到数组中的值后对其进一步处理,一般用于对象数组的情况;some 才是检查存在性;两者不可混用。
什么是稀疏数组
稀疏数组就是包含从0开始的不连续索引的数组。通常数组的length属性值代表数组中元素的个数。如果数组是稀疏的,length属性值大于元素的个数
let a1 = [,,]; // 数组直接量,该数组是[empty × 2]
0 in a1 // false: a1在索引0处没有元素
let a2 = new Array(3); //[empty × 3],该数组根本没有元素
0 in a2 // false: a2在索引0处没有元素
let a3 = [undefined];
0 in a3 // true: a3在索引0处有一个值为undefined的元素
let a4 = [,undefined];
0 in a4 // fasle: a4在索引0处没有元素
1 in a4 // true: a4在索引1处有一个值为undefined的元素
console.log(a4[0],a4[1]) // undefined undefined,可见数组访问返回undefined,可能是稀疏数组,也可能是数组元素为undefined
in
如果指定的属性在指定的对象或其原型链中,则 in 运算符返回 true。
语法:prop in object
prop
一个字符串类型或者 symbol 类型的属性名或者数组索引(非 symbol 类型将会强制转为字符串)。
objectName
检查它(或其原型链)是否包含具有指定名称的属性的对象。
var trees = new Array("redwood", "bay", "cedar", "oak", "maple");
0 in trees // 返回true
3 in trees // 返回true
6 in trees // 返回false
"bay" in trees // 返回false (必须使用索引号,而不是数组元素的值)
"length" in trees // 返回true (length是一个数组属性)
Symbol.iterator in trees // 返回true (数组可迭代,只在ES2015+上有效)
// 内置对象
"PI" in Math // 返回true
// 自定义对象
var mycar = {make: "Honda", model: "Accord", year: 1998};
"make" in mycar // 返回true
"model" in mycar // 返回true
in 右操作数必须是一个对象值。例如,你可以指定使用 String 构造函数创建的字符串,但不能指定字符串文字。
var color1 = new String("green");
"length" in color1 // 返回true
var color2 = "coral";
"length" in color2 // 报错(color2不是对象)
对被删除或值为 undefined 的属性使用 in
如果你使用 delete 运算符删除了一个属性,则 in 运算符对所删除属性返回 false。
var mycar = {make: "Honda", model: "Accord", year: 1998};
delete mycar.make;
"make" in mycar; // 返回false
var trees = new Array("redwood", "bay", "cedar", "oak", "maple");
delete trees[3];
3 in trees; // 返回false
如果你只是将一个属性的值赋值为 undefined,而没有删除它,则 in 运算仍然会返回 true。
var mycar = {make: "Honda", model: "Accord", year: 1998};
mycar.make = undefined;
"make" in mycar; // 返回true
var trees = new Array("redwood", "bay", "cedar", "oak", "maple");
trees[3] = undefined;
3 in trees; // 返回true
继承属性
如果一个属性是从原型链上继承来的,in 运算符也会返回 true。
"toString" in {}; // 返回true
5. Array.from()
Array.from()方法用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)
下面是一个类似数组的对象,Array.from()将它转为真正的数组。
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
// ES5 的写法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']
// ES6 的写法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
实际应用中,常见的类似数组的对象是 DOM 操作返回的 NodeList 集合,以及函数内部的arguments对象。Array.from()都可以将它们转为真正的数组。
// NodeList 对象
let ps = document.querySelectorAll('p');
Array.from(ps).filter(p => {
return p.textContent.length > 100;
});
// arguments 对象
function foo() {
var args = Array.from(arguments);
// ...
}
上面代码中,`querySelectorAll()`方法返回的是一个类似数组的对象,可以将这个对象转为真正的数组,再使用`filter()`方法。
只要是部署了 Iterator 接口的数据结构,Array.from()都能将其转为数组。
Array.from('hello')
// ['h', 'e', 'l', 'l', 'o']
let namesSet = new Set(['a', 'b'])
Array.from(namesSet) // ['a', 'b']
上面代码中,字符串和 Set 结构都具有 Iterator 接口,因此可以被`Array.from()`转为真正的数组。
如果参数是一个真正的数组,Array.from()会返回一个一模一样的新数组。
Array.from([1, 2, 3])
// [1, 2, 3]
值得提醒的是,扩展运算符(...)也可以将某些数据结构转为数组。
// arguments对象
function foo() {
const args = [...arguments];
}
// NodeList对象
[...document.querySelectorAll('div')]
扩展运算符背后调用的是遍历器接口(Symbol.iterator),如果一个对象没有部署这个接口,就无法转换。Array.from()方法还支持类似数组的对象。所谓类似数组的对象,本质特征只有一点,即必须有length属性。因此,任何有length属性的对象,都可以通过Array.from()方法转为数组,而此时扩展运算符就无法转换。
Array.from({ length: 3 });
// [ undefined, undefined, undefined ]
上面代码中,`Array.from()`返回了一个具有三个成员的数组,每个位置的值都是`undefined`。扩展运算符转换不了这个对象。
对于还没有部署该方法的浏览器,可以用Array.prototype.slice()方法替代。
const toArray = (() =>
Array.from ? Array.from : obj => [].slice.call(obj)
)();
Array.from()还可以接受一个函数作为第二个参数,作用类似于数组的map()方法,用来对每个元素进行处理,将处理后的值放入返回的数组。
Array.from(arrayLike, x => x * x);
// 等同于
Array.from(arrayLike).map(x => x * x);
Array.from([1, 2, 3], (x) => x * x)
// [1, 4, 9]
6. forEach()
从头到尾遍历数组,为每个元素调用指定的函数。
参数:
callback 为数组中每个元素执行的函数,该函数接收三个参数:
currentValue(当前值)数组中正在处理的当前元素。index(索引)数组中正在处理的当前元素的索引。arrayforEach()方法正在操作的数组。thisArg(可选) 当执行回调函数时用作this的值(参考对象)。默认值为undefined
forEach无法中途退出循环,只能用return退出本次回调,进行下一次回调
如果要提前终止,可以把forEach方法放在try块中,并能抛出一个异常,但这种方法是不推荐的。
它与之后会说到的几个方法不同,总是返回 undefined值,即使你return了一个值。
let numberArr = [1,2,,3];
numberArr.forEach(function (value,index,array) {
console.log(value,index,array)
})
//打印信息如下,可见空元素是不会遍历的
//1 0 [1, 2, empty, 3]
//2 1 [1, 2, empty, 3]
//3 3 [1, 2, empty, 3]
let nullArr = [1,2,null,3];
nullArr.forEach(function (value,index,array) {
console.log(value,index,array)
})
//打印信息如下,null是会遍历的
//1 0 (4) [1, 2, null, 3]
//2 1 (4) [1, 2, null, 3]
//null 2 (4) [1, 2, null, 3]
//3 3 (4) [1, 2, null, 3]
let numberArr = [1,2,3];
numberArr.forEach(function (value,index,array) {
if(index === 0) {
delete numberArr[2]; //删除第三项
//或者numberArr.pop()
}
console.log(value,index,array)
})
//打印信息如下:
// 1 0 (3) [1, 2, empty]
// 2 1 (3) [1, 2, empty]
let numberArr1 = [1,2,3,4];
numberArr1.forEach(function (value,index,array) {
if(index === 1) {
numberArr1.shift() //遍历到第二项的时候,删除第一项
}
console.log(value,index,array)
})
// 打印信息如下,遍历到第二项的时候,删除第一项,会跳过第三项
// 1 0 (4) [1, 2, 3, 4]
// 2 1 (3) [2, 3, 4]
// 4 2 (3) [2, 3, 4]
// 4、使用thisArg参数 和 箭头函数使用thisArg
let arr = [1,2,3];
let obj = {arr: 'thisArg'}
arr.forEach(function () {
console.log(this.arr)
},obj)
// 打印三次 'thisArg'
let arr = [1,2,3];
let obj = {arr: 'thisArg'}
arr.forEach(() => {
console.log(this.arr)
},obj)
// 打印三次 undefined
// 5、forEach无法中途退出循环,只能用return退出本次回调,进行下一次回调
let arr = [1,2,3];
let result = arr.forEach((value) => {
if(value == 2) {
return value;
}
console.log(value)
})
console.log(result) // undefined ,即使中间return vlaue,也还是undefined
//打印value的值如下,说明return 并不能终止循环
// 1
// 3
7.map()
map() 方法创建一个新数组,其结果是该数组中的每个元素都调用一个callback函数后返回的结果。
语法: let new_array = arr.map(function(currentValue, index, arr), thisArg)
function(必须): 数组中每个元素需要调用的函数。
1. currentValue(必须),数组当前元素的值
2. index(可选), 当前元素的索引值
3. arr(可选),数组对象本身
let a = ['1','2','3','4'];
let result = a.map(function (value, index, array) {
return value + '新数组的新元素'
});
console.log(result, a);
// ["1新数组的新元素","2新数组的新元素","3新数组的新元素","4新数组的新元素"] ["1","2","3","4"]
注意: map() 返回的是新数组,它不修改调用的数组。如果是稀疏数组,返回的也是相同方式的稀疏数组:它具有相同的长度,相同索引的缺失元素(因为空值不会调用函数)
let number = [1,2,,3];
let doubles = number.map(function (value) {
return value * 2;
})
console.log(number ,doubles);
(4) [1, 2, …, 3] (4) [2, 4, …, 6]
知识点 不要用 map 代替 forEach,map 会创建一个新的数组,占用内存。如果你不用 map 的返回值,那你就应当使用 forEach
8. every()
定义: 方法用于检测数组所有元素是否都符合函数定义的条件
语法: array.every(function(currentValue, index, arr), this)
方法返回值规则:
- 如果数组中检测到有一个元素不满足,则整个表达式返回 false,且剩余的元素不会再进行检测
- 如果所有元素都满足条件,则返回 true
- every 不会改变原数组
- 空数组上调用every方法,返回 true,因为空数组没有元素,所以空数组中所有元素都符合给定的条件
function isBigEnough(element, index, array) {
return element >= 10; // 判断数组中的所有元素是否都大于10
}
let result = [12, 5, 8, 130, 44].every(isBigEnough); // false
let result = [12, 54, 18, 130, 44].every(isBigEnough); // true
// 接受箭头函数写法
[12, 5, 8, 130, 44].every(x => x >= 10); // false
[12, 54, 18, 130, 44].every(x => x >= 10); // true
let arr = [12,34,5,23,44];
let num = 0;
let result = arr.every(function (element, index, array) {
num++;
return element > 10;
})
console.log(result,num) // 打印 false 3
// 可见发现5这个小于10的元素后,遍历立即终止,num为3
let result = [].every(function (element, index, array) {
return element > 10;
})
console.log(result) // 打印 true
9.some()
数组中的是否有满足判断条件的元素
定义:数组中的是否有满足判断条件的元素
语法:array.some(function(currentValue, index, arr), this)
方法返回值规则:
- 如果有一个元素满足条件,则表达式返回true, 剩余的元素不会再执行检测。
- 如果没有满足条件的元素,则返回false。
- some 被调用时不会改变数组
- 空数组调用some,返回false
- callback 只会在那些”有值“的索引上被调用,不会在那些被删除或从来未被赋值的索引上调用。
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
let result = [].some(function (element, index, array) { return element > 10; })
console.log(result) // 打印 false
10. filter()
过滤原始数组,返回新数组
语法:
let new_array = arr.filter(function(currentValue, index, arr), this)
- filter 不会改变原数组,它返回过滤后的新数组。
- callback 只会在已经赋值的索引上被调用,对于那些已经被删除或者从未被赋值的索引不会被调用。也就是说filter()会跳过稀疏数组中缺少的元素,它的返回数组总是稠密的,可以用这个方法压缩稀疏数组的空缺。
let number = [1,2,3,4,5,6];
let small = number.filter((value) => {
return value < 4;
})
console.log(number,small)
// 打印 [1, 2, 3, 4, 5, 6] [1, 2, 3]
//压缩稀疏数组的空缺
let arr = [1,2,3,,5];
let arr1 = arr.filter(() => true);
console.log(arr,arr1)
// 打印 [1, 2, 3, empty, 5] [1, 2, 3, 5]
11.reduce
定义:reduce() 方法对累加器和数组中的每个元素(从左到右)应用一个函数,最终合并为一个值 语法:
array.reduce(function(total, currentValue, Index, arr), initialValue)
function(必须): 数组中每个元素需要调用的函数 // 回调函数的参数
- total(必须),初始值, 或者上一次调用回调返回的值
- currentValue(必须),数组当前元素的值
- index(可选), 当前元素的索引值
- arr(可选),数组对象本身
initialValue(可选): 指定第一次回调 的第一个参数
回调第一次执行时:
- 如果 initialValue 在调用 reduce 时被提供,那么第一个 total 将等于 initialValue,此时 currentValue 等于数组中的第一个值;
- 如果 initialValue 未被提供,那么 total 等于数组中的第一个值,currentValue 等于数组中的第二个值。此时如果数组为空,那么将抛出 TypeError。
- 如果数组仅有一个元素,并且没有提供 initialValue,或提供了 initialValue 但数组为空,那么回调不会被执行,数组的唯一值将被返回。
// 数组求和
let sum = [0, 1, 2, 3].reduce(function (a, b) {
return a + b;
}, 0);
// 6
// 将二维数组转化为一维 将数组元素展开
let flattened = [[0, 1], [2, 3], [4, 5]].reduce(
(a, b) => a.concat(b),
[]
);
// [0, 1, 2, 3, 4, 5]
reduceRight 从右至左累加
这个方法除了与reduce执行方向相反外,其他完全与其一致,请参考上述 reduce 方法介绍。
12 .ES6 keys()&values()&entries() 遍历键名、遍历键值、遍历键名+键值
语法:
array.keys()
array.values()
array.entries()
ES6 提供三个新的方法——entries(),keys()和values()——用于遍历数组。它们都返回一个遍历器对象(详见《Iterator》一章),可以用for...of循环进行遍历,唯一的区别是keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。
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 (let elem of ['a', 'b'].values()) {
if( elem=='b'){
break
}
console.log(elem);
}
// 'a'
如果不使用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']
entries()浏览器兼容性(MDN):Chrome 38, Firefox 28,Opera 25,Safari 7.
keys()浏览器兼容性(MDN):Chrome 38, Firefox 28,Opera 25,Safari 8,
注意:目前只有Safari 9支持,,其他浏览器未实现,babel转码器也还未实现