跳转至

JavaScript数组

约 5193 个字 496 行代码 预计阅读时间 24 分钟

介绍

数组也是一种复合数据类型,在数组可以存储多个不同类型的数据。与C++或者JavaScript一样,JavaScript数组中的每个数据都有一个唯一的索引,所以可以通过索引进行数据的访问。在数组中,数组的数据也被称为元素,对应的索引最小值为0

在JavaScript中,有两种创建数组的方式:

  1. 使用Array()构造函数创建一个空数组,这种方式无法在创建数组时为数组添加元素
  2. 使用[]创建一个空数组,这种方式可以在初始化时为数组添加元素

例如下面的代码:

JavaScript
1
2
3
4
5
6
let arr1 = new Array();

let arr2 = [];

// 初始化时添加元素
let arr3 = [1, 2, 3, 4];

与C++和Java一样,可以直接通过下标访问和修改元素,例如下面的代码:

JavaScript
1
2
3
4
5
let arr3 = [1, 2, 3, 4];

console.log(arr3[1]); // 2
arr3[2] = 5;
console.log(arr3[2]); // 5

在JavaScript中,因为数组也是对象,所以数组也有自己的属性,这里先介绍数组常用的属性:长度。在JavaScript中,如果想获取数组的长度,只需要访问数组对象的length属性即可,例如下面的代码:

JavaScript
1
2
3
let arr3 = [1,2,3,4];

console.log(arr3.length); // 4

JavaScript数组中的length并不是一个常量,其长度可以指定,如果指定的数值大于原先的数值,则相当于为数组扩容,多余的部分以undefined填充;如果指定的数值小于原先的数值,则相当于删除数组中的元素。例如下面的代码:

JavaScript
1
2
3
4
5
6
7
let arr3 = [1, 2, 3, 4];

arr3.length = 10;
console.log(arr3[6]); // undefined

arr3.length = 1;
console.log(arr3); // [1]

Note

需要注意,尽管length属性可以修改,但是其值不可以是负数,否则会报错RangeError: Invalid array length

遍历数组

基本使用

和C++与Java一样,使用for循环枚举下标结合数组的长度即可,这种方法不再赘述。在JavaScript中,还有一种遍历数组的方式是通过for-of语句

在JavaScript中,for-of可以相当于C++和Java中的增强for,其语法如下:

JavaScript
1
2
3
4
5
for(let value of array)
{

}
// 此时value就是array中的每一个元素

例如下面的代码:

JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
let arr3 = [1, 2, 3, 4];

for (const number of arr3) {
    console.log(number);
}

输出结果
1
2
3
4

for-infor-of的区别

for-in用于遍历对象的可枚举属性(包括原型链上的属性),遍历的结果是对象的key而不是value,可以用于普通对象和数组,但是因为数组遍历出来的是下标,所以基本使用for-in遍历数组,例如:

JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const obj = { a: 1, b: 2, c: 3 };

for (let key in obj) {
  console.log(key); // 输出: 'a', 'b', 'c'
}

const arr = ['x', 'y', 'z'];

for (let index in arr) {
  console.log(index); // 输出: '0', '1', '2' (数组的索引)
}

for-of用于遍历可迭代对象(如数组、字符串、Map、Set 等)的值,遍历的结果是对象的值(value),而不是键(key),适合遍历数组、字符串等实现了[Symbol.iterator]方法的对象,例如:

JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const arr = ['x', 'y', 'z'];

for (let value of arr) {
  console.log(value); // 输出: 'x', 'y', 'z' (数组的元素)
}

const str = 'hello';

for (let char of str) {
  console.log(char); // 输出: 'h', 'e', 'l', 'l', 'o'
}

需要注意,使用{}直接定义的普通对象默认不可以迭代,所以不能使用for-of遍历

数组常用方法

isArray()方法

Array类的静态方法,参数传递一个数组对象,用于检查某一个对象是否是数组。方法返回true或者false

JavaScript
1
2
3
4
let arr = [];
let obj = {};
console.log(Array.isArray(arr)); // true
console.log(Array.isArray(obj)); // false

at()方法

Array对象方法,参数传递索引。可以根据索引获取数组中的指定元素,与使用[]访问的方式不同,at()可以接收负索引作为参数,如果是负数索引,就相当于从后向前遍历。方法返回下标对应的值

