使用JavaScript的内置数组函数

166 阅读11分钟

简介

在使用任何一种编程语言时,你可能会需要一些没有被原生集成到该语言中的功能。因此,你要么自己实现它们,要么转向使用各种模块或库。

这直接影响了你的应用程序的效率(更多的内存使用,更多的HTTP请求,等等)。为了避免这种情况,从事先进的编程语言的开发人员在语言中集成了一些功能,以帮助避免在常用任务中使用外部库。

熟悉这些内置函数被认为是一种语言的基本知识,而且仅靠内置函数你仍然可以走得很远。当然,你很可能最终会使用一些模块/库来完成某些任务。

在这个面向初学者的指南中,我们将看一下与数组有关的JavaScript的内置函数。

JavaScript的数据类型、结构和对象的内置函数

在JavaScript中,有八种数据类型

  1. 字符串
  2. 数字
  3. 布尔型
  4. 空值
  5. 未定义
  6. 符号
  7. 大英数
  8. 对象

然而,并不是每个数据类型都有一个内置函数。它们只被定义在字符串、数字和布尔

说到JavaScript中的数据结构最常用的七个结构是。

  1. 数组
  2. 堆栈
  3. 队列
  4. 链接列表
  5. 树状结构
  6. 哈希表

与数据类型类似,在数据结构中,内置函数只在数组中定义。最后,JavaScript中的对象也有内置函数,如Date、RegExp和Math

在本指南中,我们将特别关注数组。

JavaScript中的内置字符串函数

JavaScript中的数组是一个全局的、类似列表的对象。它被用来存储各种类型的数据。JavaScript的数组元素不一定是同一类型的,它们可以是任意的。列表式数据结构的这种特性也被称为异质性--数组是异质的

数组是基于索引的,从0 ,这是各种编程语言的标准实现。

let myArray1 = [x1, x2, ... , xN];
let myArray2 = new Array(x1, x2, ... , xN);
let myArray3 = Array(x1, x2, ... , xN);

鉴于数组在日常工作中的普遍使用,熟悉用于添加、删除和以其他方式处理或操作数组中的元素的函数被认为是基本知识。

push()

push(element) 函数将新的element 添加到数组的末端。

let array = [1, 2, 3, 4];
array.push(5);
   
console.log(array); // Output: [1, 2, 3, 4, 5]

通常情况下,push() 动作与堆栈和队列有关--而不是数组,不过在JavaScript的实现中,这个操作被戏称为push()

为什么呢?

数组是为了尽可能的通用,实际上你可以在JavaScript中使用数组来实现QueueStack ,因为这些不是内置的类型,你必须自己实现它们或者使用外部库。

用*push()*合并数组

此外,使用push() 函数和spread运算符一起,你可以将多个数组合并在一起。

let array1 = [1, 2, 3, 4];
let array2 = [5, 6, 7, 8]
array1.push(...array2);
   
console.log(array1); // Output: [ 1, 2, 3, 4, 5, 6, 7, 8 ]

pop()

pop() 可以用来删除一个数组的最后一个元素。与 一起, 是用于实现队列和堆栈的三个整体方法之一,也可以在该背景之外使用。push() pop()

let array = [1, 2, 3, 4];
let x = array.pop();

console.log(x); // Output: 4
console.log(array); // Output: [1, 2, 3]

pop() 函数返回弹出的元素,所以你可以为其他任何目的重新使用它。例如,你可以将pop() 元素直接放入一个新的数组或其他数据结构中,或将其保存到数据库中。

shift()

shift() 从数组中取出第一个元素并返回--与 基本上是相反的。通过这三个函数,你可以实现pop()**FIFO(先进先出)LIFO(后进先出)**结构。

let array = [1, 2, 3];
let x = array.shift();
    
console.log(x); // Output: 1
console.log(array); // Output: [2, 3]

sort()

sort() 函数对一个数组的元素进行排序,基于它们的自然升序。

什么是自然顺序?

根据数据类型的不同 - 自然顺序包含不同的含义。对于Number 实例,它们可以通过<,> 和类似的比较操作符进行比较。字符串是按字母顺序比较的。

异质数组是分批排序的--类型为Number 的元素被排序,其次是类型为String 的元素,然后是自定义对象。

值得注意的是,sort() 对数组进行就地排序,改变原来的数组,所以如果你想保留原来的数组--你必须进行深度拷贝,而不是仅仅保留一个引用,因为这个引用也会指向改变后的数组。

let array1 = [1, 3, 2, 0];
let array2 = ["JavaScript", "Java", "Python"];
let array3 = ["b", 3, 1, "c", "a"];

let originalArray1 = [...array1];
   
console.log('Sorted array1:', array1.sort());
console.log('Sorted array2:', array2.sort());
console.log('Sorted array3:', array3.sort());

console.log('Original array1:', originalArray1);

这就导致了:

