JS-Array

一、JS没有“真正的数组”

像C++,Java这些编程语言中数组元素分派的内存都是一连,这有利于机能提拔,然则JS的数组不是如许的。它运用对象模仿数组,即对象属性为数字,并含有length属性。所以JS数组对象的内存不是一连的,同平常对象内存分派。

二、建立数组

2.1 字面量体式格局

var a = [], // 定义空数组
    b = [1,true], // 数组元素用逗号离隔
    c = [1,,3], // 疏忽中心的元素
    d = [1,2,]; // 末端是逗号

注重:

  1. 数组元素范例能够各不相同,由于JS数组实质是个对象;
  2. b[1]元素是未定义的,不是取值是undefined的元素;
  3. 数组d的长度是2,不是3,由于字面量准予末端是逗号。

2.2 运用组织函数Array

var a = new Array(), // 等价 []
    b = new Array(2), // 等价 [,,], 注重这里是两个逗号哦
    c = new Array(1,2), // 等价 [1, 2]
    d = Array(1,2); // 等价于new Array(1,2)

注重:

  1. 运用组织函数Array比较坑的就是差别数目的参数,Array函数的行动不一致。
  2. Array即是组织函数也是工场函数。即new Array() 等价于直接挪用Array();

三、索引和属性称号

接见对象的属性是经由过程属性称号,而接见数组的元素则是经由过程索引。索引即为数组元素的下标。索引是32位的正整数,有用取值局限是[0, 2^32 – 2](由于数组的length属性也是32位整数,一切下标最大为2^32-2),不在这个局限的值都不是索引。虽然JS没有整数范例,但索引的操纵都是依据32位正整数体式格局处置惩罚的。数组实质也是对象,也是能够经由过程属性称号的体式格局接见数组的属性。

var a = [1,2],
    b = {
        0: 1,
        1: 2
    };

console.log(a[1]); // 索引接见体式格局
console.log(a['1']); // 索引接见体式格局,会把'1'转成正整数1
console.log(b[1]); // 属性称号接见体式格局,会把1转成字符串“1”

注重:

  1. 索引是一种特别的属性称号;
  2. 属性称号体式格局会把中括号里的表达式转成字符串,索引体式格局会把中括号里的表达式转成32整数,假如不是正当的索引,则视为属性称号,所以JS数组不存在下标越界的问题

3.2 希罕数组

JS数组元素不一定是一连的,索引位置上没有元素(没有元素取值是undefined的元素是差别的)的数组叫希罕数组。

var a = [,], // 定义即为希罕数组
    b = Array(3), // 定义即为希罕数组
    c = [1,2,3];
delete c[1]; // delete操纵形成希罕

注重:

  1. 再强调索引位置上没有元素跟取值是undefined的元素不一样的(有些数组的要领, 运算符的行动不一样)。
for(var a in [,]) {console.log(1)} // 没有轮回
for(var a in [1,2]) {console.log(1)} // 轮回2次

四、length属性

4.1 length属性

length属性示意“数组元素的数目”,JS的数组元素并非一连的,有些索引的位置能够没有元素,所以length属性并不能真正示意元素的数目,其值即是数组最大索引+1。而且length属性是可写的

var arr = [1];
arr.length; // 1
arr.length = 3; // 增大length属性值
arr.length;// 3, 索引1,2位置是未定义的元素。
arr.length = 0; // 减小length属性值
arr[0]; //undefined

4.2 伪数组

行动有点像数组的对象叫伪数组。广义上只需含有length属性且length值是在索引有用取值局限内(或能够经由过程范例转换成有用索引)的对象都能够视为伪数组。伪数组能够运用数组的一些要领,也能够反过来定义:能够运用数组要领的对象叫伪数组对象。

var a = {length: 2}; // a是伪数组
Array.prototype.slice.call({length: 2}); // 能够运用slice要领,其效果等价于Array(2)的效果

五、要领

5.1 ES3

主如果操纵元素相干的要领

1. push/pop

2. unshift/shift

之前一向殽杂unshift和shift的功用。平常都记得push是向数组尾部插进去数据,pop是从数组尾部弹出元素,能够借助push/pop影象unshift/shift。push名字比pop长,而unshift名字也比shift长。即push和unshift功用相似,而且名字都比对应功用的要领pop/shift名字长。长对长,短对短,预计再也不会殽杂unshift和shift要领的功用了。

3. join

近来看到某个框架源码有这么个片断:

var indent = '';
for (i = 0; i < space; i += 1) {
    indent += ' ';
  }

也许意义就是依据参数space天生指定长度的空格字符串。能够经由过程join要领革新下哈:

 var indent = Array(space + 1).join(' '); // 记得+1,不然字符串长度少1

join要领会把值为undefined/null的数组元素转成空字符串。

4. reverse

5. sort

6. concat

一向认为该要领用于多个数组兼并,实在除了的功用外还能够把非数组范例的参数插进去返回值数组里。

