js数组 学习笔记

248 阅读12分钟

数组

数组是值的有序集合。每个值叫做一个元素,而每个元素在数组中有一个位置,以数字表示,称为索引。

JS数组是JS对象的特殊形式,数组索引实际上和碰巧是整数的属性名差不多。使用方括号[]访问数组元素就像用方括号访问对象的属性一样。

数组的特别之处在于,当使用小于2^{32}的非负整数作为属性名时数组会自动维护其length属性值

var a = [];             //length = 0
a[0] = 1;               //length = 1
a[-1.2] = "-1.2";       //length = 1
console.log(a);         //[1, -1.2: "-1.2"];

创建数组

字面量创建数组
var empty = [];
var primes = [2,3,4,5];
var count = [1,,3];     //数组有3个元素,中间的那个元素值为undefined
var undefs = [,,];      //数组有2个元素,都为undefined

数组直接量的语法允许有可选的结尾的逗号,故[,,]只有两个元素而非三个

构造函数Array()创建数组
var a = new Array();                            //等同于var a = [];
var a = new Array(10);                          //数组中没有存储值,甚至数组的索引属性“0”,“1”等还未定义
var a = new Array(5,4,3,2,1,"test");            //等同于var a = [5,4,3,2,1,"test"];

稀疏数组

稀疏数组就是包含从0开始的不连续索引的数组。如果数组是稀疏数组,length的属性值大于元素的个数。

var tmp = new Array(5);       //数组没有元素,但tmp.length = 5
var a = [];                 //创建一个空数组,length = 0
a[1000] = 0;                //赋值添加一个元素,但是设置length = 1001

注意,在数组直接量中省略值时不会创建稀疏数组。省略的元素在数组中是存在的,其值是undefined

var a1 = [,,,];             
var a2 = new Array(3);
0 in a1;                    //true
0 in a2;                    //false

使用delete运算符删除数组元素是,数组会变成稀疏数组。

var a = [1,2,3];
delete a[1];
1 in a;                     //false
console.log(a.length);      //3

数组方法

数组方法

常用数组方法

join()

Array.join([separator])方法将数组中所有元素都转化为字符串并连接在一起,返回生成字符串。可指定一个可选的字符串来分隔数组的各个元素,若不指定,默认使用逗号。

Array.join()方法是String.split()方法的逆运算,后者是将字符串分割成若干块来创建数组。

var a = [1,2,3];
a.join();               //=> "1,2,3"
a.join("");             //=> "123"
a.join("-");            //=> "1-2-3"
reverse()

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

var a = [1,2,3];
a.reverse();            //现在a是 [3,2,1]
sort()

Array.sort([compareFunction])方法将数组中的元素排序并返回排序后的数组。该方法会改变原数组。当不带参数调用时,数组元素以字母表顺序排序。

var a =["ban","che","app"];
a.sort();                       //现在a是 ["app", "ban", "che"]

若需要做其他排序时,必须给sort()方法传递一个比较函数。该函数决定了它的两个参数在排好序的数组中的先后顺序。

  • 如果 compareFunction(a, b) 小于 0 ,那么 a 会被排列到 b 之前;
  • 如果 compareFunction(a, b) 等于 0 , a 和 b 的相对位置不变。
  • 如果 compareFunction(a, b) 大于 0 , b 会被排列到 a 之前。
var a = [33,4,11,22];
a.sort();                               //[11, 22, 33, 4]
a.sort(function(a,b){ return a-b; });   //[4,11, 22, 33]
a.sort(function(a,b){ return b-a; });   //[33,22,11,4]
concat()

Array.concat(value1[, value2[,...[,valueN]]])方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新的浅拷贝的数组。

var a = [1,2,3];
a.concat(4,5);              //返回[1,2,3,4,5]
a.concat([4,5]);            //返回[1,2,3,4,5]
a.concat([4,5],[6,7]);      //返回[1,2,3,4,5,6,7]
a.concat(4,[5,[6,7]]);      //返回[1,2,3,4,5,[6,7]]
slice()

Array.slice([begin[, end]])方法返回指定数组的一个片段或子数组。两个参数制定了开始和结束的位置(包括 begin位置元素,不包括end位置元素)。如果只指定一个参数,返回数组将包含从开始位置到数组结尾的所有元素。如果参数中出现负数,它表示相对于数组最后一个元素的位置。

