欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JavaScript -- Array进阶详解

程序员文章站 2022-03-07 21:36:01
测试宿主环境均为Node.js文章目录前言一、创建数组二、使用步骤1.引入库2.读入数据总结前言除了Object之外,Array应该ECMAScript中最常用的类型了,但和其他语言不一样,ECMAScript中的数组长度是可变的,而且由于ECMAScript的弱类型,这也使得数组Array中存取的类型可以是混杂的,例如数组的第一个元素可能是number类型,第二个可能是string类型,第三个可能又是Object,但通常我们不会这么做,更多地是使用统一的规范一、创建数组我们来看两种有意思的...

如没有特别说明,测试宿主环境为Node.js


前言

除了Object之外,Array应该ECMAScript中最常用的类型了,但和其他语言不一样,ECMAScript中的数组长度是可变的,而且由于ECMAScript的弱类型,这也使得数组Array中存取的类型可以是混杂的,例如数组的第一个元素可能是number类型,第二个可能是string类型,第三个可能又是Object,但通常我们不会这么做,更多地是使用统一的规范


一、逗号创建数组

我们来看两种有意思几种的逗号数组创建方式:

  1. 第一种

    var arr = [,,,]; 		   //创建包含三个空位的数组
    console.log(arr.length);   // => 3
    console.log(arr);		   // => [ <3 empty items> ]
    
  2. 第二种

    var arr = [2,,,6]; 		   //创建包含四个空位的数组
    console.log(arr.length);   // => 4
    console.log(arr);		   // => [ 2, <2 empty items>, 6 ]
    
  3. 第三种

    var arr = [2,,,]; 		   //创建包含三个空位的数组
    console.log(arr.length);   // => 3
    console.log(arr);		   // => [ 2, <2 empty items> ]
    
  4. 第四种

    var arr = [,,1,]; 		   //创建包含四个空位的数组
    console.log(arr.length);   // => 3
    console.log(arr);		   // => [ <2 empty items>, 1 ]
    
  5. 第五种

    var arr = [,,,3]; 		   //创建包含四个空位的数组
    console.log(arr.length);   // => 4
    console.log(arr);		   // => [ <3 empty items>, 3 ]
    

不知道你所预期的结果和实际输出的有没有区别,得到这样结果的原因是从ES5规范开始就允许在列表(数组值,属性列表)末尾多加一个逗号(在实际处理中会被忽略不计)

那如何简便判断数组的length呢?

如果你读懂了上文的解释,你或许猜到了,如果数组最后一个是, 控制台输出会忽略这个, 然后正常的思维计算数组长度,现在利用这个规律返回看看上面的五个示例,得到的结果是不是这样的呢?

数组创建的静态方法:
关于数组创建,这里我引入Array.from()Array.of()两个方法

  1. Array.from()
    //字符串会被拆分为单字符数组
    console.log(Array.from("hello"));   	// => ["h","e","l","l","o"];
    
    //对现有数组进行复制
    const arr1 = [1,2,3,4,5];
    const console.log(Array.from(arr1));    // => [1,2,3,4,5]
    console.log(arr1 === arr2);  			// => flase
    
    //还可接受第二个可选的映射函数参数,这个函数可以直接增强新数组的值,类似于调用函数map()
    const arr3 = [2,3,4,5];
    console.log(Array.from(arr3,x => x**2)); // =>[4,9,14,25]  注:** 相当于执行了Math.pow()函数
    
  2. Array.of()
    这个函数主要用在把一组参数转换为数组
    //以前我们通常会用如下函数返回一系列参数组成的数组
    function fn() {
        console.log(Array.prototype.slice.call(arguments));
    }
    fn(1, 2, 3, 4); // => [1,2,3,4]
    //但现在我们有了更简便的方法
    console.log(Array.of(1,2,3,4)); // => [1,2,3,4]
    

二、容易忽略的.length

