数组

ECMAScript数组也是一组有序的数据

这将意味着,频繁操作数组(尤其是删除数组内个别项)将很耗费性能。
因为每一个元素是一个坑(内存空间),中间有了新坑,后边的元素就要挪动。

创建数组的几种方式

1、Array构造函数(new操作符可以省略)

使用这种方式,在知道数组的固定长度情况下最好传入Array函数的第一个参数(一个数字),用于创建初始时有固定长度的数组,这也是一种性能优化的小点。

  1. const array1 = new Array(1) // 得到固定长度的数组 [empty],各项为空

若Array函数的参数不止一个元素、或者不是一个数字,则将以这些数字创建一个新数组

  1. const array2 = new Array('a') // 不是一个数字,得到['a']
  2. const array3 = new Array(1, '2', 'a') // 不止一个元素,得到[1, '2', 'a']

2、数组字面量(array literal)表示法

  • 字面量,经常出现的词语。类似于“使用字面意思”的感觉。比如要创建一个数组,字面上的做法就是[1]
  • 注意 与对象一样,在使用数组字面量表示法创建数组不会调用Array构造函数。
    1. let names = []; // 创建一个空数组
    2. let values = [1,2,]; // 在数组最后一个值后面加逗号的效果,创建一个包含 2 个元素的数组

    3、es6新增 from() 方法

    用于将 类数组结构转换为数组实例
    第一个参数是一个类数组对象/任何可迭代对象,
    第二个可选的映射函数参数。这个函数可以直接增强新数组的值,而无须像 调用 Array.from().map()那样先创建一个中间数组。 第三个可选参数,用于指定映射函 数中 this 的值。但这个重写的 this 值在箭头函数中不适用。