JavaScript
1
2
3
4
let arr = [1, 2, 3, 4];

console.log(arr.at(1)); // 2
console.log(arr.at(-2)); // 3

concat()方法

Array对象方法,其参数传递一个或多个数组对象,用来连接调用方法的数组对象和参数中的数组对象。方法返回一个新数组

Note

需要注意的是,这个方法会将连接后的数组返回,并不会修改原先任何一个对象数组

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

console.log(arr.concat(arr1, arr2));
// 不会改变原数组
console.log(arr);
console.log(arr1);
console.log(arr2);

输出结果
[
   1,  2, 3, 4,  5,
   6,  7, 8, 9, 10,
  11, 12
]
[ 1, 2, 3, 4 ]
[ 5, 6, 7, 8 ]
[ 9, 10, 11, 12 ]

indexOf()方法

Array对象方法,用于获取参数元素在数组中第一次出现的索引,该函数接受两个参数,第一个参数代表要查找的元素,第二个参数代表查找的起始位置。方法找到对应的数值,返回其下标,否则返回-1

Note

第二个参数可以不传递,默认情况下从数组第一个元素的位置开始查找

JavaScript
1
2
3
4
5
let arr = [1, 2, 3, 3, 3, 5, 3, 4];

console.log(arr.indexOf(3)); // 2
console.log(arr.indexOf(3, 5)); // 6
console.log(arr.indexOf(6)); // -1

lastIndexOf()方法

本方法与indexOf()方法功能类似,只是其是从后向前查找,不再演示。方法接受两个参数,第一个参数表示查找元素,第二个表示起始位置,找到对应的数值,返回其下标,否则返回-1

join()方法

Array对象方法,其作用是将数组中的元素根据连接符连接成一个字符串,方法接受一个参数,参数表示连接符,默认为逗号,

JavaScript
1
2
3
4
let arr = [1, 2, 3, 3, 3, 5, 3, 4];

console.log(arr.join()); // 1,2,3,3,3,5,3,4
console.log(arr.join("-")); // 1-2-3-3-3-5-3-4

slice()方法

Array对象方法,其有连两个参数,第一个参数表示截取的起始位置(包括该位置),第二个参数表示截取的结束位置(不包括该位置)。方法返回一个新数组

  1. 如果指定了起始位置和终止位置,那么函数就会从起始位置一直截取到终止位置,不包括终止位置
  2. 如果指定了起始位置,但是没有指定终止位置,则默认情况下截取到数组的最后一个元素,包括最后一个元素
  3. 如果两个参数都不指定,则默认情况下对数组进行浅拷贝

Note

需要注意,不论是截取还是浅拷贝,都不会改变原来的数组

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

// 指定起始位置和种植位置
console.log(arr.slice(1,4));
console.log(arr.slice(2));
let arr1 = arr.slice();
console.log(arr1);

输出结果
[ 2, 3, 3 ]
[ 3, 3, 3, 5, 3, 4 ]
[
  1, 2, 3, 3,
  3, 5, 3, 4
]

浅拷贝与深拷贝

在JavaScript中,浅拷贝意味着对数组或者对象进行浅层次的拷贝。在数组中,如果其元素都是原始值,则浅拷贝与深拷贝没有差异,但是如果数组中是对象,则会出现问题

数组和对象的深浅拷贝概念是一致的,下面以数组的深浅拷贝进行叙述:

  • 浅拷贝:如果数组中存储了对象,则浅拷贝情况下只会拷贝对象本身,但是不会单独拷贝对象中的属性。如果使用浅拷贝将数组中的对象拷贝给另外一个对象,则当另外一个对象修改其属性时,被拷贝的对象中的属性也会同时改变
  • 深拷贝:与浅拷贝相反,如果数组中存储了对象,则深拷贝情况下不仅会拷贝对象本身,也会将对象中的属性一起拷贝。如果使用深拷贝将数组中的对象拷贝给另外一个对象,则当另外一个对象修改其属性时,被拷贝的对象中的属性不会受到影响