Sorted array1: [ 0, 1, 2, 3 ]
Sorted array2: [ 'Java', 'JavaScript', 'Python' ]
Sorted array3: [ 1, 3, 'a', 'b', 'c' ]
Original array1: [ 1, 3, 2, 0 ]

你也可以向sort() ,提供一个你自己实现的排序函数,覆盖默认行为。该函数应该接受两个参数,并根据它们的比较返回1,0-1

如果第一个值小于第二个值,则返回1 。如果第一个值大于第二个值,则返回-1 。如果它们是相同的,则返回0

根据这个L

  • 如果函数返回一个大于0的值--第二个元素被排序第一个之前
  • 如果函数返回的值小于0--第一个元素被排序第二个之前。
  • 如果函数返回值为0--这些是相等的,并保持它们的相对顺序。

让我们实现一个自定义函数,按相反的顺序对字符串进行排序,而不是按字母顺序。为了实现这一点--我们将调换返回值,如果第一个值大于第二个值,则返回1 ,而不是反过来。

let array = ["JavaScript", "Java", "Python"];

console.log('Custom sort:', array.sort(customSort));
console.log('Default sort:', array.sort());

function customSort(a, b) {
    if (a < b) {
        return 1;
    }
    if (a > b) {
        return -1;
    }
    return 0;
}

这就导致了:

Custom sort: [ 'Python', 'JavaScript', 'Java' ]
Default sort: [ 'Java', 'JavaScript', 'Python' ]

slice()

slice(start, end) 函数返回索引值start 和索引值end-1 之间的数组的一部分。它通过各种方式一个数组切片,并返回一个新的数组,由元素组成。

原有的数组则保持不变

let array = [10, 20, 30, 40, 50];
let subarray = array.slice(2, 4);
   
console.log(array);    // Output: [ 10, 20, 30, 40, 50 ]
console.log(subarray); // Output: [30, 40]

slice() 对数组来说,就像 对字符串一样--它是一个真正常用的函数,用于截断或子阵列的某些序列。substring()

拼接()

splice(start, deleteCount, item) 函数用于替换和删除数组中的元素,以及插入它们。它的边界从start 开始,并删除deleteCount 元素,如果提供的话,可以选择用item 或多个元素来替换它们。

此外,它还返回被删除的元素,如果你愿意,可以保存这些元素。

let array = [10, 20, 30, 40, 50];
let splicedArray = array.splice(3, 2, 'newElement');


console.log('Spliced elements: ', splicedArray);
console.log('Changed array: ', array);

在这里,splice() 函数从第3个元素开始(基于0的索引),并删除下面的两个元素,用一个newElement 来代替它们。被删除的元素被保存在splicedArray 数组中。

Spliced elements:  [ 40, 50 ]
Changed array:  [ 10, 20, 30, 'newElement' ]

如果没有可选的item 或多个项目,你实际上可以使用splice() ,有点类似于slice() ,但同时,实际上是从数组中删除元素。

let array = [10, 20, 30, 40, 50];
let splicedArray = array.splice(3, 2);

console.log('Spliced elements: ', splicedArray);
console.log('Changed array: ', array);

这就导致了:

Spliced elements:  [ 40, 50 ]
Changed array:  [ 10, 20, 30 ]

reverse()

reverse()顾名思义,就是颠倒数组中元素的顺序。

let array = [1, 2, 3];
   
console.log(array.reverse()) // Output: [3, 2, 1]

注意:reverse()方法是就地反转数组的。这意味着原来的num_array和string_array被颠倒了,原来的顺序被丢失。

尽管它是就地完成的,但通常还是要将操作的结果 "分配 "给一个新的变量,以至少表示一个反转的数组。

let array = [1, 2, 3];
let arrayReversed = array.reverse();
   
console.log(arrayReversed ) // Output: [3, 2, 1]

map()

map(f) 函数将函数f 应用于数组中每个元素的副本。当你想把元素映射到不同的集合时,这个函数非常有用,比如把用户映射到他们的ID或者把元素映射到一个类别。

let array = ["Java", "Python", "JavaScript"];

let langLengths = array.map(function(x){
    return x.length;
});

console.log(langLengths);

在这个代码片断中,我们映射列表中每个字符串的长度,产生:

[ 4, 6, 10 ]

如果你想把语言名称和它们的长度放在一起,你会想把结果存储在一个字典里,它可以容纳键值对。

let array = ["Java", "Python", "JavaScript"];
let mapping = Object.assign({}, ...array.map((x) => ({[x]: x.length})));

console.log(mapping);

这就产生了:

{ Java: 4, Python: 6, JavaScript: 10 }

forEach()

forEach(f) 数组中的每个元素应用函数 。 和 的区别是: 创建了一个新的数组,并没有改变原来的数组,而 则改变了原来的数组。f map forEach map forEach

let languageArray = ["Java", "JavaScript", "Python"];

console.log("Printing each element: \n______");
// Print each element
languageArray.forEach(element => console.log(element));