var a = [1, 2];
a.concat([3, 4]) // [1, 2, 3, 4]
a.concat(3, 4) //  [1, 2, 3, 4]

7. slice

8. splice

splice要领能够完成对数组恣意位置,恣意数目的元素举行增添,替代,删除操纵。

var a = [1, 2, 3, 4, 5];
// 替代:将元素2,3替代成10,11
a.splice(1, 2, 10, 11)
console.log(a) // [1, 10, 11, 4, 5]

// 删除:删除10,11
a.splice(1, 2) // [1, 4, 5]

// 插进去:在元素4背面插进去元素22,23,24
a.splice(2, 0, 22, 23, 24)
console.log(a) // [1, 4, 22, 23, 24, 5]
  1. splice的返回值是被删除或许替代的元素的鸠合
  2. 大部分状况我们常常对数组的首尾举行增加删除操纵,所以平常运用push/pop, unshift/shift要领多些。

5.2 ES5

主如果遍历和基于遍历的搜刮、诊断相干的要领

1. forEach

2. map

3. filter

4. every/some

5. reduce/reduceRight

  • 是不是指定初始值轮回的次数不一样的
  • reduce运用场景许多,仔细看下MDN Demos,另有这个面试题

改成纯Promise版:

function genTask(action, delay, context) {
    return function() {
        return new Promise(resolve => {          
             action && action.call(context);
             setTimeout(resolve, delay == null ? 0 : (delay * 1000))
        })
    }
}

function machine(name) {
    var tasks = [];
    tasks.push(genTask(function() {
        console.log(`start ${name}`)
    }))
    function execute() {
        var self = this;
        tasks.reduce((promise, task) => {
            return promise.then(task)
        }, Promise.resolve())
    }
    function _do(task) {       
        tasks.push(genTask(function() {
            console.log(`${name} ${task}`)
        }))
        return this
    }

    function wait(delay) {
        tasks.push(genTask(() => {
            console.log(`wait ${delay}s`);
        }, delay, null))
        return this
    }
    
    function waitFirst(delay) {
        tasks.unshift(genTask(() => {
            console.log(`wait ${delay}s`);
        }, delay, null))
        return this
    }

    return {
        name: name,
        execute: execute,
        do: _do,
        wait: wait,
        waitFirst: waitFirst
    }
}

machine('ygy')
.waitFirst(3)
.do('eat')
.execute();
function fetch(x) {
  return new Promise((resolve, reject) => {
      console.log(x)
    setTimeout(() => {
      resolve(x)
    }, 500 * x)
  })
}

async function test() {
    let arr = [3, 2, 1]
    await arr.reduce(async (promise, item) => { 
      await promise;
      console.log(item)
      return await fetch(item);
    }, Promise.resolve())
    console.log('end')
}
test();

6. indexOf/lastIndexOf

采纳相对相称(===)的推断逻辑。

7. Array.isArray

注重:

  • forEach, map, filter, every/some, reduce/reduceRights, indexOf/lastIndexOf都邑有遍历数组的行动,能够依据差别的需求选用不必的遍历要领,而且都不会遍历数组中被删除或从未被赋值的元素,见希罕数组;
  • 有人尝试把async函数作为上述数组具有遍历功用的回调函数,但能够获得意想不到的效果,比方这个重学 JS:为啥 await 在 forEach 中不见效。不仅仅是forEach,其他的遍历要领也都只处置惩罚同步代码。

异步函数的返回值是个Promise对象,相当于这些遍历要领现实在操纵Promise对象。

5.3 ES6

主如果增加了新的功用,让数组运用的越发轻易

1. copyWithin

2. entries

3. fill

3. find

4. findIndex

功用相似ES5点indexOf,参数差别,是indexOf的加强版:更天真,运用回调函数能够更天真的掌握相称推断逻辑

5. includes

推断数组是不是包括指定的元素,在此之前我们平常借助indexOf要领的返回只是不是为-1推断元素是不是存在():

var a = [1, 2, 4];
a.indexOf(1) !== -1 // true, 存在
a.indexOf(6) !== -1 // false, 不存在

ES6引入includes要领特地用来推断元素是不是存在,而且采纳的是0值相称的等值推断算法,indexOf要领采纳的相对相称算法。

var a = [1, NaN];
a.includes(NaN) // true, 存在
a.indexOf(NaN) !== -1 // false, 不存在

6. keys

7. values

8. [Symbol.iterator]

9. [Symbol.species]

10. Array.of

我们都晓得Array的组织函数依据的参数数目的差别具有差别的行动:

Array(7);          // 一个参数示意数组的长度:构建长度为7的数组
Array(1, 2, 3);    // 多个参数示意数组的元素:构建数组为[1, 2, 3]

Array.of要领一致了这类行动,都是用来依据元素构建数组:

Array.of(7);       // [7] 
Array.of(1, 2, 3); // [1, 2, 3]

11. Array.from

参考

  1. MDN Array
  2. MDN Array.prototype
    原文作者:普拉斯
    原文地址: https://segmentfault.com/a/1190000012897239
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