在JavaScript中,如果想对一个对象进行深拷贝,可以调用structuredClone()函数,其参数传递被拷贝的对象,该函数会返回一个新对象。如果想对一个对象进行浅拷贝,可以调用Object类的静态方法assign(),其参数传递被拷贝的对象,该函数会返回一个新对象

例如下面的代码:

JavaScript
 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
53
54
55
56
57
// 直接浅拷贝
const obj = {
    name: "Tom",
    age: 20,
    friend: {
        name: "Jerry",
        age: 18
    }
}

const obj1 = Object.assign({}, obj);
console.log(obj1);
console.log(obj1.friend === obj.friend); // true
// 修改影响原对象
obj1.friend.name = "Alice";
console.log(obj1.friend.name); // Alice
console.log(obj.friend.name); // Alice

// slice浅拷贝
const obj2 = {
    name: "Tom",
    age: 20,
    friend: {
        name: "Jerry",
        age: 18
    }
}

let arr = [obj2];
const arr1 = arr.slice();
console.log(arr1);
console.log(arr1[0] === arr[0]); // true

// 修改影响原对象
arr1[0].friend.name = "Alice";
console.log(arr1[0].friend.name); // Alice
console.log(arr[0].friend.name); // Alice

// 深拷贝
const obj3 = {
    name: "Tom",
    age: 20,
    friend: {
        name: "Jerry",
        age: 18
    }
}

let arr2 = [obj3];

const arr3 = structuredClone(arr2);
console.log(arr3);
console.log(arr2[0] === arr3[0]); // false
// 修改不影响原对象
arr3[0].friend.name = "Alice";
console.log(arr2[0].friend.name); // Jerry
console.log(arr3[0].friend.name); // Alice

需要注意,在上面的代码中,obj对象自身中的name属性和age属性都是原始值,所以深浅拷贝效果都是一样的,但是其中的friend属性属于引用类型,此时浅拷贝就会出现修改新对象影响原对象的问题

对象的复制

在JavaScript中,经常会涉及到对象与对象直接的赋值,下面是常见的两种复制方式:

  1. 使用展开运算符...
  2. 调用Object类中的静态方法assign()

对于第二种方法,在浅拷贝和深拷贝处已经介绍,此处不再赘述。下面讨论展开运算符...

在JavaScript中,展开运算符可以将一个数组中的元素展开到另一个数组中或者作为函数的参数传递,也可以通过它也可以对数组进行浅拷贝

例如下面的代码

JavaScript
1
2
3
let arr = [1, 2, 3, 4];
let arr1 = [...arr];
console.log(arr1); // [ 1, 2, 3, 4 ]

利用展开运算符将数组中的元素传递给函数的参数,代码如下:

JavaScript
1
2
3
4
5
6
7
let arr = [1, 2, 3, 4];

function sum(a, b, c, d) {
    return a + b + c + d;
}

console.log(sum(...arr)); // 10

push()方法

Array对象方法,向数组的末尾添加一个或多个元素,参数传递一个或多个值,并返回新的长度

JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
let arr = [1, 2, 3, 4];

let len = arr.push(4, 5, 6);
console.log(arr); 
console.log(len);

输出结果
[
  1, 2, 3, 4,
  4, 5, 6
]
7

pop()方法

Array对象方法,删除并返回数组的最后一个元素

JavaScript
1
2
3
4
5
let arr = [1, 2, 3, 4];

let number = arr.pop();
console.log(arr); // [ 1, 2, 3 ]
console.log(number); // 4

unshift()方法

Array对象方法,向数组的开头添加一个或多个元素,参数传递一个或多个值,并返回新的长度

JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
let arr = [1, 2, 3, 4];

let len = arr.unshift(4, 5, 6);
console.log(arr);
console.log(len);

输出结果
[
  4, 5, 6, 1,
  2, 3, 4
]
7

shift()方法

Array对象方法,删除并返回数组的第一个元素

JavaScript
1
2
3
4
5
let arr = [1, 2, 3, 4];

let number = arr.shift();
console.log(arr); // [ 2, 3, 4 ]
console.log(number); // 1

splice()方法

Array对象方法,其作用由其传递的参数传递,一共由三种常用的作用:

  1. 删除元素
  2. 插入元素
  3. 替换数组中的元素

该方法参数一共有三个:

  1. 第一个参数:删除的起始位置
  2. 第二个参数:删除元素的数量,包括起始位置的元素
  3. 第三个参数:要插入的元素