JavaScript的数组长度length具有一些其他语言没有的特性

  1. 数组长度可变,可以更改length的值来修改数组长度
    let arr = ["a","b","c","d"];
    arr.length = 5;
    console.log(arr); // =>  ["a","b","c","d",undefined,underfined]; 注:未定义的单元用undefined填充
    arr.lengeh = 2;
    console.log(arr); // =>  ["a","b"]; 注:缩小数组长度会覆盖以前的以定义的单元
    
  2. 插入问题,设置某位置的值,有可能会改变数组长度,哪怕设置为undefined
    let arr = [2,4,6,8];
    console.log(arr); // => [ 2, 4, 6, 8 ]
    console.log(arr.length); // => 4
    
    arr[80] = 100;   
    console.log(arr); 	// => [ 2, 4, 6, 8, <76 empty items>, 100 ]
    console.log(arr.length); // => 81
    
    arr[99] = undefined;
    console.log(arr); 	// => [ 2, 4, 6, 8, <76 empty items>, 100, <18 empty items>, undefined ]
    console.log(arr.length); // => 100
    

三、检测数组的两种方法

说到检测数组,不管是在Java中还是JavaScript中,我们都不由想到instanceof操作符
这是正确的,你可以使用如下方法检测数组

const arr = [12,2,3,4];
console.log(arr instanceof Array);  // => true

我希望你也能知道还有一种少见但重要的检测方法,试想我们有两个框架在同一个网页中,涉及两个不同的全局执行上下文,就可能有两个不同版本的Array构造函数,如果把一个用第一个框架的声明的数组传入另一个框架,在用instanceof Array检测是否是数组,显然此时会返回false

因为 instanceof Array操作代表的并不是像我们理解的那样判断一个对象是不是数组,而是告诉我们该对象是否是当前全局作用域下的Array的实例

为了解决这个问题,ECMAScript提供了Array.isArray()方法,这个方法的目的就是确定一个值是否为数组,如下:

let arr = ['a','b','c','d'];
if (Array.isArray(arr)) {
	console.log('yes');	
}

四、迭代器方法(返回迭代器)

我们知道,对象Object有三个静态方法Object.keys()、Object.values()、Object.entries()

同样的,ECMAScript也在Array的对象原型上为我们添加了类似方法来实现数组的迭代

  1. Array.prototype.keys()
    这个方法返回数组 索引 的迭代器
    let arr = [3,5,7,9,11];
    console.log(Array.from(arr.keys())); // => [ 0, 1, 2, 3, 4 ]
    
  2. Array.prototype.values()
    这个方法返回数组 值 的迭代器
    let arr = [3,5,7,9,11];
    console.log(Array.from(arr.values()));  // => [ 3, 5, 7, 9, 11 ]
    
  3. Array.prototype.entries()
    这个方法返回数组 索引/值 对的迭代器
    let arr = [3,5,7,9,11];
    console.log(Array.from(arr.entries()));  // => [ [ 0, 3 ], [ 1, 5 ], [ 2, 7 ], [ 3, 9 ], [ 4, 11 ] ]
    

对,如你所想,Array.from()方法还可以接收迭代器,转为数组


五、迭代方法(返回boolean值或者Array对象)

你可能意识到了,上面的三种迭代器方法方法只能帮我们输出数组的内容,并不能做一些判断和操作,那么接下来的五个迭代方法将会对你操作数组提供更便捷的方式,传入的第一个参数为 断言函数,在某些方法中通常简写为匿名函数,如下就是采用匿名函数的形式,断言函数的三个参数分别为 值 、索引、该数组本身

  1. every():

对数组的每一项都运行传入的参数,如果对每一项函数都返回true,则这个方法返回true
像every翻译过来一样(全部),我们可以用这个方法来判断某个数组的所有值是否都满足某个条件,例如

let arr = [24,56,87,33,45];
console.log(arr.every((item, index, array) => item > 10));  //=> true  注:5个值都大于10,返回true
  1. some():

对数组的每一项都运行传入的参数, 如果有一项函数返回true,则这个方法返回true
像some翻译过来一样(某些),我们可以用这个方法来判断某个数组的某个值都满足某个条件

let arr = [24,56,87,33,45];
console.log(arr.some((item, index, array) => item > 80));  //=> true  注:87大于80,返回true
  1. filter():

对数组的每一项都运行传入的参数,函数返回true的项将会组成数组后返回
像filter翻译过来一样(过滤器),我们只挑选符合我们需求的值组成数组并返回

let arr = [24,56,87,33,45];
console.log(arr.filter((item, index, array) => item > 35));  //=> [56, 87, 45]  注:返回大于35的值组成的数组
  1. forEach():

对数组的每一项都运行传入的参数,类似于for循环,只是forEach会将每一项函数返回值组成数组返回