var a = [1,2,3,4,5];
a.slice(0,3);           //返回 [1,2,3]
a.slice(3);             //返回 [4,5]
a.slice(1,-1)           //返回 [2,3,4]
splice()

Array.splice(start[, deleteCount[, item1[, item2[, ...]]]])方法是在数组中插入或删除元素的通用方法。该方法会改变数组。

  • 第一个参数指定了插入和(或)删除的起始位置。
  • 第二个参数指定了应该从数组中删除的元素个数。
  • 前两个参数指定了需要删除的数组元素。其后的任意个数的参数指定了需要插入到数组中的元素,从第一个参数指定的位置开始插入。

如果省略了第二个参数,则从起始位置开始到数组结尾的所有元素都将被删除。splice()返回一个由删除元素组成的数组,若无删除元素则返回空数组。

var a = [1,2,3,4,5,6,7,8];
a.splice(4);                    //返回 [5,6,7,8]; a为 [1,2,3,4]
a.splice(1,2);                  //返回 [2,3]; a为 [1,4]
a.splice(1,0,'a','b');          //返回 []; a为 [1,'a','b',4]
a.splice(1,2,2,3);              //返回 ['a','b']; a为 [1,2,3,4]
push()和pop()

push()pop()方法允许将数组当成栈来使用,可以在数组尾部进行元素的添加和删除操作。都会改变数组。

  • push()方法在数组的尾部添加一个或多个元素,返回数组新的长度。
  • pop()方法删除数组的最后一个元素,减少数组的长度,并返回它删除的元素
var a = [];         //a:[]
a.push(1,2);        //a:[1,2];  返回 2
a.pop();            //a:[1];    返回 3
unshift()和shift()

unshift()和shift()方法可以在数组头部进行元素的添加和删除操作。都会改变数组。

  • unshift()在数组的头部添加一个或多个元素,返回数组新的长度。
  • shift()删除数组的第一个元素并将其返回,然后把所有随后的元素下移一个位置。
var a = [1];
a.unshift(3);       //a:[3,1];  返回 2
a.shift();          //a:[1];    返回 3
a.unshift(3,4,5)    //a:[3,4,5];返回 4

注意,当使用多个参数调用unshift()时,参数时一次性插入的,而非一个一个插入的。

ES5中的数组方法

首先,大多数方法的第一个参数接收一个函数,并且对数组的每个元素(或一些元素)调用一次该函数。如果是稀疏数组,对不存在的元素不调用传递的函数。在大多数情况下,调用提供的函数使用三个参数:数组元素、元素的索引和数组本身。通常,只需要第一个参数值,可以忽略后两个参数。第二个参数是可选的。如果有第二个参数,则调用的函数被看做是第二个参数的方法。也就是说,在调用函数时传递进去的第二个参数作为它的this关键字的值来使用。被调用的函数的返回值非常重要,但是不同的方法处理返回值的方式也不一样。ECMAScript 5中的数组方法都不会修改它们调用的原始数组。当然,传递给这些方法的函数是可以修改这些数组的。

forEach()

forEach(callback[, thisArg])方法从头到尾遍历数组,为每个元素调用指定的函数。无返回值

  • 第一个参数callback为每一个元素执行函数,forEach()使用三个参数调用该函数:数组元素、元素的索引(可选)和数组本身(可选)。
  • 第二个参数(可选),传递 callback 的值一般用this值
var data = [1,2,3,4,5];
var sum = 0;
//数组求和
data.forEach( function(val){ sum += val } );  
console.log(sum);                               //15

//给数组每个元素与指定的值相加
data.forEach(function(val,index,arr){ arr[index] = val + this },2);
console.log(data);                              //[3, 4, 5, 6, 7]
map()

map(callback[, thisArg])方法将调用的数组的元素传递给指定函数,并返回一个数组,它包含该函数的返回值。

var a = [1,2,3];
var b = a.map( function(val){ return val*val } );
console.log(b);             //[1,4,9]

传递给map()的函数的调用方式和传递给forEach()的函数的调用方式一样。但传递给map()的函数应该有返回值。注意,map()返回的是新数组:它不修改调用的数组。如果是稀疏数组,返回的也是相同方式的稀疏数组:它具有相同的长度,相同的缺失元素。

fliter()

fliter()方法返回的数组元素是调用的数组的一个子集。传递的函数是用来逻辑判定的:该函数返回truefalse。调用判定函数就像调用forEach()map()一样。如果返回值为true或能转化为true的值,那么传递给判定函数的元素就是这个子集的成员,它将被添加到一个作为返回值的数组中。