如果只传递第一个参数,那么会从该位置开始一直删除到最后一个元素之间的所有元素;如果传递第一个和第二个参数,则代表删除从指定位置开始的指定个数的元素;如果传递第一个参数,并且第二个参数为1,则表示删除指定位置的元素;如果传递第一个参数、第二个参数和第三个参数,则代表删除指定位置开始的指定个数的元素,并在起始位置处添加第三个参数的元素

该方法会返回删除的元素

Note

第三个参数可以是多个,而不仅仅局限于一个

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

// 删除指定位置开始的指定个数的元素
let arr1 = arr.splice(1, 2);
console.log(arr); // [ 1, 4 ]
console.log(arr1); // [ 2, 3 ]

// 删除当前位置的元素
arr = [1, 2, 3, 4];
let arr2 = arr.splice(1, 1);
console.log(arr); // [ 1, 3, 4 ]
console.log(arr2); // [ 2 ]

// 在指定位置插入元素
arr = [1, 2, 3, 4];
let arr3 = arr.splice(1, 1, 11, 12, 13);
console.log(arr); // [ 1, 11, 12, 13, 3, 4 ]
console.log(arr3); // [ 2 ]

reverse()方法

Array对象方法,其作用是反转数组,方法返回一个新数组

JavaScript
1
2
3
4
5
let arr = [1, 2, 3, 4];

let arr1 = arr.reverse();

console.log(arr1); // [ 4, 3, 2, 1 ]

回调函数的应用

高阶函数:如果一个函数的参数或返回值是函数,则这个函数就称为高阶函数。如果将函数作为函数的参数,则意味着可以对另一个函数动态地传递代码

例如下面的代码:

JavaScript
 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
class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
}

const personArr = [
    new Person("mark", 18),
    new Person("alice", 38),
    new Person("peter", 8),
    new Person("david", 16)
]

function filter(arr, cb) {
    const newArr = [];

    for (let i = 0; i < arr.length; i++) {
        // 使用回调函数
        if (cb(arr[i])) {
            newArr.push(arr[i]);
        }
    }

    return newArr;
}

// 显式创建函数将其作为参数传递
function fn(ele) {
    return ele.age < 18
}

let newArr = filter(personArr, fn);

console.log(newArr); // [ Person { name: 'peter', age: 8 }, Person { name: 'david', age: 16 } ]

在实际开发中,一般不会直接使用一个定义的函数作为实参传递,更多地将箭头函数作为实参传递给函数的形参,例如下面的代码:

JavaScript
1
2
3
4
5
6
// ...

// 使用箭头函数作为实参传递
let newArr = filter(personArr, ele => ele.age < 18);

console.log(newArr); // [ Person { name: 'peter', age: 8 }, Person { name: 'david', age: 16 } ]

上面演示了回调函数作为参数传递,下面演示函数作为返回值的例子:

JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
function fn(cb) {
    console.log("this is fn function");
    return () => {
        console.log("this is answer function");

        const result = cb();

        return result;
    }
}

// 传递箭头函数
let func = fn(()=>{console.log("this is parameter"); return "this is result";});
let str = func();
console.log(str);

输出结果
this is fn function
this is answer function
this is parameter
this is result

在上面的代码中,首先fn()函数接收一个箭头函数作为参数,进入fn()函数,先执行打印this is fn function,接着执行return语句,因为return语句返回的是一个函数,所以在外部使用func变量接收,此时func就执行fn函数返回的函数,调用func()函数,先打印this is answer function,再执行了cb指向的函数(也就是fn的回调函数参数:()=>{console.log("this is parameter"); return "this is result";}),打印了this is parameter并返回this is resultfunc()函数执行完后,将其返回值给result变量,func()函数内部的return语句返回result后在调用处被str接收,打印str就是this is result

闭包

介绍

在JavaScript中,闭包表示能访问到外部函数作用域中变量的函数,一般在需要隐藏一些不希望被外部访问的内容时就可以使用闭包

形成闭包的三个条件如下:

  1. 函数的嵌套
  2. 内部函数要引用外部函数中的变量
  3. 内部函数要作为返回值返回

例如下面的代码:

JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
function outer(){
    let num = 0; // 位于函数作用域中

    return () => {
        num++;
        console.log(num);
    }
}

const newFn = outer();

newFn(); // 1
newFn(); // 2
newFn(); // 3

闭包的原理

在JavaScript中,函数的作用域在函数创建的时候就已经确定好了,所以函数内访问某一个变量关键并不是是看函数调用的位置之前是否存在对应的变量,即与其实际的调用位置没有关系,这一作用域也被称为词法作用域,而闭包就是利用词法作用域

例如下面的代码:

JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
let a = 10;

function fn(){
    console.log(a);
}

function fn2(){
    let a = 20;


    fn(); // 10
}

fn2();

在上面的代码中,因为函数fn()在创建时就已经确定好了作用域,所以其访问到的a一定是全局变量的a,而不是调用位置上面的a

接着看闭包下的情况:

JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
let a = 10;
function fn3() {
    let a = 20;

    function fn4() {
        console.log(a); // 20
    }

    return fn4;
}

let fn5 = fn3();

fn5();

因为fn4()函数创建时的作用域在fn3内部,所以访问的变量是fn3()函数内部的a,而不是全局变量的a

闭包的注意事项

闭包的声明周期:

  1. 闭包在外部函数调用时产生,外部函数每次调用都会产生一个全新的闭包
  2. 在内部函数丢失时销毁(内部函数被垃圾回收了,闭包才会消失)

例如下面的代码:

JavaScript
 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
// 第一点
function createCounter() {
    let count = 0;
    return function() {
        count++;
        console.log(count);
    };
}

const counter1 = createCounter();
counter1(); // 输出: 1
counter1(); // 输出: 2

const counter2 = createCounter();
counter2(); // 输出: 1

// 第二点
function createFunction() {
    let data = "Hello, World!";
    return function() {
        console.log(data);
    };
}

let myFunction = createFunction();
myFunction(); // 输出: Hello, World!

myFunction = null; // 解除对内部函数的引用,闭包可以被垃圾回收

因为闭包主要用来隐藏一些不希望被外部访问的内容,所以闭包也需要占用一定的内存空间。在实际开发中,相较于类来说,闭包比较浪费内存空间(类可以使用原型而闭包不能),如果需要执行次数较少时,使用闭包,否则使用类

数组高阶方法

sort()方法

Array对象方法,其作用是为数组中的数据排序,但是默认情况下,排序的比较方式是按照元素的Unicode编码进行比较,所以如果数组中存储的都是数值,则此时会出现10和1紧挨着的类似情况

例如下面的代码:

JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
let arr = [2, 3, 1, 9, 0, 4, 5, 7, 8, 6, 10];

let numbers = arr.sort();

console.log(numbers);

输出结果
[
  0, 1, 10, 2, 3,
  4, 5,  6, 7, 8,
  9
]

为了解决这种问题,就需要调用者自己传递一个回调函数告诉sort函数排序时元素的比较方式,在JavaScript中,传递给sort函数的回调函数接收两个参数,这两个参数分别代表第一个比较操作数和第二个比较操作数,假设二者分别为ab,则有:

  1. a - b > 0,则表示升序排序
  2. a - b < 0,则表示降序排序
  3. a - b === 0,则表示不改变原始顺序

Note

因为只有a - b === 1时,函数才会进行排序,所以如果需要降序,就需要写成b - a

JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
let arr = [2, 3, 1, 9, 0, 4, 5, 7, 8, 6, 10];

let numbers = arr.sort((a, b) => b - a);

console.log(numbers);

输出结果
[
  10, 9, 8, 7, 6,
   5, 4, 3, 2, 1,
   0
]

forEach()方法

Array对象方法,其主要作用是遍历数组元素,其参数传递一个回调函数,回调函数一共有三个参数:

  1. 第一个参数:element,表示当前的元素
  2. 第二个参数:index,表示当前元素的索引
  3. 第三个参数:array,表示遍历的数组对象

Note

尽管可以传递三个参数,但是实际上一般只会使用到第一个参数

例如下面的代码:

JavaScript
1
2
3
4
5
6
7
8
9
let arr = [1, 2, 3];
arr.forEach((ele, index, array) => {
    console.log(ele, index, array)
});