console.log("\nPrinting each element in uppercase: \n______");
// Print uppercase version of each element, while keeping original strings intact
languageArray.forEach(element => console.log(element.toUpperCase()));

// Change the original array, changing all elements to lowercase
languageArray.forEach(function(element, index, array){
    array[index] = array[index].toLowerCase();
});
console.log("\nEach element converted to lowercase: \n______");
console.log(languageArray);

即使你不使用element ,也需要定义它,比如在上一个例子中。 这些结果是:

Printing each element: 
______
Java
JavaScript
Python

Printing each element in uppercase: 
______
JAVA
JAVASCRIPT
PYTHON

Each element converted to lowercase: 
______
[ 'java', 'javascript', 'python' ]

join()

join() 方法将一个数组的所有元素连接成一个字符串,根据元素的类型,将其转换为字符串表示。数字很容易转换为字符串,但是对于自定义对象,toString() 方法被调用以返回字符串表示。

此外,在连接时--默认的分隔符是逗号,产生类似CSV的格式。然而,你可以定义任何字符作为分隔符,只需将其传入函数即可。

让我们从更简单的类型开始。

let array = [1, 2, "hello"];
let str1 = array.join();
let str2 = array.join('');
let str3 = array.join('_');


console.log('Result: ', str1);
console.log('Result: ', str2);
console.log('Result: ', str3);
console.log('Type of result: ', typeof(str1));

数字很容易转换为字符串,并根据我们定义的分隔符连接起来。

Result: 1,2,hello
Result: 12hello
Result: 1_2_hello
Type of result: string

但是当处理自定义对象时,转换为字符串将导致一个对象引用,除非定义了一个有效的toString() 方法,该方法返回一个字符串表示。在这种情况下,让我们定义一个User 类,用一个toString() 来返回用户的名字。

class User {
    /** @access private */
   #name;
    
    constructor(name){
        this.#name = name;
    }
    
    getName() {
        return this.#name;
    }
    
    setName(name) {
        this.#name = name;
    }

    toString() {
      return this.#name;
    }
}


let john = new User("John");
let maria = new User("Maria");

let array = [john, maria, "hello"];
let str = array.join();

console.log('Result: ', str);
console.log(typeof('Type of result: ', str));

这就导致了:

Result: John,Maria,hello
Type of result: string

every()

every(p) p如果数组中的每个元素满足传递的谓词,则返回 。true

谓词只不过是一个接受变量的函数,并返回truefalse

为此,你可以轻松地创建匿名函数(甚至是显式函数),根据你提供的变量返回一个布尔值。例如,你可以检查一个列表中的every() 元素是否大于0 ,或者是否包含某些值。

let simpleArray = [1, 2, 3];
console.log(simpleArray.every(x => x > 0)); // Output: true

let objectArray = [new User('John'), new User('Maria')];
console.log(objectArray.every(x => x.age > 21));

some()

some(p) 如果任何元素满足传递的谓词 ,则返回真。p

let a = [1, 2, 3];
    
console.log(a.some(x => x == 2)); // Output: true

filter()

filter(p) 返回一个新的数组,该数组由满足通过的谓词 的元素组成。没有通过的元素(函数返回 )在过滤后不被包括在内。p false

let a = [1, 2, 3];
    
console.log(a.every(x => x > 1)); // Output: [2, 3]

indexOf()lastIndexOf()

indexOf()lastIndexOf() 函数接受一个元素,如果在数组中存在,返回它在序列中的索引。如果不存在--则返回-1

如果存在多个与提供的元素相匹配的元素--只返回第一个元素的索引。

let simpleArray = [1, 4, 5, 4, 5, 6, 5, 8];

console.log(simpleArray.indexOf(5));
console.log(simpleArray.indexOf(10));

这就导致了:

2
-1

同样,lastIndexOf() 方法向后迭代,并返回最后出现的,而不是第一个出现的匹配元素。

let simpleArray = [1, 4, 5, 4, 5, 6, 5, 8];

console.log(simpleArray.lastIndexOf(5));
console.log(simpleArray.lastIndexOf(10));

这样的结果是:

6
-1

此外,你可以为lastIndexOf() indexOf() 函数提供一个可选的起点,这两个函数都是基于0。

let simpleArray = [1, 4, 5, 4, 5, 6, 5, 8];

console.log(simpleArray.lastIndexOf(5, 3));
console.log(simpleArray.indexOf(5, 5));

lastIndexOf() 不从数组的末端开始--从元素8 。它从索引为3的元素开始,也就是这个数组中的第二个元素4indexOf() 不是从数组的开始,而是从索引为5的元素开始。

//                          ↓ lastIndexOf() start
let simpleArray = [1, 4, 5, 4, 5, 6, 5, 8];
//                                ↑ indexOf() start

鉴于起点的变化,这就导致了:

2
6

总结

在本指南中,我们看了一些与JavaScript中数组有关的最常用的内置函数。鉴于Arrays在日常工作中的普遍性--熟悉这些函数是任何一个新的开发者都必须要做的。