var a = [5, 4, 3, 2, 1];
var smallvalues = a.filter(functionx{ return x < 3 });   
var everyother = a.filter(functionx,i{ return i%2==0 }); 
console.log();                      // [2, 1]
console.log();                      // [5, 3, 1]

注意,filter()会跳过稀疏数组中缺少的元素,它的返回数组总是稠密的。

//压缩稀疏数组的空缺
var dense = sparse.filter(function(){ return true; });
//压缩空缺并删除undefined和null元素
a = a.filter(functionx{ return x !== undefined && x != null; });
every()和some()

every()some()方法是数组的逻辑判定:它们对数组元素应用指定的函数进行判定,返回truefalse

  • every()方法就像数学中的“针对所有”的量词:当且仅当数组中的所有元素调用判定函数都返回true,它才返回true
  • some()方法就像数学中的“存在”的量词:当数组中至少有一个元素调用判定函数返回true,它就返回true;并且当且仅当数值中的所有元素调用判定函数都返回false,它才返回false
var a = [1,2,3,4,5];
a.every(function(x){ return x < 10; });          // => true: 所有的值<10
a.every(function(x){ return x % 2 === 0; });     // => false: 不是所有的值都是偶数

注意,一旦every()some()确认该返回什么值它们就会停止遍历数组元素。some()在判定函数第一次返回true后就返回true,但如果判定函数一直返回false,它将会遍历整个数组。every()恰好相反:它在判定函数第一次返回false后就返回false,但如果判定函数一直返回true,它将会遍历整个数组。注意,根据数学上的惯例,在空数组上调用时,every()返回truesome()返回false

reduce()和reduceRight()

reduce()reduceRight()方法使用指定的函数将数组元素进行组合,生成单个值。reduce()需要两个参数。

  • 第一个是执行化简操作的函数。化简函数的任务就是用某种方法把两个值组合或化简为一个值,并返回化简后的值。
  • 第二个(可选)的参数是一个传递给函数的初始值。

reduce()使用的函数与forEach()map()使用的函数不同。比较熟悉的是,数组元素、元素的索引和数组本身将作为第2~4个参数传递给函数。第一个参数是到目前为止的化简操作累积的结果。第一次调用函数时,第一个参数是一个初始值,它就是传递给reduce()的第二个参数。在接下来的调用中,这个值就是上一次化简函数的返回值。在下面的第一个例子中,第一次调用化简函数时的参数是0和1。将两者相加并返回1。再次调用时的参数是1和2,它返回3。然后它计算3+3=6、6+4=10,最后计算10+5=15。最后的值是15,reduce()返回这个值。

var a = [1,2,3,4,5];
var sum = a.reduce(function(x,y){ return x+y }, 0);         // 数组求和
var product = a.reduce(function(x,y){ return x*y }, 1);     // 数组求积
var max = a.reduce(function(x,y){ return(x>y)?x:y; });      // 求最大值

当不指定初始值调用reduce()时,它将使用数组的第一个元素作为其初始值。这意味着第一次调用化简函数就使用了第一个和第二个数组元素作为其第一个和第二个参数。在上面求和与求积的例子中,可以省略初始值参数。

在空数组上,不带初始值参数调用reduce()将导致类型错误异常。如果调用它的时候只有一个值——数组只有一个元素并且没有指定初始值,或者有一个空数组并且指定一个初始值——reduce()只是简单地返回那个值而不会调用化简函数。

reduceRight()的工作原理和reduce()一样,不同的是它按照数组索引从高到低(从右到左)处理数组,而不是从低到高。如果化简操作的优先顺序是从右到左。

indexOf()和lastIndexOf()

indexOf()lastIndexOf()搜索整个数组中具有给定值的元素,返回找到的第一个元素的索引或者如果没有找到就返回-1。indexOf()从头至尾搜索,而lastIndexOf()则反向搜索。

var a = [0,1,2,1,0];
a.indexOf(1// => 1: a[1]是1
a.lastIndexOf(1// => 3: a[3]是1
a.indexOf(3// => -1: 没有值为3的元素

不同于其他方法,indexOf()lastIndexOf()方法不接收一个函数作为其参数。第一个参数是需要搜索的值,第二个参数是可选的:它指定数组中的一个索引,从那里开始搜索。如果省略该参数,indexOf()从头开始搜索,而lastIndexOf()从末尾开始搜索。