输出结果
1 0 [ 1, 2, 3 ]
2 1 [ 1, 2, 3 ]
3 2 [ 1, 2, 3 ]

快速遍历对象

前面遍历对象时使用的是for-in,但是这样遍历对象有点繁琐,所以可以考虑使用Object.keys()方法结合forEach快速遍历对象。其中,Object.keys()方法会返回对象中所有的key构成的数组,例如:

JavaScript
1
2
3
4
5
6
const obj = {
    id: '1',
    name: 'zhangsan',
}
let list = Object.keys(obj);
console.log(list); // ['id', 'name']

此时就可以使用数组的forEach方法实现快速遍历:

JavaScript
1
2
3
4
5
6
const obj = {
    id: '1',
    name: 'zhangsan',
}

Object.keys(obj).forEach(i=>console.log(obj[i])); // 1 zhangsan

filter()方法

Array对象方法,其主要作用是根据某一个条件筛选元素,其参数传递一个回调函数,回调函数一共有三个参数:

  1. 第一个参数:element,表示当前的元素
  2. 第二个参数:index,表示当前元素的索引
  3. 第三个参数:array,表示遍历的数组对象

该方法会返回一个新数组,不会修改原始数组中的数据

Note

尽管可以传递三个参数,但是实际上一般只会使用到第一个参数

JavaScript
1
2
3
4
let arr = [2, 3, 1, 9, 0, 4, 5, 7, 8, 6, 10];

let arr1 = arr.filter((ele) => ele > 5);
console.log(arr1); // [ 9, 7, 8, 6, 10 ]

map()方法

Array对象方法,根据当前数组生成一个新数组,其参数为一个回调函数,回调函数一共有三个参数:

  1. 第一个参数:element,表示当前的元素
  2. 第二个参数:index,表示当前元素的索引
  3. 第三个参数:array,表示遍历的数组对象

该方法会返回一个新数组,不会修改原始数组中的数据

Note

尽管可以传递三个参数,但是实际上一般只会使用到第一个参数

JavaScript
1
2
3
4
let arr = [1, 2, 3];

let arr1 = arr.map(ele => "this is " + ele + " element");
console.log(arr1); // [ 'this is 1 element', 'this is 2 element', 'this is 3 element' ]

includes()方法

Array对象方法,用于判断数组是否存在某种元素,该函数有两个参数,第一个参数表示查询元素,第二个参数表示查询起始点(默认从0开始),如果第二个参数不是整数,会将其转换成整数

JavaScript
1
2
3
4
5
6
7
let arr = [1,2,3,4,5,5,5,9,3,2];

p1 = arr.includes(22, 0);
p2 = arr.includes(3, 0);
// 默认从头开始查询
p3 = arr.includes(3);
console.log(p1, p2, p3); // false true true

every()方法

Array对象方法,该方法用于检测数组中的每一个元素是否符合给定条件,一旦检测到不符合就退出。类似于对数组所有的元素进行逻辑与。其参数为一个回调函数,回调函数一共有三个参数:

  1. 第一个参数:element,表示当前的元素
  2. 第二个参数:index,表示当前元素的索引
  3. 第三个参数:array,表示遍历的数组对象
JavaScript
1
2
3
4
5
6
7
8
9
let arr = [1,2,3,4,5,5,5,9,3,2];

isTrue = arr.every((i, index)=>{
    // 尽管第五个元素5不满足条件,但是因为只有在return的时候才比较,所以依旧会打印出5的下标
    console.log(index);

    return i < 5;
})
console.log(isTrue); // false

some()方法

Array对象方法,用于检测数组中的每一个元素是否至少有一个符合给定条件,一旦有就返回true,否则返回fale。类似于对于数组所有的元素进行逻辑或。其参数为一个回调函数,回调函数一共有三个参数:

  1. 第一个参数:element,表示当前的元素
  2. 第二个参数:index,表示当前元素的索引
  3. 第三个参数:array,表示遍历的数组对象
JavaScript
1
2
3
4
let arr = [1,2,3,4,5,5,5,9,3,2];

isTrue = arr.some(i=>i < 5)
console.log(isTrue); // true

reduce()方法