类数组对象即任何可迭代的结构,或者有一个 length 属性 和可索引元素的结构。

  1. // 字符串会被拆分为单字符数组
  2. Array.from("123") // ["1", "2", "3"]
  3. // 类似下面写法的结果:
  4. '123'.split("") // ["1", "2", "3"]
  5. // 配合set去重
  6. Array.from(new Set([1, 2, 3, 1, 3)) // [1, 2, 3]
  7. // 伪数组转数组
  8. Array.from(arguments);
  9. // 各个参数的用途
  10. Array.from([1, 2, 3, 4], function(x) {
  11. return x**this.exponent // (**: 指数操作符,用于幂运算。在这里是求平方)
  12. }, {exponent: 2}) // [1, 4, 9, 16]

from的多种用法:

image.png
image.png

from用途总结:

  1. 将字符串转为数组
  2. 将类数组(或任何可迭代对象)转为数组【这就是from方法分类到数组创建方式这里的底气吧】
  3. 浅拷贝
  4. 去重(结合Set)

    4、es6新增 of() 方法

    用于将一组参数转换为数组实例

  1. Array.of(undefined)
  2. Array.of(1, 2, 3, 4)
  3. //
  4. function aaa (a,b,c) {
  5. console.log(Array.of(a,b,c)) // [1,2,3]
  6. }
  7. aaa(1,2,3)

注意兼容性:

  1. if (!Array.of) {
  2. Array.of = function() {
  3. return Array.prototype.slice.call(arguments);
  4. };
  5. }

伪数组转为数组的几种方法

  1. Array.from(arguments)
  2. Array.of(arg1, arg2, arg3) 【牵强】
  3. Array.prototype.slice.call(arguments)
  4. …arguments

    数组空位

    使用数组字面量初始化数组时,可以使用一串逗号来创建空位(hole)

使用Array构造函数创建时传入第一个参数为数字,即可创建对应个数的空位

  1. const arr1 = [,,,] // [empty × 3]
  2. const arr2 = new Array(3) // [empty × 3]
  3. const arr3 = Array.from([,,,]) // [undefined, undefined, undefined]

ES6 规范重新定义了该如何处理这些空位 ES6 新增的方法和迭代器与早期 ECMAScript 版本中存在的方法行为不同。ES6 新增方法普遍将这些空位当成存在的元素,只不过值为 undefined:

image.png

ES6 之前的方法则会忽略这个空位,但具体的行为也会因方法而异: image.png

注意:由于行为不一致和存在性能隐患,因此实践中要避免使用数组空位。如果确实需要空位,则可以显式地用 undefined 值代替。

数组索引

如果把一个值设置给超过数组最大索引的索引,则数组长度会自动扩 展到该索引值加 1

  1. let arr1 = [1]
  2. console.log(arr1.length) // 1
  3. console.log(arr1) // [1]
  4. arr1[3] = 'a'
  5. console.log(arr1) // [1, empty, empty, 'a']

数组 length 属性不是只读的。通过修改 length 属性,可以从数组末尾删除或添加元素。 image.png

如果将 length 设置为大于数组元素数的值,则新添加的元素都将以 undefined 填充

image.png

  1. arr1.length = 5
  2. console.log(arr1) // [1, empty, empty, 'a', empty]

empty表示在数组中不存在,因 此访问其值会返回特殊值 undefined。

  1. console.log(arr[2]) // undefined

数组中最后一个元素的索引始终是 length - 1,因此下一个新增槽位的索引就是 length。每次 在数组最后一个元素后面新增一项,数组的 length 属性都会自动更新,以反映变化。

  1. arr1[arr1.length] = 'last'
  2. console.log(arr1) // [1, empty × 2, "a", empty, "last"]

注意: 数组最多可以包含 4 294 967 295(40亿多) 个元素,这对于大多数编程任务应该足够了。如果 尝试添加更多项,则会导致抛出错误。以这个最大值作为初始值创建数组,可能导致脚本 运行时间过长的错误。

判断数组

判断一个变量/对象是不是数组

instanceof的问题:

使用instanceof判断变量类型的前提是,假设当前网页只有一个全局执行上下文。

如果网页里有多个框架,则可能涉及两个不同的全局执行上下文,因此就会有两个不同版本的 Array 构造函数。如果要把数组从一个框架传给另一个框架,则这个数组的构造函数将有别于在第二个框架内本地创建的数组。

image.png

其他方法的问题

  • Object.prototype.toString.call(o) === “[object Array]”
  • o.constructor === Array

    所谓的鸭子类型

    Are there any other methods of determining whether a value is an array that might work around this? o.constructor === Array is one, with the same problem as an instanceof check. Another option relies on so-called “duck typing“, where if a value quackslooks like a duckan array then it is a duckan array. Along the constructor-checking lines, you could check for other array methods like push or concat, or perhaps for a length property, but these properties could exist in the same fashion on a non-array object. If you’re willing to have false positives and negatives (assuming unconstrained input) that might be acceptable, but of course that won’t always be the case. One test in this style is Object.prototype.toString.call(o) === “[object Array]”, but that relies on Object.prototype.toString and Function.prototype.call not being changed (probably a good assumption but still fragile). It’s also a bit more of an obvious hack than any of the other ideas.

大体意思是,o.constructor === Array这句代码也能检测,这种写法就叫做鸭子类型,看上去像数组、就把他当做数组。
这种属于利用构造函数的特征进行检查的方法,除了检查构造函数的名字外、还可以通过检查原型链上的一些原生方法,比如push、concat等数组独有的方法,或者检查数组的length属性来判断。但是这种检查不太靠谱、不保险的,不是Array(non-array)的对象也有可能有。
而Object.prototype.toString.call(o) === “[object Array]”这种方法虽然觉得万能,但是也依赖Object.prototype.toStringFunction.prototype.call 这俩方法不被修改。这种判断方法写的代码也是比较脆弱的。

Array.isArray

可以说,Array.isArray() 的出现就是为了弥补instanceof等判断数组不足的问题。

为解决这个问题,ECMAScript 提供了 Array.isArray()方法。这个方法的目的就是确定一个值是否为数组,而不用管它是在哪个全局执行上下文中创建的。

For these reasons, ECMAScript 5 defines a method, Array.isArray, to completely address the problem. If the first argument provided is an array object created in any window at all, it returns true; if no arguments were provided or if the first argument wasn’t an array object, it returns false.

完美解决其他方法的问题,测试代码如下:

  1. function test(fun, expect) { if (fun() !== expect) console.log("FAIL: " + fun); }
  2. test(function() { return Array.isArray([]); }, true);
  3. test(function() { return Array.isArray(new Array); }, true);
  4. test(function() { return Array.isArray(); }, false);
  5. test(function() { return Array.isArray({ constructor: Array }); }, false);
  6. test(function() { return Array.isArray({ push: Array.prototype.push, concat: Array.prototype.concat }); }, false);
  7. test(function() { return Array.isArray(17); }, false);
  8. Object.prototype.toString = function() { return "[object Array]"; };
  9. test(function() { return Array.isArray({}); }, false);
  10. test(function() { return Array.isArray({ __proto__: Array.prototype }); }, false);
  11. test(function() { return Array.isArray({ length: 0 }); }, false);
  12. var w = window.open("about:blank");
  13. w.onload = function()
  14. {
  15. test(function() { return Array.isArray(arguments); }, false);
  16. test(function() { return Array.isArray(new w.Array); }, true);
  17. };

检测数组的几种方式

1、value instanceof Array === true【限制条件:当前网页只有一个全局上下文、原型链没被改过】
2、Object.prototype.toString.call(value) === ‘[object Array]’ 【限制条件:保证call和toString方法没被改过】
3、value.proto.constructor.name === ‘Array’【限制条件:原型链没被改过】
3.1、相同意思的写法:value.constructor.name === ‘Array’
4、Array.isArray(value) === true 【完美】

扩展阅读、参考文章:严格判定JavaScript对象是否为数组

迭代器方法_用于检索数组内容的迭代器方法

keys、values、entries,因为这些方法都返回迭代器,所以可以将它们的内容通过Array.from()直接转换为数组实例

  1. let arr = ["foo", "bar", "baz", "qux"];
  2. console.log(arr.keys()) // Array Iterator {}
  3. console.log(arr.values()) // Array Iterator {}
  4. console.log(arr.entries()) // Array Iterator {}

keys()迭代器 返回数组索引

  1. Array.from(a.keys()) // [0, 1, 2, 3]

values()迭代器 返回数组元素

  1. Array.from(a.values()) // ["foo", "bar", "baz", "qux"]

entries()迭代器 返回索引/值对儿

  1. Array.from(a.entries()) // [[0, "foo"], [1, "bar"], [2, "baz"], [3, "qux"]]

使用 ES6 的解构可以非常容易地在循环中拆分键/值对:

  1. for (const [idx, element] of a.entries()) {
  2. console.log(idx, element);
  3. }

复制和填充方法

copyWithin 和 fill

copyWithin() 批量复制(浅复制)

  1. copyWithin(targetIndex[, startIndex[, endIndex]])
  • 【浅复制】数组start到end的内容(包含开始索引start,不包含结束索引end),从数组的位置索引target开始替换掉对应个数的内容。整体流程是,先复制、再插入替换。不用担心复制期间数据重写。
  • 使用这个方法不会改变原数组大小、但是修改原数组。也就是原数组会变成替换过内容的新数组、但是能保证数组的长度不变。
  • 如果是负数,target 将从末尾开始计算。如果大于等于 arr.length,将会不发生拷贝。
  • 如果是负数,start 将从末尾开始计算。如果 start 被忽略,copyWithin 将会从0开始复制。
  • 如果是负数,end 将从末尾开始计算。如果 end 被忽略,copyWithin 方法将会一直复制至数组结尾(默认为 arr.length) ```javascript const array1 = [‘a’, ‘b’, ‘c’, ‘d’, ‘e’];

// copy to index 0 the element at index 3 console.log(array1.copyWithin(0, 3, 4)); // expected output: Array [“d”, “b”, “c”, “d”, “e”]

// copy to index 1 all elements from index 3 to the end console.log(array1.copyWithin(1, 3)); // expected output: Array [“d”, “d”, “e”, “d”, “e”]

  1. - 如果end超界呢?
  2. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/269022/1624955787993-869b41d1-1288-4a04-8d9d-e5e05ba0f14c.png#clientId=uff99ca29-1baa-4&from=paste&height=383&id=u38d7f507&margin=%5Bobject%20Object%5D&name=image.png&originHeight=766&originWidth=976&originalType=binary&ratio=2&size=107852&status=done&style=none&taskId=u5efbca9c-6fad-47f9-a1cb-4be7917e5e0&width=488)
  3. <a name="h9YGZ"></a>
  4. ### 仿写copyWithin方法(polyfill)
  5. [https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/copyWithin#polyfill](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/copyWithin#polyfill)
  6. <a name="JT4Gr"></a>
  7. ## fill() 填充数组
  8. 用法类似copyWithin,只不过上边那个是「替换复制的值」,fill是「替换指定值」
  9. > 使用 fill()方法可以向一个已有的数组中插入全部或部分相同的值。开始索引用于指定开始填充 的位置,它是可选的。如果不提供结束索引,则一直填充到数组末尾。负值索引从数组末尾开始计算。 也可以将负索引想象成数组长度加上它得到的一个正索引:
  10. ```javascript
  11. // 用7填充索引从1到2的内容(不包含end,所以需要3-1)
  12. zeroes = [0,0,0,0,0]
  13. zeroes.fill(7, 1, 3);
  14. console.log(zeroes); // [0, 7, 7, 0, 0]
  15. zeroes.fill(0); // 重置 [0,0,0,0,0]
  16. // 用8填充索引大于等于1且小于4的元素
  17. // (-4 + zeroes.length = 1)
  18. // (-1 + zeroes.length = 4)
  19. zeroes.fill(8, -4, -1);
  20. console.log(zeroes); // [0, 8, 8, 8, 0];

如果索引超界呢?
image.png

创建一个数组,包含10000项“石头姐”:

  1. new Array(10000).fill('石头姐')

转换方法

toLocaleString()、toString()和 valueOf()

valueOf()

返回的还是数组本身
直接读取数组,没有隐式的经过valueOf
用alert包裹valueOf的返回结果,是一个以逗号分隔的字符串,就像调用了join(‘,’)。这是因为alert期待字符串

  1. let array1 = [1, 2]
  2. console.log(array1.valueOf()) // [1, 2]
  3. // 重写valueOf
  4. Array.prototype.valueOf = function () {
  5. return this.map((item) => {
  6. return item + '石头姐'
  7. })
  8. }
  9. console.log(array1.valueOf()) // [ '1石头姐', '2石头姐' ]
  10. alert(array1.valueOf()) // '1石头姐,2石头姐'

toString()

返回由数组中每个值的等效字符串拼接而成的一个逗号分隔的字符串。也就是说,对数组的每个值都会调用其 toString()方法,以得到最终的字符串。各字符串以逗号分隔

  1. let array1 = [1, 2]
  2. console.log(array1.toString()) // 1,2
  3. alert(array1.valueOf()) // 1,2
  4. alert(array1) // 1,2

toLocaleString()

对数组的每个值都会调用其 toLocaleString()方法,以得到最终的字符串。各字符串以逗号分隔

  1. let person1 = {
  2. toLocaleString() {
  3. return "Nikolaos";
  4. },
  5. toString() {
  6. return "Nicholas";
  7. }
  8. };
  9. let person2 = {
  10. toLocaleString() {
  11. return "Grigorios";
  12. },
  13. toString() {
  14. return "Greg";
  15. }
  16. };
  17. let people = [person1, person2];
  18. alert(people); // Nicholas,Greg
  19. alert(people.toString()); // Nicholas,Greg
  20. alert(people.toLocaleString()); // Nikolaos,Grigorios

join

如果数组中某一项是null或undefined,则在join()、toLocaleString()、 toString()和 valueOf()返回的结果中会以空字符串表示。

  1. [1,2,null, 3, undefined, 4, ''].join(',') // "1,2,,3,,4,"

栈方法

数组使用 push、pop 模拟栈
栈:一种数据结构,后进先出(LIFO,last-in-first-out)。限制插入和删除项。推入或弹出数据都发生在栈顶
栈顶插入:push
栈顶获取:pop
image.png

push

数组最后边新增任意个值。返回最新数组的长度,改变原数组及其长度
image.png

pop

删除数组最后一项。返回被删除的那一项,改变原数组及其长度
image.png

队列方法

数组使用 shift、push 模拟队列
队列:一种数据结构,先进先出(FIFO,First-In-First-Out)。在队列末尾添加数据、在列表开头获取数据。
队尾插入:posh
队头获取:shift
image.png

shift

删除数组第一项。返回被删除的那一项,改变原数组及其长度
image.png

unshift

数组最前边新增任意个值。返回最新数组的长度,改变原数组及其长度
配合pop,可在反方向上模拟队列(数组开头添加数据,数组末尾取数据)
image.png

排序方法

reverse、sort

reverse()

翻转数组,改变原数组。返回颠倒后的数组。
直观但不灵活

sort()

排序数组,改变原数组。返回排序后的数组。
默认情况下,sort()会按照升序重新排列数组元素,即最小的值在前面,最大的值在后面。为此, sort()会在每一项上调用 String()转型函数,然后比较字符串(ASCII码)来决定顺序。即使数组的元素都是数值,也会先把数字转换为字符串再比较、排序。

无参数默认:

image.png
比较每一项的ASCII码:
image.png

ASCII码(十进制值)
1 49
A 65
B 66
a 97

即使数组的元素都是数值,也会先把数组转换为字符串再比较、排序。
所以不注意的话下边直接用sort排序结果会不是你想的

  1. let values = [10, 0, 5, 1, 15];
  2. // 你以为结果是0、1、5、10、15 ?!!
  3. values.sort();
  4. // 错了!调用 sort()会按照这些数值的字符串形式重新排序:
  5. console.log(values); // [0,1,10,15,5]

比较函数:

比较函数接收两个参数

正序

  • 如果第一个参数应该排在第二个参数前面,就返回负值
  • 如果两个参数相等,就返回 0
  • 如果第一个参数应该排在第二个参数后面,就返回正值

    1. function compare(value1, value2) {
    2. if (value1 < value2) {
    3. return -1;
    4. } else if (value1 > value2) {
    5. return 1;
    6. } else {
    7. return 0;
    8. }
    9. }

    倒序

  • 如果第一个参数应该排在第二个参数前面,就返回正值

  • 如果两个参数相等,就返回 0
  • 如果第一个参数应该排在第二个参数后面,就返回负值
    1. function compare(value1, value2) {
    2. if (value1 < value2) {
    3. return 1;
    4. } else if (value1 > value2) {
    5. return -1;
    6. } else {
    7. return 0;
    8. }
    9. }

    简写

    ```javascript values.sort((a, b) => a < b ? 1 : a > b ? -1 : 0);

values.sort((a, b) => a - b) // 正序 values.sort((a, b) => b - a) // 倒序

  1. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/269022/1625024750529-a860b3d8-68d4-420e-9dc1-b0d31b8d2d28.png#clientId=ub22e23e2-868d-4&from=paste&height=93&id=uc8af4eba&margin=%5Bobject%20Object%5D&name=image.png&originHeight=186&originWidth=484&originalType=binary&ratio=2&size=18765&status=done&style=none&taskId=u22cf6bb6-8826-4fc8-89b6-173cdc9b59f&width=242)
  2. <a name="XKz7X"></a>
  3. # 操作方法
  4. concatslicesplice
  5. <a name="zt22q"></a>
  6. ## concat
  7. 将原数组拷贝一个副本,然后把待合并项加入到副本数组中,最后返回这个新数组。不改变原数组。<br />参数如果是非数组,则直接追加到副本数组中。<br />参数如果是数组,默认会打平数组,即将参数数组的每一项分别追加到副本数组中。
  8. ```javascript
  9. let array1 = [1]
  10. console.log(array1.concat(2)); // [ 1, 2 ]
  11. console.log(array1.concat(3,4, [5])); // [ 1, 3, 4, 5 ]
  12. console.log(array1.concat([6, 7])); // [ 1, 6, 7 ]
  13. console.log(array1.concat([8], [9, 10])); // [ 1, 8, 9, 10 ]
  14. console.log(array1.concat(2).concat(3,4,[5]).concat([6, 7]).concat([8], [9, 10])) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

打平数组 Symbol.isConcatSpreadable

如果concat的参数是数组,可以设置不打平。方法是给参数数组设置Symbol.isConcatSpreadable属性为true

  1. let colors = ["red", "green", "blue"];
  2. let moreNewColors = {
  3. [Symbol.isConcatSpreadable]: true,
  4. length: 2,
  5. 0: "pink",
  6. 1: "cyan"
  7. };
  8. console.log(colors.concat(moreNewColors)) // ["red", "green", "blue", "pink", "cyan"]

如果不设置这个属性、或设置为false,则不会打平

  1. let colors = ["red", "green", "blue"];
  2. let newColors = ["black", "brown"];
  3. newColors[Symbol.isConcatSpreadable] = false;
  4. let moreNewColors = {
  5. length: 2,
  6. 0: "pink",
  7. 1: "cyan"
  8. };
  9. console.log(colors.concat(newColors)) // ["red", "green", "blue", ["black", "brown"]]
  10. console.log(colors.concat(moreNewColors)) // ["red", "green", "blue", {...}]

image.pngimage.png

slice

浅拷贝数组指定项,返回新数组。不改变原数组。
参数1: start 开始索引 (数组方法中凡是涉及到start的,都包含。固定规律了)
参数2: end 结束索引 (不包括)若不传end就复制到结束

  1. [0,1,2,3].slice(1,3) // [1, 2]
  2. [0,1,2,3].slice(1) // [1, 2, 3]
  3. // 索引参数为负数,则倒着数。数组的最后一项就是-1
  4. [0,1,2,3,4].slice(-3, -1) // -1:4;-2:3;-3:2。所以得到[2, 3] 不包含结束值
  5. // 结束索引小于开始索引,返回空数组
  6. [0,1,2,3].slice(2, 1) // []

计算技巧:如果slice()的参数有负值,那么就以数值长度加上这个负值的结果确定位置。比如,在包含 5 个元素的数组上调用 slice(-2,-1),就相当于调用 slice(3,4)。

  1. [0,1,2,3,4].slice(3,4) // [3] 不包含4

不改变原数组:
image.png

splice

主要目的是在数组指定位置插入内容。改变原数组。返回被删除的元素(没有元素删除返回空数组)。
第一个参数永远是开始的位置索引
可以实现三种作用:

删除

第二个参数表示要删除的个数

  1. // .splice(开始删除的索引, 向后删除的个数)
  2. [0, 1, 2, 3].splice(2, 2) // 从索引2开始,删除2个元素。元数组变成[0,1],返回[2,3]

插入

第二个参数填0就是不删除
从第三个参数开始就是要插入的内容(第三个参数之后还可以传第四个、第五个参数,乃至任意多个要插入的元素。)

  1. // .splice(要插入的位置索引, 0, 要插入的内容)
  2. arr = [0, 1, 2]
  3. arr.splice(1, 0, 33, 44, 55) // 返回 []。 因为没有删除内容
  4. console.log(arr) // [0, 33, 44, 55, 1, 2]

替换

第二个参数就不是0了,就是要替换的个数

  1. arr = [0, 1, 2, 3, 4]
  2. arr.splice(1, 2, 33, 44) // 返回 [1, 2]
  3. console.log(arr) // [0, 33, 44, 3, 4]

搜索和位置方法

ECMAScript 提供两类搜索数组的方法:按严格相等搜索和按断言函数搜索。

indexOf、lastIndexOf、includes、find、findIndex

按严格相等搜索

就是说搜索的时候,会拿数组的每一项与函数调用时传入的参数严格对比。全等才算搜索到。
这些方法都接受两个参数:
第一个参数是要搜索的元素
第二个参数是要开始搜索的位置(包含这个位置)

indexOf

返回搜索到内容所在的下标。若找不到返回“-1”

  1. arr = [0, 1, 2, 3, 4]
  2. arr.indexOf(2) // 从第0个位置开始搜索数字2,arr中有数字2,返回2(下标)
  3. arr.indexOf(2, 3) // 从第三个位置开始搜索数字2,[3,4]中没有返回-1
  4. arr.indexOf(2, -3) // 负数可以看成是“数组长度-3”的位置,就是从第二位开始搜索,返回数字2所在的下标2

lastIndexOf

从数组末尾(最后一项)开始向前搜索
返回搜索到内容所在的下标。若找不到返回“-1”

  1. arr = ['a', 'b', 'c', 'd', 'e']
  2. arr.lastIndexOf('d') // 3,倒着寻找,e、d(判断全等,返回d所在索引)
  3. arr.lastIndexOf('d', 2) // -1,从索引为2的位置倒着往前找(c、b、a)找到头也没有d返回-1

includes

es7新增
函数返回值为布尔值。表示数组是否包含搜索的内容。

  1. arr = [0, 1, 2, 3, 4]
  2. arr.includes(2, 2) // 从第二个位置搜索数字2,刚好2在数组的位置索引就是第二位。所以返回true,表示搜索到了

上边的搜索都是判断元素全等,适合查找基础类型。涉及到数组中有引用类型或者想修改查找规则的时候就不适用了。那么就可以使用find/findIndex来实现:

按断言函数搜索

搜索的时候按照回调函数返回值来断言是否搜索到匹配项。

find

在数组中找内容,找到后把这个内容返回,并停止继续查找。找不到内容返回undefined

  • 第一个参数是回调函数:
    • 回调函数的三个参数和其他传回调函数的方法一致。分别是当前遍历的元素、当前遍历元素的索引、当前数组
  • 第二个参数是this指向。用于指定回调函数内部的this指向谁。 ```javascript arr = [{name: 1}, {name: 2}, {name: 3}] let result = arr.find((item, index, arr) => { console.log(item) // 只打印了1、2,因为2时满足条件,就停止向后遍历了 return item.name >= 2 }) result // {name: 2}

// 没有符合条件的:返回undefined let result1 = arr.find((item, index, arr) => { console.log(item) return item.name > 4 }) result1 // undefind

  1. <a name="YQDga"></a>
  2. ### findIndex
  3. 用法同find<br />但是找到内容后的返回值就是目标所在数组的索引值<br />找不到就返回 -1
  4. ```javascript
  5. arr = [{name: 1}, {name: 2}, {name: 3}]
  6. let result = arr.findIndex((item, index, arr) => {
  7. console.log(item) // 只打印了1、2,因为2时满足条件,就停止向后遍历了
  8. return item.name >= 2
  9. })
  10. result // 1 【返回索引值了】
  11. // 没有符合条件的:返回undefined
  12. let result1 = arr.findIndex((item, index, arr) => {
  13. console.log(item)
  14. return item.name > 4
  15. })
  16. result1 // -1

迭代方法

5 个迭代方法:forEach、filter、every、some、map
对数组每一项都运行传入的函数,并且都不改变原数组
每个方法接收两个参数:

  1. 第一个参数回调函数 - 以每一项为参数运行的函数
    1. 传给每个方法的函数接收 3 个参数:数组元素、元素索引和数组本身。
  2. 第二个参数 - 以及可选的作为函数运行上下文的作用域对象(影响函数中 this 的值)。

    forEach

    没有返回值,就是一个遍历

    filter

    回调函数返回true的内容会被加入到最后返回的数组中
    1. arr = [{name: 1}, {name: 2}, {name: 3}]
    2. arr.filter((item) => item.name >= 2) // [{name: 2}, {name: 3}]
    3. arr // [{name: 1}, {name: 2}, {name: 3}]
    4. arr.filter((item) => item.name >= 4) // [] 没有符合条件的元素,就返回空数组

    every

    数组里的每一项都符合回调函数里的判断条件、则回调函数都返回true。最终every返回true并停止遍历。
    就是检测数组里的每一项是否都符合判断条件。有任何一项不符合就返回false
    类似判断条件的「且」 ```javascript arr = [{name: 1}, {name: 2}, {name: 3}] arr.every((item) => { console.log(item) // {name: 1}, {name: 2}, {name: 3} 都符合条件就都走了 return item.name > 0 }) // true

arr.every((item) => { console.log(item) // {name: 1} 只走了一次,下边判断条件没通过直接结束循环 return item.name > 2 }) // false

  1. <a name="Db7Ae"></a>
  2. ## some
  3. 数组里只要有一项是符合条件的,回调函数只要返回一次true。则some返回true并停止遍历<br />检测数组里是否有符合条件的元素。只要有一项即可,所有项都不符合才返回false。<br />类似判断条件的「或」
  4. ```javascript
  5. arr = [{name: 1}, {name: 2}, {name: 3}]
  6. arr.some((item) => {
  7. console.log(item) // {name: 1} 只走了一次,下边判断条件成功就停止循环
  8. return item.name > 0
  9. }) // true
  10. arr.some((item) => {
  11. console.log(item) // {name: 1}, {name: 2}, {name: 3} 都走了一遍也找不到符合条件的
  12. return item.name < 0
  13. }) // false

map

不改变原数组。返回数组每一项根据回调函数逻辑修改后的新数组。

  1. let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
  2. let mapResult = numbers.map((item, index, array) => item * 2);
  3. mapResult; // 2,4,6,8,10,8,6,4,2
  4. numbers // [1, 2, 3, 4, 5, 4, 3, 2, 1]

归并方法

迭代数组的所有项,并返回一个最终值
回调函数接收 4 个参数:上一个归并值、当前项、当前项的索引和数 组本身。
这个函数返回的任何值都会作为下一次调用同一个函数的第一个参数。(所以回调函数一定要有返回值)
如果没有给这两个方法 传入可选的第二个参数(作为归并起点值),则第一次迭代将从数组的第二项开始,因此传给归并函数 的第一个参数是数组的第一项,第二个参数是数组的第二项。

reduce

正序迭代

  1. let values = [1, 2, 3, 4, 5];
  2. let sum = values.reduce((prev, cur, index, array) => {
  3. console.log(prev, cur)
  4. return prev + cur
  5. });

第一次执行归并函数时,prev 是 1,cur 是 2
image.png

reduceRight

倒序迭代
用法同上,只是循环数组时倒着循环。

扩展思考

改变原数组的方法汇总(其余的就不改变)

  1. copyWithIn(特殊点是不会改变数组长度、根据函数参数来决定数组内容的改变)
  2. push
  3. shift
  4. pop
  5. unshift
  6. reverse
  7. sort
  8. splice

    拷贝数组的几种方法

    以下使用数组原型方法的拷贝,都是浅拷贝:

  9. arr.slice(0)

  10. arr.map(item => item)
  11. arr.filter(item => true)
  12. arr.reduce((prev, cur)=> prev.concat(cur), [])

浅拷贝测试:
image.png