let arr = [24, 56, 87, 33, 45];
console.log(arr.filter((item, index, array) => arr[index])); //=>[ 24, 56, 87, 33, 45 ]

//等价于
let arr_2 = [];
for (let i = 0; i < arr.length; i++) {
    arr_2.push(arr[i]));  
}
console.log(arr_2);   //=>[ 24, 56, 87, 33, 45 ]
  1. map():

对数组的每一项都运行传入的参数,将每一项函数return返回的值组成数组后返回
可用此方法对数组的值进行操作改变后返回

let arr = [24, 56, 87, 33, 45];
console.log(arr.map(item, index, array) => item * 2);  //=>[ 48, 112, 174, 66, 90 ]

尽管有了如上的迭代器方法,可以很容易对数组进行我们想要的的操作,但我们还是感到对数组某些方面没有足够边便捷的操作,例如如何快速在头部和尾部添加值或者删减值(你可能注意到上文用了一次push函数),又或者如何对数组进行快速整齐地赋值,接下来两节着重围绕此进行说明


六、复制和填充方法

ES6新增了两个批量复制和填充数组的方法:

  1. Array.prototype.fill(value[, start[, end]])
    //一个参数时,默认value填充数组所有元素
    let arr = [1,2,3,4,5,6];
    arr.fill(5);
    console.log(arr); // =>[5,5,5,5,5,5]; 注:全被覆盖-
    
    //两个参数时,使用value填充大于等于start的元素
    let arr = [48653];
    arr.fill(2,5);
    console.log(arr); // =>[4,2,2,2,3]; 注:8,6,5都大于等于5,被覆盖
    
    //三个参数时,使用value填充大于等于start,小于等于end的元素
    let arr = [48653];
    arr.fill(2,5,6);
    console.log(arr); // =>[4,8,2,2,3]; 注:6,5都大于等于5,小于等于6,被覆盖
    
  2. Array.prototype.copyWithin(target[, start[, end]])
    //一个参数时,复制索引0的开始的内容,插值到索引start开始的地方
    let arr = [1,2,3,4,5,6];
    arr.copyWithin(2);
    console.log(arr); // =>[ 1, 2, 1, 2, 3, 4 ]; //注:复制索引0的开始的内容,插值到索引为2开始的地方
    
    //两个参数时,复制索引start的开始的内容,插值到索引target开始的内容
    let arr = [1, 2, 3, 4, 5, 6];
    arr.copyWithin(2, 3);
    console.log(arr); // =>[ 1, 2, 4, 5, 6, 6 ];  // 注:复制4,5,6,插值到下标为2开始的位置
    
    //三个参数时,复制索大于等于start,小于end的内容,插值到索引target开始的地方
    let arr = [1, 2, 3, 4, 5, 6];
    arr.copyWithin(2, 3, 5);
    console.log(arr); // =>[ 1, 2, 4, 5, 5, 6 ];  注:复制4,5,插入到索引值为2的地方
    

copyWithin()和fill()的参数满足以下条件

  • 负值索引从数组末尾开始计算
  • 静默忽略超出数组边界,0长度及方向反向的索引范围
  • 索引值有部分可用,则用该可用的部分

七、栈与队列方法

  1. Array.protype.push()
    //从数组尾部添加元素,返回新数组长度
    let arr = [1,2,3,4];
    console.log(arr.push(5,6)); // => 6 
    console.log(arr);	// => [1,2,3,4,5,6];
    
  2. Array.protype.pop()
    //从数组尾部删除一个元素,返回该删除的元素
    let arr = ["a","b","c","d"];
    console.log(arr.pop()); // => "d"
    console.log(arr);	// => ["a","b","c"];
    
  3. Array.protype.unshift()
    //从数组头部添加元素,返回新数组长度
    let arr = [1,2,3,4];
    console.log(arr.unshift(7,8,9)); // => 7
    console.log(arr);	// => [7,8,9,1,2,3];
    
  4. Array.protype.shift()
    	//从数组头部删除一个元素,返回该删除的元素
    let arr = [1,2,3,4];
    console.log(arr.shift()); // => 1 
    console.log(arr);	// => [2,3,4];
    

到现在应该算了解了操作数组的大部分方法,我们已经能对数组内部元素做许多操作了,接下来更深入了解数组本身的一些基本操作,如数组翻转,排序和数组之间的连接


八、翻转与排序

本文地址:https://blog.csdn.net/JKR10000/article/details/109158515