Array对象方法,可以用来将一个数组中的所有元素整合为一个值,其参数有两个,第一个为一个回调函数,回调函数一共有两个参数:

  1. 第一个参数:表示用于合并的第一个值
  2. 第二个参数:表示用于合并的第二个值

Note

尽管可以传递三个参数,但是实际上一般只会使用到第一个参数

方法的第二个参数为初始值,方法返回一个合并后的值

Note

mapreduce的区别:map用于对每个元素进行计算,reduce用于对整个数组的元素进行累计计算

JavaScript
1
2
3
4
5
let arr = [1, 2, 3];

// 累加求和,初始值为2
let number = arr.reduce((a, b) => a + b, 2);
console.log(number);

可变参数

this之后,arguments参数是第二个函数隐含的参数。在JavaScript中,arguments是一个类数组对象(伪数组),所谓伪数组,就是和数组相似,可以通过索引来读取元素,也可以通过for循环变量,但是它不是一个数组对象,不能调用数组的方法

arguments用来存储函数的实参,无论用户是否定义形参,传递给函数的参数值都会存储到arguments对象中,所以可以通过该对象直接访问实参

例如下面的代码:

JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// 定义一个函数,可以求任意个数值的和
function sum() {
    let result = 0;

    for (let num of arguments) {
        result += num;
    }

    return result;
}

// 计算两个数和
let number = sum(1, 2);
// 计算四个数的和
let number1 = sum(1, 2, 3, 4);

console.log(number, number1); // 3 10

但是,尽管有arguments参数,但是其不能使用数组的方法,所以在实际使用中很少使用。在前面介绍了展开运算符...可以将数组的元素展开依次传递给函数,此时展开运算符用作函数的实参,实际上也可以用作函数的形参部分,此时这个参数称为该函数的可变参数

在JavaScript中,可变参数可以接收任意数量实参,并将他们统一存储到一个数组中返回,可变参数的作用和arguments基本是一致,但是也具有一些不同点:

  1. 可变参数的名字可以自己指定(即形参名)
  2. 可变参数就是一个数组,可以直接使用数组的方法
  3. 可变参数可以配合其他参数一起使用

所以上面的代码可以改为:

JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
function sum2(...num) {
    // 调用数组方法
    return num.reduce((a, b) => a + b, 0);
}

// 计算两个数的和
let ret = sum2(1,2);
// 计算四个数的和
let ret1 = sum2(1,2,3,4);

console.log(ret, ret1); // 3 10

需要注意,因为可变参数可以与普通参数一起使用,所以一旦一起使用,与Java的可变参数一样,可变参数必须作为最后一个参数,此处不再演示

函数的方法

在JavaScript中,函数也是一个对象,所以其也有对应的方法,下面介绍其中的方法

call()apply()方法

call()apply()方法,二者的共同点都是调用函数,并且可以使用第一个参数指定函数的this,但是对于call()函数来说,第一个参数之后的参数只需要一个一个列出来就会作为函数的实参传递,而对于apply()函数来说,第一个参数之后的参数需要放在一个数组中后再传递给函数,例如下面的代码:

JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
const obj = { name: "mark"}

function fn2(a, b) {
    console.log("a =", a, "b =", b, this);
}

// call直接列出需要传递的实际参数
fn2.call(obj, "hello", true);
// apply需要将实际参数放进数组中再传递
fn2.apply(obj, ["hello", true]);

bind()方法

bind()是函数的方法,可以用来创建一个新的函数,其主要作用如下:

  1. bind()可以为新函数绑定this
  2. bind()可以为新函数绑定参数值

该方法会返回绑定的新函数

Note

bind()方法会为新函数绑定this和参数值,所以一旦绑定,不论新函数之后是调用call()方法还是apply()方法都不能再修改其中的this和参数值

例如下面的代码:

JavaScript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
const obj = {name: "mark"}

function fn(a, b, c) {
    console.log(a, b, c, this);
}

let fn1 = fn.bind(obj, 10, 20, 30);
fn1(); // 10 20 30 { name: 'mark' }

// 新函数无法通过call()或者apply()修改this或者参数值
const obj1 = {name: "peter"}

fn1.call(obj1, 40, 50, 60); // 10 20 30 { name: 'mark' }
fn1.apply(obj1, [40, 50, 60]); // 10 20 30 { name: 'mark' }