数组 | Array_API(含手写原理)

创建数组

(1)new Array() 【ES5】

创建数组可以通过Array构造函数的方式构造数组 (new 操作符可以省略)

1
2
let colors = new Array('red', 'green', 'yellow'); 
console.log(colors); // ['red', 'green', 'yellow']

特殊:当传入一个数值的时候,会创建一个指定数组的数组,用逗号创建的数组空位,值为undefined(关于数组空位,发现一些好玩的,请看这篇博文

1
2
3
let arr = Array(10);  // 这里省略了new操作符
console.log(arr); // [empty × 10]
console.log(arr.length); // 10

(2)Array.of() 【ES6】

Array.of()创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型

Array.of()代替了之前用Array.prototype.slice.call(arguments)转数组的笨拙写法

Array.of()Array构造函数的区别在于处理单个数组的情况

1
2
Array(10);   // [empty × 10]
Array.of(10); // [10]

(3)Array.from() 【ES6】

Array.from()创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型

语法(注意:容易被忽略)

1
Array.from(arrayLike, mapFn, thisArg);

参数

  • arrayLike:类数组或者可迭代对象(如Map,Set等)或者有一个length属性和可索引元素的结构
  • mapFn(可选):用于增强数组元素的回调函数
  • thisArg(可选):执行回调时的this对象

举例

1
2
3
4
5
6
7
8
9
10
11
// 第二个参数的妙用
let arrLike = {
'0': 1,
'1': 2,
'2': 3,
'3': 4,
length: 4,
};

let arr = Array.from(arrLike, item => item * 3);
console.log(arr); // [3,6,9,12]

Array.from()对现有数组是浅拷贝

1
2
3
let arr1 = [5,6,7,8];
let arr2 = Array.from(arr1);
console.log(arr1 === arr2); // false

数组索引

(1)中括号索引法

1
2
let colors = ['red', 'green', 'yellow'];
console.log(colors[1]); // green

(2)Array.length()

Array.length()返回或设置一个数组中的元素个数

1
2
let colors = ['red', 'green', 'yellow'];
console.log(colors.length); // 3

注意:数组的length并不只是可读的,而是可以通过修改length属性,从数组的末尾删除或者添加元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 删除
let colors = ['red', 'green', 'yellow'];
colors.length = 2;
console.log(colors); // ['red', 'green']

// 添加
let colors = ['red', 'green', 'yellow'];
colors.length = 4;
console.log(colors[3]); // undefined

// 使用length属性可以方便为数组末尾添加元素
let colors = ['red', 'green', 'yellow'];
colors[colors.length] = 'pink';
console.log(colors); // ['red', 'green', 'yellow', 'pink']

检测数组

(1)Array.isArray()

1
2
Array.isArray([1,2,3]);   // true
Array.isArray({name: 'Katrina'}); // false

思考:检测一个对象是否是数组的方法有?

迭代器方法

(1)Array.prototype.values() 【ES6】

values() 方法返回一个新的 Array Iterator 对象,该对象包含数组每个索引的值

(2)Array.prototype.keys() 【ES6】

keys() 方法返回一个包含数组中每个索引键的Array Iterator对象

(3)Array.prototype.entries() 【ES6】

entries() 方法返回一个新的Array Iterator对象,该对象包含数组中每个索引的键/值对

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const a = ['foo', 'bar', 'baz', 'qux'];

const aValues = Array.from(a.values());
const aKeys = Array.from(a.keys());
const aEntries = Array.from(a.entries());

console.log(aValues); // ['foo', 'bar', 'baz', 'qux']
console.log(aKeys); // [0, 1, 2, 3]
console.log(aEntries); // [[0, 'foo'], [1, 'bar'], [2, 'baz'], [3, 'qux']]


// 利用解构赋值可以很容易拆分键值对
for (let [key, value] of aEntries) {
console.log(key, value);
};

/*
0 'foo'
1 'bar'
2 'baz'
3 'qux'
*/

手写实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// entries()
Array.prototype.myEntries = function(arr) {
let res = [];

for (let i = 0; i < this.length; i++) {
res.push([i, this[i]])
};
return res;
};


// test
const a = ['foo', 'bar', 'baz', 'qux'];
console.log(a.myEntries());

/*
[[0, 'foo'], [1, 'bar'], [2, 'baz'], [3, 'qux']]
*/

复制和填充

(1)批量复制:copyWithin() 【ES6】

copyWithin() 方法浅复制数组的一部分到同一数组中的另一个位置,并返回它,不会改变原数组的长度

语法

1
arr.copyWithin(target, start, end)

参数

  • target:从这个位置开始填充
  • start(可选):填充的内容从这个位置开始截取
  • end(可选):填充的内容到这个位置截取完毕(不包含end)

注意

  • 负索引会被计算成负索引+length

  • copyWithin()静默忽略超出数组边界、零长度及方向相反的索引范围

举例

1
2
let arr = [0,1,2,3,4,5];
arr.copyWithin(0, 2, 4); // [2,3,2,3,4,5]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
需要注意:
1. copyWithin()静默忽略超出数组边界、零长度及方向相反的索引范围
2. 不会改变原数组的长度

*/
Array.prototype.myCopyWithin = function(target, start, end) {
target < 0 ? target +length : target;
start < 0 ? start +length : start;
end < 0 ? end +length : end;

const arr = this;
for (let i = start; i < end; i++) {
arr[target] = arr[i]
};
return arr;
};

// test
let arr = [0,1,2,3,4,5];
arr.copyWithin(0, 2, 4); // [2,3,2,3,4,5]

(2)填充数组:fill() 【ES6】

fill() 方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素,不包括终止索引,会改变原数组

语法

1
arr.fill(value, start, end)

参数

  • value:用于填充数组的元素
  • start:起始索引,默认为0
  • end:终止索引,默认为this.length

注意

  • 负索引会被计算成负索引+length

  • fill()静默忽略超出数组边界、零长度及方向相反的索引范围

举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let arr = [1,2,3,4,5,6,7];

// 索引过低忽略
arr.fill(0, -3, 4);
console.log(arr); // [1,2,3,4,5,6,7]

// 索引过高忽略
arr.fill(0, 20, 24);
console.log(arr); // [1,2,3,4,5,6,7]

// 索引反向忽略
arr.fill(0, 6, 3);
console.log(arr); // [1,2,3,4,5,6,7]

// 索引部分可用,填充可用部分
arr.fill(0, 3, 20);
console.log(arr); // [1,2,3,0,0,0,0]

手写实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
需要注意:
1. fill()静默忽略超出数组边界、零长度及方向相反的索引范围
2. 会改变原数组
*/

Array.prototype.myFill = function(value, start, end) {
start < 0 ? start + length : start;
end < 0 ? end + length : end;
end > this.length ? this.length : end;
for (let i = start; i < end; i++) {
this[i] = value;
};

return this;
};

// test
let arr = [1,2,3,4,5,6,7];
arr.fill(0, 3, 20);
console.log(arr); // [1,2,3,0,0,0,0]

转换方法

(1)toString() && toLocaleString() && valueOf()

valueOf()返回数组本身

toString()返回由数组中每个值的等效字符串拼接而成的一个逗号分隔的字符串

toLocaleString()返回一个字符串表示数组中的元素。数组中的元素将使用各自的 toLocaleString 方法转成字符串,这些字符串将使用一个特定语言环境的字符串(例如一个逗号 “,”)隔开

举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
let colors = ['red', 'green', 'yellow'];

colors.toString(); // 'red,green,yellow'
colors.valueOf(); // ['red', 'green', 'yellow']

alert(colors.toString()) // 'red,green,yellow'
alert(colors.valueOf()) // 'red,green,yellow'
alert(colors) // 'red,green,yellow'

/*
这里被显式调用toString()方法和valueOf()方法,分别返回数组的字符串表示
最后alert(colors),因为alert期待字符串,所以调用了toString()方法
*/


let person1 = {
toLocaleString() {
return "Nikolaos";
},
toString() {
return "Nicholas";
}
};
let person2 = {
toLocaleString() {
return "Grigorios";
},
toString() {
return "Greg";
}
};
let people = [person1, person2];
alert(people); // Nicholas,Greg 调用toString()
alert(people.toString()); // Nicholas,Greg 调用toString()
alert(people.toLocaleString()); // Nikolaos,Grigorios 调用toLocaleString()

(2)join()

join()返回以指定分隔符分隔数组元素的字符串

1
2
let colors = ['red', 'green', 'yellow'];
console.log(colors.join('-')); // 'red-green-yellow'

注意:如果数组中某一项是null或者undefined,则返回值会以空字符串表示

手写实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*
需要注意:
1. separator默认为逗号分隔
2. 如果数组中某一项是``null``或者``undefined``,则返回值会以空字符串表示
*/
Array.prototype.myJoin = function(separator = ',') {
let res = '';

for (let i = 0; i < this.length; i++) {
this[i] = this[i] === undefined || this[i] === null ? '' : this[i];
if (i < this.length - 1) {
res += (this[i] + separator);
} else if (i === this.length - 1) {
res += this[i];
};
};

return res;
};


// test
const a = [1,,3,4,undefined,6];
console.log(a.myJoin()); // 1,,3,4,,6
console.log(a.myJoin('-')); // 1--3-4--6

栈和队列方法

(1)push() 【ES5】

向数组末尾添加元素,返回修改后数组的长度

手写实现

1
2
3
4
5
6
7
8
9
10
/*
需要注意:返回的是修改后数组的长度
*/
Array.prototype.myPush = function() {
return [...this, ...arguments].length;
};

// test
let a = [1,2,4,5,6];
console.log(a.myPush(8,9)); //7

(2)pop() 【ES5】

删除数组的最后一项,同时减少数组的length值,返回被删除的元素

手写实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
需要注意:
1. 要减少数组的length值
2. 返回被删除的元素
3. 数组长度为0时返回undefined
*/
Array.prototype.myPop = function() {
if (this.length === 0) return undefined;
const res = this[this.length - 1];
this.length = this.length - 1;

return res;
};

let a = [1,2,4,5,6];
console.log(a.myPop()); // 6
console.log(a.length); // 4

(3)shift() 【ES5】

删除数组的第一项,同时减少数组的length值,返回被删除的元素

手写实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
需要注意:
1. 要减少数组的length值
2. 返回被删除的元素
3. 数组长度为0时返回undefined
*/
Array.prototype.myShift = function() {
if (this.length === 0) return 0;
const res = this[0];
for (let i = 1; i < this.length; i++) {
this[i-1] = this[i];
};
this.length = this.length - 1;
return res;
};

let a = [1,2,4,5,6];
console.log(a.myShift()); // 1
console.log(a); // [2,4,5,6]

(4)unshift() 【ES5】

在数组开头添加元素,返回修改后数组的长度

手写实现

1
2
3
4
5
6
7
8
9
10
/*
需要注意:返回的是修改后数组的长度
*/
Array.prototype.myUnshift = function() {
return [...arguments, ...this].length;
};

// test
let a = [1,2,4,5,6];
console.log(a.myUnshift(8,9)); //7

排序方法

补课啦:常见的排序算法及JS实现

(1)数组排序:sort() 【ES5】

sort() 方法用原地算法(即不创建额外的空间)对数组的元素进行排序,并返回数组。默认排序顺序是在将元素转换为字符串,然后比较它们的 UTF-16 代码单元值序列时构建的

语法

1
arr.sort(compareFunction)

参数

  • compareFunction(可选):用来指定按某种顺序进行排列的函数。如果省略,元素按照转换为的字符串的各个字符的 Unicode 位点进行排序
1
2
3
4
5
6
7
8
9
function compareFunction(value1, value2) {
if (value1 < value2) {
return -1; // 负值,value1排在value2前面
} else if (value1 > value2) {
return 1; // 正值,value1排在value2后面
} else {
return 0;
}
};

(2)翻转数组:reverse() 【ES5】

reverse() 方法将数组中元素的位置颠倒,并返回该数组,该方法会改变原数组

手写实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
改变原数组
*/
Array.prototype.myReverse = function() {
if (this.length <= 1) return this;
let left = 0, right = this.length -1;
while (left <= right) {
[this[left], this[right]] = [this[right], this[left]];
left++;
right--;
};
return this;
};

// test
const arr = ['h', 'e', 'l', 'l', 'o'];
console.log(arr.myReverse()); // ['o', 'l', 'l', 'e', 'h']

操作方法

(1)合并数组:concat() 【ES5】

concat() 方法用于合并两个或多个数组,此方法会首先创建一个当前数组的副本,然后再把参数添加到副本末尾,所以不会更改现有数组,而是返回一个新数组

语法

1
var new_array = old_array.concat(value1[, value2[, ...[, valueN]]])

参数

  • valueN:可以是数组或者值

注意

可以使用Symbol.isConcatSpreadable来控制传入的类数组对象是否强制打平,true为强制打平

举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let a1 = [1,2,3];
let a2 = {
[Symbol.isConcatSpreadable]:true,
length: 2,
0: 4,
1: 5,
};
let a3 = {
[Symbol.isConcatSpreadable]:false,
length: 2,
0: 4,
1: 5,
};

// 强制打平
let a1_2 = a1.concat(a2);
console.log(a1_2); // [1,2,3,4,5]

// 不强制打平
let a1_3 = a1.concat(a3);
console.log(a1_3); // [1,2,3, {[Symbol.isConcatSpreadable]:false,length: 2,0: 4,1: 5}]

手写实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*
注意:此方法会创建一个当前数组的副本,并不会修改原数组
*/
Array.prototype.myConcat = function() {
let args = [...arguments];
let res = this;

for (let i = 0; i < args.length; i++) {
if (typeof args[i] === 'object') {
res = [...res, ...args[i]];
} else {
res[res.length] = args[i];
}
};
return res;
};


// test
const a1 = [1,2,3];
const a2 = [5,6];
console.log(a1.myConcat(4, a2)); // [1,2,3,4,5,6]

(2)截取数组:slice() 【ES5】

slice() 方法返回一个新的数组对象,这一对象是一个由 beginend 决定的原数组的浅拷贝(包括 begin,不包括end),原始数组不会被改变

语法

1
arr.slice(start, end);

参数

  • start(可选):起始索引,默认为0
  • end(可选):终止索引(不含),默认为this.length

注意

  • 遇到负索引需要进行索引+length处理
  • start超过原数组的索引范围返回空数组
  • end大于原数组的长度,则截取到原数组的末尾

手写实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Array.prototype.mySlice = function(start, end) {
start > 0 ? start : start +length;
end > 0 ? end : end +length;
end > this.length ? this.length : end;

let res = [];
for (let i = start; i < end; i++) {
res.push(this[i]);
};

return res;
};

// test
const arr = ['h', 'e', 'l', 'l', 'o'];
console.log(arr.mySlice(2,4)); // ['l', 'l'];
console.log(arr); // ['h', 'e', 'l', 'l', 'o']

(3)插入元素:splice() 【ES5】

splice() 方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容,此方法会改变原数组

语法

1
arr.splice(start, deletcount, item1, item2,...)

参数

  • start:起始位置
  • deletecount(可选):要移除数组元素的个数,0或者负数表示不移除
  • item1 item2(可选):要添加的元素

返回值

由被删除的元素组成的一个数组

如果只删除了一个元素,则返回只包含一个元素的数组

如果没有删除元素,则返回空数组

方法

  • 删除:传2个参数
  • 插入:传3个参数
  • 替换:传3个参数

手写实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/*
改变原数组
*/
Array.prototype.mySlice = function() {
let args = [...arguments];
let start = Array.prototype.shift.call(args);
let deletecunt = Array.prototype.shift.call(args);

let left = this.slice(0, start); // 左边数组
let right = this.slice(start+deletecount, this.length); // 右边数组

let res = [...left, ...atgs, ... right];

// 改变原数组
for (let i = 0; i < res.length; i++) {
this[i] = res[i];
};

return this.slice(start, start + deletcount); // 返回删除的数据
};

// test
var myFish = ['angel', 'clown', 'drum', 'mandarin', 'sturgeon'];
var removed = myFish.splice(3, 1);
console.log(removed); // ['mandarin']


var myFish = ['angel', 'clown', 'drum', 'sturgeon'];
var removed = myFish.splice(2, 1, "trumpet");
console.log(removed); // ['drum]

搜索和位置方法

(1)按严格相等搜索:indexOf() 【ES5】 && lastIndexOf() 【ES5】 && inclues() 【ES6】

indexOf()方法返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1

lastIndexOf() 方法返回指定元素(也即有效的 JavaScript 值或变量)在数组中的最后一个的索引,如果不存在则返回 -1,从数组的后面向前查找,从 fromIndex 处开始

includes() 方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回 false

注意

  • 三者都可以指定fromIndex,即从哪一项开始寻找
  • 三者都采用严格相等搜索,即===比较
  • 找到目标之后不会继续往下找

手写实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/*
严格相等
*/
// indexOf()
Array.prototype.myIndexOf = function(num, fromIndex = 0) {
for (let i = fromIndex; i < this.length; i++) {
if (this[i] === num) {
return i;
};
};
return -1;
};

// test
const arr = [1,2,3,4,5,6];
console.log(arr.myIndexOf(2)) // 1
console.log(arr.myIndexOf(2, 4)) // -1


// includes()
Array.prototype.myIncludes = function(num, fromIndex = 0) {
for (let i = fromIndex; i < this.length; i++) {
if (this[i] === num) {
return true;
};
};
return false;
};

// test
const arr = [1,2,3,4,5,6];
console.log(arr.myIncludes(2)) // true
console.log(arr.myIncludes(2, 4)) // false

(2)按断言函数搜索:find() && findIndex() 【ES6】 ALL

find() 方法返回数组中满足提供的测试函数的第一个元素的值,否则返回undefined

findIndex()方法返回数组中满足提供的测试函数的第一个元素的索引,若没有找到对应元素则返回-1

注意

  • 两者都采用断言函数搜索,断言函数接收3个参数:元素 索引 数组本身
  • 因此两个的参数都是一个回调函数
  • 找到符合条件的元素后就不会再继续

手写实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Array.prototype.myFind = function(callback) {
if (typeof callback !== 'function') {
throw new Error('callback must be a function');
}

const arr = this;
for (let i = 0; i < arr.length; i++) {
if (callback(arr[i], i, arr)) {
return arr[i];
};
};

return undefined;
};

// test
const arr = [1,2,3,4,5,6];
const res = arr.myFind((item, index, arr) => item > 4);
console.log(res, arr); // 5 [1,2,3,4,5,6]



// findIndex()
Array.prototype.myFindIndex = function(callback) {
if (typeof callback !== 'function') {
throw new Error('callback must be a function');
}

const arr = this;
for (let i = 0; i < arr.length; i++) {
if (callback(arr[i], i, arr)) {
return i;
};
};

return -1;
};

迭代方法

(1)every() 【ES5】

every() 方法测试一个数组内的所有元素是否都能通过某个指定函数的测试,它返回一个布尔值

举例

1
2
3
4
5
6
7
let arr = [3,4,5,6,7];
arr.every(item => item > 2); // true
arr.every(item => item > 4); // false

/*
特点:必须每一项元素都满足条件
*/

手写实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Array.prototype.myEvery = function(callback) {
if (typeof callback !== 'function') {
throw new Error('callback must be a function');
};

for (let i = 0; i < this.length; i++) {
if (!callback(this[i], i, this)) {
return false;
}
};
return true;
};

// test
let arr = [3,4,5,6,7];
arr.myEvery(item => item > 2); // true
arr.myEvery(item => item > 4); // false

(2)some() 【ES5】

some() 方法测试数组中是不是至少有 1 个元素通过了被提供的函数测试,它返回一个布尔值

举例

1
2
3
4
5
6
7
let arr = [3,4,5,6,7];
arr.some(item => item > 2); // true
arr.some(item => item > 4); // true

/*
特点:只要有一项元素满足即可
*/

手写实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Array.prototype.mySome = function(callback) {
if (typeof callback !== 'function') {
throw new Error('callback must be a function');
};

for (let i = 0; i < this.length; i++) {
if (callback(this[i], i, this)) {
return true;
}
};
return false;
};

// test
let arr = [3,4,5,6,7];
arr.mySome(item => item > 4); // true
arr.mySome(item => item > 8); // false

(3)filter() 【ES5】

filter() 方法创建一个新数组,其包含通过所提供函数实现的测试的所有元素

举例

1
2
let arr = [3,4,5,6,7];
arr.filter(item => item > 5); // [ 6, 7]

手写实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 迭代法
Array.prototype.myFilter = function(callback) {
if (typeof callback !== 'function') {
throw new Error('callback must be a function');
};

let res = [];
for (let i = 0; i < this.length; i++) {
if (callback(this[i], i, this)) {
res.push(this[i]);
};
};
return res;
};

let arr = [3,4,5,6,7];
arr.myFilter(item => item > 5); // [ 6, 7]

// 借助reduce
Array.prototype.myFilter = function(callback) {
if (typeof callback === 'function') {
return this.reduce((prev, item, index, arr) => {
if (callback(item, index, arr)) {
prev.push(item);
};
return prev;
}, [])
} else {
throw new Error('callback is not a fucntion');
}
};

// test
let arr = [3,4,5,6,7];
arr.myFilter(item => item > 5); // [ 6, 7]

(4)forEach() 【ES5】

forEach() 方法对数组的每个元素执行一次给定的函数,没有返回值,改变原数组

举例

1
2
3
let arr = [3,4,5,6,7];
arr.forEach(item => item*2);
console.log(arr); // [6,8,10,12,14]

手写实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*
注意: 改变原数组

*/
Array.prototype.myForEach = function(callback, thisArg) {
const arr = this;

for (let i = 0; i < arr.length; i++) {
callback.call(thisArg, arr[i]);
};
};

const obj = {
num: 10
};
const arr = [1, 2, 3, 4, 5, 6];

arr.myForEach(function (value, index, arr) {
console.log(value + this.num); // 依次打印:11 12 13 14 15 16
}, obj);
console.log(arr); // [1, 2, 3, 4, 5, 6]

(5)map() 【ES5】

map() 方法创建一个新数组,这个新数组由原数组中的每个元素都调用一次提供的函数后的返回值组成

举例

1
2
3
let arr = [3,4,5,6,7];
let newArr = arr.map(item => item*2);
console.log(newArr); // [6,8,10,12,14]

注意

  • map仅对每一项已分配值得索引调用
1
2
let a = [1,2,,4];
let newA = a.map(item => item*2); // [2,4,,8]

手写实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 迭代法
Array.prototype.myMap = function(callback) {
if (typeof callback !== 'function') {
throw new Error('callback must be a function');
};
let arr = this;
let res = [];

for (let i = 0; i < arr.length; i++) {
let value = callback(arr[i], i, arr);
res.push(value);
};
return res;
};

// test
let a = [1,2,4];
let newA = a.myMap(item => item*2); // [2,4,8]


// reduce改进
Array.prototype.myMap = function(callback) {
if (typeof callback !== 'function') {
throw new Error('callback must be a function');
};

let arr = this;
return arr.reduce((prev, curr, index, arr) => {
prev.push(callback(curr, index, arr));
return prev;
}, [])
};

// test
let a = [1,2,4];
let newA = a.myMap(item => item*2); // [2,4,8]

归并方法

(1)reduce() 【ES5】

reduce()接收两个参数:归并函数和归并起点的初始值

其中归并函数接收四个参数:上一个归并值、当前项、当前项的索引、数组本身。归并函数返回的任何值都会成为下一次调用同一个函数的第一个参数,即归并值,如果没有归并起点的初始值,则把第一个元素作为初始值,迭代从第二个元素开始

reduce的应用详见:强大的reduce

手写实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
Array.prototype.myReduce = function(callbackFn, initalValue) {
// Step1:检验
if (this === null) {
throw new Error('Array.prototype.reduce called on null or undefined');
};
if (typeof callbackFn !== 'function') {
throw new Error('Callback must be a function');
};

let obj = Object(this);
const lenValue = obj.length;
const len = lenValue >>> 0;

if (!len && !initalValue) {
throw new TypeError('The array contains no elements and initalValue is not provided')
};

// Step2: 确定accumulator初始值
let k = 0;
let accumulator;

if (initalValue) {
accumulator = initalValue;
} else {
let kPressent = false;
while (!kPressent && k < len) {
const pK = String(k);
kPressent = obj.hasOwnProperty(pK); // 是否有效
if (kPressent) {
accumulator = obj[pK];
};
k++;
};
if (!kPressent) {
throw new TypeError('The array contains error elements');
};
};

// Step3:确定返回值
while (k < len) {
if (k in obj) {
accumulator = callbackFn(accumulator, obj[k], k, obj);
};
k++;
};
return accumulator;
};

// test
const arr = [1,2,3,4,5];
const sum = arr.myReduce((prev, curr) => prev+curr, 0);
console.log(sum); // 15

(2)reduceRight() 【ES5】

reduceRight()reduce()唯一不同的就是遍历方向是从最后一项到第一项,其余全部相同

其他

(1)数组扁平化:flat() 【ES6】

flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回

语法

1
arr.flat([depth])

参数

  • depth(可选):指定要提取嵌套数组的深度,默认为1
1
2
3
4
5
6
7
8
9
10
11
function myFlat(arr, depth = 1) {
if (depth < 1) return arr;
return arr.reduce((prev, curr, index, arr) => {
return Array.isArray(curr) ? prev.concat(myFlat(curr, depth - 1)) : prev.concat(curr);
}, [])
};

// test
const arr = [1, 2, [[3,4]]]
myFlat(arr, 1) // [1, 2, [3,4]]
myFlat(arr, 2) // [1, 2, 3, 4]

总结

(1)改变原数组方法

push

unshift

pop

shift

reverse

splice

sort

(2)ES6新增方法

Array.from

Array.of

copyWithin()

fill()

find()

findIndex()

entries()

keys()

values()

includes()

flat()

知识补充

数组空位

map和forEach的区别

强大的reduce

深浅拷贝

参考

MDN Array