资讯专栏INFORMATION COLUMN

JavaScript || 数组

Euphoria / 3508人阅读

摘要:总结使用访问数组元素,使用访问数组属性,如。数组的长度保证大于每个元素的索引值,数组没有元素,最大索引为,为为数组元素赋值,如果其索引大于等于现有数组长度,的属性值将设置为如果设置的值小于数组长度时,会将索引大于的元素全部删除。

数组

数组是值的有序集合,数组中每个值称为元素,元素在数组中的位置称为索引。JavaScript中的数组是一种特殊的对象:

类属性class attributeArray

新元素添加到数组后,自动更新自身的length属性

Array.prototype对象中继承方法

设置length属性值小于元素个数时,会截断数组

数组的特点

JavaScript中,数组的元素可以是任意JavaScript值,并且同一个数组中不同元素值的类型可以不同

数组的索引是基于0的32位数值,最大的索引为(2^32 - 1) - 1

数组是动态的,可以根据需要增长或缩短

数组的操作方法大部分定义在Array.prototype原型对象上,使所有的数组对象都可以使用

数组的实现经过了优化,用索引访问元素的效率高于直接访问常规对象的属性

数组可能是稀疏或者稠密的。

1 数组的创建与访问 1.1 创建数组

有两种方法可以创建数组:数组字面量[1, 2, 3]new Array(1, 2, 3)推荐使用数组字面量创建数组,简洁易懂。

对象字面量创建的数组,其元素可以是表达式、对象或其他数组。如果省略数组中字面量的值,数组会变为稀疏数组。

var empty = [];   //  <==> var  empty = new Array();数组字面量的写法更简洁
var primes = [2, 3, 5, 7, 11];
var misc = [1.1, true, "a"];    //数组中元素可以是任意类型值

var base = 1024;
var table = [base, base + 1, base + 2, base + 3];  // ==> [ 1024, 1025, 1026, 1027 ]

var b = [[1, {a: "1", b: 2}], [true, {x: 2, y:3}]];

通过Array()构造函数也可以创建数组,但是使用比较繁琐,根据传入参数个数的不同,分为三种情况:

不传入参数:var a = new Array()a数组与[]相同

传入一个非负整数:var a = new Array(10);创建一个存放10个数组元素的空间,但是此时没有定义索引属性和存储值,此时的数组是稀疏的

传入2个或多个数组元素或者一个非数值元素:var c = new Array("1", 2, true, "text");,创建的数组["1", 2, true, "text"]

稀疏数组与稠密数组

根据数组是否有不连续的数组,将其分为稀疏数组和稠密数组

稀疏数组的length属性大于元素的个数,在实现上比稠密数组更慢,消耗内存空间更大,查找元素的效率与常规对象相似。

要避免使用稀疏数组,其查找效率很低。稀疏数组空缺的部分没有值和索引,因为不能用for-in循环遍历出空缺的部分,for-in循环可以遍历对象的可枚举属性(自身和继承)

var undefs = [, , ];   //数组字面量允许逗号结尾,是稀疏数组,没有元素,使用for-in循环不会输出任何内容
var count = [1, , 3];   //稀疏数组,使用for-in循环只输出[1, 3]
var arr = new Array(10);   //稀疏数组,没有赋值和索引

a = new Array(5);   //数组没有元素,a.length为5,稀疏
a[1000] = 0;        //a.length为1001,索引大于元素个数,稀疏

a = [, , , ];  //是稀疏数组,0 in a;  => false
a = [1, , 3];   //稀疏数组,长度为3
a = [,];        //数组没有元素,长度是1

总结:数组创建尽量使用字面量方法,如果数组的索引不连续,则数组是稀疏数组,查询效率低,内存消耗高

1.2 数组的访问

数组是特殊的对象,可以利用对象属性的访问方式.[],因为数组元素的索引是非负数的字符串,所以需要使用[]访问,而数组的属性(如length则可以用.访问)。

利用[]可以读写数组的元素:

var a = ["Hello"];
var value = a[0];        //读取第一个元素
a[1] = 3.14;         //写入第二个元素

数组是特殊的对象

所有的数组都是对象,但对象不一定是数组;

数组对象的属性名在0~2^32-2之间的是索引,可以为数组创建任意属性,只有当属性是索引时,才更新其length属性

使用负整数、与整数等的浮点数作为属性时,会将其转化为索引

    a[-1.23] = true;   //创建一个名为"-1.23"的属性
    a[-3] = false;   //创建一个名为"-3"的属性
    a["1000"] = 0;     //数组的第1001个元素为值为0
    a[2.000];  //与a[2]相同

注:JS中没有越界的概念,因为是将数组作为对象来处理,对于不存在的下标,将其当做普通属性添加到数组对象上。

总结:使用[]访问数组元素,使用.访问数组属性,如.length

1.3 数组的长度

对于稠密数组,其length属性代表元素的个数,索引从0开始,最大值是length - 1。如果是稀疏数组,其length属性大于元素的个数。数组的长度保证大于每个元素的索引值

[].length;   //==>0,数组没有元素
[1, 2, 3].length;   //==>3,最大索引为2,length为3

为数组元素赋值,如果其索引i大于等于现有数组长度,length的属性值将设置为i+1

如果设置的lengthn小于数组长度时,会将索引大于n的元素全部删除。

在ES5中,可以通过Object.defineProperty()设置数组的length属性的描述符descripter{writable: false},使其变为只读属性,设置之后不能添加或者删除数组元素

a = [1, 2, 3, 4, 5];
a.length = 3;             //数组变为[1, 2, 3]
a.length = 0;            //删除所有数组元素

a = [1, 2, 3];
//注意设置为只读后,不能在为数组添加元素,除非修改writable为true
Object.defineProperty(a, "length", {writable: false});
a.length = 2;    //a.length不会改变
a.length;      //==>3

2 数组的操作

Array.prototype对象中定义许多关于数组操作的方法。

2.1 数组元素的添加与删除 push()unshift()方法

push()方法:在数组末尾插入一个或多个元素,与pop()方法相对应,返回插入元素后数组的新长度

unshift()方法:在数组的首部插入一个或多个元素,与shift()方法相对应,返回插入元素后的数组新长度。注意一次插入多个元素和多次插入一个元素的效果是不同的

pop()shift()方法

pop()方法:在数组末尾删除一个元素,返回删除的元素;

shift()方法:在数组首部删除一个元素,返回删除的元素;

var a = [];
a.push("one");  // ==> ["one"],在末尾添加一个元素
a.push("two", "zero");  // ==> ["one", "two", "zero"],在末尾添加两个个元素

a.pop();      // ==>"zero",a变为["one", "two"]
a.unshift("four"); // ==> a变为["four", "one", "two"]
a.shift();   // ==> 删除"four"

注:会修改调用该方法的数组

splice()方法

splice(start, num, insert1, insert2,...)方法是插入、删除和替换数组元素的通用方法。

参数start:插入或删除的起始位置

参数num:需要删除元素的个数,如果不指定会将起始位置后的所有元素删除

insertinsert2...:需要插入的元素

var a = [1, 2, 3, 4, 5, 6, 7];
a.splice(5);   //==>[6, 7],a变为[1, 2, 3, 4, 5]
a.splice(2, 2);   //==>[3, 4],a变为[1, 2, 5]
a.splice(-1, 1);   //==>[5],a变为[1, 2]

a.splice(1, 0, "a", false, 0);   // ==>[],没有删除元素,a变为[1, "a", false, 0, 2]
a.splice(2, 2, ["x", "y"]);     // ==>[false, 0],a变为[1, "a", ["x", "y"], 2]

注:会修改调用该方法的数组

2.2 数组的遍历

使用for循环是遍历数组的常见方法,可以配合部分if语句过滤部分元素。

for(var i=0, len=arr.length; i

对于稀疏数组,可以使用for-in循环历来过滤掉不存在的索引。for-in循环会访问继承的可枚举属性,利用hasOwnProperty()方法过滤掉非自身属性。

for(var i in p) {
  if(!p.hasOwnProperty(i)) {continue;}  //跳过继承的属性
}
forEach()方法

由于for-in循环本身是未遍历对象而设计,如果数组有其余可枚举属性,需要专门过滤。ES5定义forEach()方法来遍历数组。

forEach(fn)方法接收一个函数作为参数,将每个元素分别调用该函数。forEach()可以给fn传递三个参数,fn(value, index, array)

value:数组元素

index:数组元素对应的索引

array:数组本身

var a = [1, 2, 3, 4, 5];
//计算数组元素的和
var sum = 0;
a.forEach(function(value) {   //只传递数组元素一个参数
  sum += value;
});
console.log(sum);   //  ==> 15
//将数组每个元素加2
a.forEach(function (value, index, array) {
  array[index] = value + 2;
});
console.log(a);   // ==>[3, 4, 5, 6, 7]

forEach()方法不能在所有元素都传递给fn函数调用前终止遍历(不能使用break跳出循环),如果要提前终止循环,需要将forEach()方法放在try块中,如果forEach()调用的函数fn能抛出foreach.break异常,遍历提前终止。

function foreach(value, index, array) {
  try {
    a.forEach(index, array);
  } catch(e) {
    if(e === foreach.break) {
      return;
    } else {
      throw e;
    }
  }
}
2.3 多维数组

JS并不支持真正的多维数组,可以利用数组的数组来进行模拟,使用[][]访问即可。

var table = new Array(10);   //表格10行
var len=table.length;
for(let i=0; i 35
3 数组的方法

Array.prototype中定义了与数组操作相关的方法,主要分为ES3和ES5两个部分

3.1 ES3中的数组操作方法 Array.prototype.join():不修改原字符串

Array.prototype.join(seperator)方法将数组中所有元素先转化为字符串,再利用参数seperator传入的分隔符将元素转换成的字符串拼接起来,最后返回该字符串。默认使用,分隔。String.prototype.split()方法的逆向操作

var a = [1, 2, 3];
a.join();   // ==> "1,2,3"
a.join(" ");   // ==> "1 2 3"
a.join("");   // ==> "123"
"1,2,3".split(",").join();  //==> "1,2,3"
Array.prototype.reverse():会修改原数组

将数组中的元素顺序颠倒,返回逆序后的数组

var a = [1, 2, 3];
a.reverse.join();   //"3,2,1",并且a变为[3, 2, 1],所以注意使用副本来操作
Array.prototype.sort():会修改原数组

将数组中元素按照指定的顺序后返回,默认按照字母表的顺序怕排序。undefined值被排到最后,要使用其他方式排序,需要传入一个比较函数fn(a, b),依据两个参数在排序好的数组中的先后顺序:

如果第一个参数在第二个参数之前,函数返回值小于0

如果第一个参数在第二个参数值后,函数返回值大于0

如果两个参数相等,函数返回值等于0

var arr = ["banana", "cherry", "apple"];
arr.sort();
arr.join(",");   //==> "apple,banana,cherry",默认按照字母表顺序

var a = [1, 2, 3, 4];
a.sort(function(a, b) {
  return a - b;    //如果目标是升序,a在b的前面,函数返回值小于0即可
});
a.sort(function(a, b) {
  return b - a ;   //如果目标是降序,a在b的后面,函数返回值大于0即可
});

//不区分大小写升序排列
var arr = ["ant", "Dog", "Bug", "cat"];

arr.sort(function(first, second) {
  first = first.toLowerCase();    //将字符串全部转化为小写
  second = second.toLowerCase();
  if(first < second) {       //如果正确顺序是first在second前,则返回-1
    return -1;
  } else if (first > second) {
    return 1;
  } else {
    return 0;
  }
});

Array.prototype.concat():不修改原数组

用于拼接原始数组与传入的参数,组成一个新的数组并返回。如果传入的参数是数组,会连接最外层的数组元素

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]]  
// 将数组[5, [6, 7]]最外层拆开,但是保留内部
Array.prototype.slice():不改变原数组

返回指定数组的一个片段或数组:

通过两个参数指定切割的起始位置与结束位置(不包含第二个位置的元素)

如果只有一个参数,返回从开始位置到数组结束的所有元素

参数接收负数,-1表示最后一个元素,-2表示倒数第二个元素

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]

toString()toLocalString()

先将数组的每个元素转化为字符串,再利用逗号将其拼接为一个字符串,其中toLocaleString()方法区别在于转换DateNumber类型的数字、日期和时间时,根据时区、数字来转化。

转换结果与不传入参数的join()方法类似

[1, 2, 3].toString();   // ==> "1,2,3"
["a", [1, "b"]].toString();  // ==> "a,1,b"
3.2 ES5中操作数组的方法

ES5中定义了9个方法来遍历forEache()、映射map()、过滤filter()、检测every() some()、简化reduce() reduceRight()和搜索indexOf() lastIndexOf()数组。九种方法不会修改调用它的数组,但是传入的函数会。

九种方法基本都属于过程抽象,第一个参数基本都是一个函数,对数组的每个元素调用该函数

Array.prototype.map()

map(fn)只有一个参数,map()方法传递三个参数给fn(value, index, array),根据需要选择。其调用方式与forEach()相似,但是传递给map(fn)的函数fn有返回值。

map()返回的是新数组,不修改原数组

如果是稀疏数组,返回稀疏素组;具有相同的长度与缺失元素

var a = [1, 2, 3];
a.map(function(value) {
  return value * value;     //map()传入的函数必须要有返回值,将返回值作为返回数组的元素的值
});          //==> [1, 4, 9],原数组a还是[1, 2, 3]不变

Array.prototype.filter()

filter()方法返回的数组是调用数组的一个子集。通过传递的函数进行逻辑判断,如果返回值是true,当前元素被添加到返回的子集中;如果返回false,则过滤掉该元素;

filter()会跳过稀疏数组中缺失的元素,返回的总是稠密数组

arr.filter(function() {return true;}); 会过滤掉所有缺失的元素;

arr.filter(funtion(x) {return (x !== undefined && x !=null)}):过滤掉undefinednull

var a = [5, 4, 3, 2, 1];
a.filter(function(value) {
  return value < 3;
});        // ==> [2, 1]
a.filter(function(value, index) {
  return index % 2 === 0;  //过滤掉索引为奇数的元素
});     // ==> [5, 3, 1],不会改变原数组

Array.prototype.every()Array.prototype.some()

every()some()用于对数组进行逻辑判断,返回true或`false;每个元素应用传入的函数进行判断:

every()方法:当且仅当数组中所有元素调用函数的判断结果为true时,才返回true

some()方法:至少有一个元素调用函数的判断结果为true时,返回true

every()some()在确定返回truefalse后会停止遍历数组。类似&&||的短路特性

在空数组上调用,根据惯例,every()返回truesome()返回false

var a = [5, 4, 3, 2, 1];
a.every(function(value) {
  return value < 10;
});      //  ==>true,所有的值都小于10
a.every(function(value) {
  return value % 2 === 0;
});    // ==> false,不是所有元素都是偶数

a.some(function(value) {
  return value % 2 === 0;
});    // ==> true,数组a中有偶数
a.some(isNaN);   //  ==> false,a中所有元素都不是非数值元素

Array.prototype.reduce()Array.prototype.reduceRight()

reduce()reduceRight():使用指定函数将数组中的元素进行组合,生成单个值。在函数式编程中常用,称为注入和折叠。ruduceRight()的工作原理与reduce()相同,只是从右侧开始索引

reduce(fn, init)有两个参数:

fn是执行简化操作的函数;

init是传递给简化函数的初始值,如果不指定,默认为数组的第一个元素

reduce()传递给化简函数fn(init, value, index array)四个参数:

init:第一次调用函数时,initreduce()传入的初始值,如果reduce()没有指定第二个参数,默认使用数组的第一个元素;以后的调用中,其值等于化简函数的返回值

valueindexarray:分别是数组元素、元素索引和数组本身。

var a = [1, 2, 3, 4, 5];
a.reduce(function(init, value) {
  return init + value;
}, 0);    //初始值init是0,返回累加的结果==> 15
//初始值为1,如果不指定,默认为数组的第一个元素
a.reduce(function(init, value) {return init * value;}, 1);  //  ==>120
a.reduce(function(init, value) {return (init > value)? init: value;});

var a = [2, 3, 4];
//计算2^(3^4),乘方操作的优先顺序是从右到左。
a.reduceRight(function(init, value) {return Math.pow(value, init);});

a.reduce(function(init, value) {return init + value;}, 0); 的执行过程:

化简函数第一次调用时,reduce()方法将0传递给函数的init参数,将数组的第一个元素传递给value

执行化简函数,将返回值赋予init,再次继续下次调用

Array.prototype.indexOf()Array.prototype.lastIndexOf()

搜索整个数组中指定值,返回第一个匹配值的索引,如果没有找到返回-1indexOf()从头到尾查找、lastIndexOf()从尾到头查找。

indexOf()lastIndexOf()的参数没有函数,第一个参数是查找的值;第二个参数可选,指定开始查找的索引位置

//查找数组中所有出现的value,返回一个包含匹配值索引的数组
function findall(arr, value) {
  var results = [];
  var len = a.length;
  var index = 0;         //开始查找的索引

  while(index < len) {   //arr.indexOf(arr, index)中第二个参数是为了过滤掉之前已经查找过的数组元素
    index = arr.indexOf(value, index);    //避免重复的同时,加快了搜索效率
    if(index === -1) {break;}      //如果未找到,便退出;因为forEach()退出循环不方便,所以使用while
    results.push(index);
    index += 1;
  }
}
4 类数组对象 4.1 数组类型

判断一个未知的对象是否为数组,可以使用ES5提供的Array.isArray()方法,定义在Array()构造器函数上,没有定义在Array.prototype原型上。

Array.isArray([]);    //  ==>true
Array.isArray({});    //  ==>false

因为typeofinstanceof在判断Array类型时比较繁复,所以使用同一的Array.isArray()来判断。isArray()方法的可能实现:

//Function.isArray || function(o)用来判断程序中是否有同名的isArray函数,确保不会重写原有函数。
var isArray = Function.isArray || function(o) {
  return (typeof o === "object") && (Object.prototype.toString.call(o) === "[object Array]");
};
4.2 类数组对象

JS中数组的部分特性是其余对象没有的:

新元素添加到数组后,自动更新length属性

设置length为较小值时,截断数组

Array.prototype中继承属性

类属性为Array

这4点并不是数组的本质。但是可以将一个用于length属性(值为数值),其余属性为非负整数(索引)的对象看做类数组。由于数组的方法通用性很强,可以使用数组的方法来操作类数组对象,只是类数组对象并未继承Array.prototype对象,所以需要Function.call()来间接调用。

var a = {"0": "a", "1": "b", "2": "c", "length": 3};   //类数组对象
Array.prototype.join.call(a, ",");      // ==> "a,b,c"
Array.prototype.slice.call(a, 0);     //  ==> ["a", "b", "c"]真正数组的副本
Array.prototype.map.call(a, function(value) {
  return value.toUpperCase();
});      //  ==> ["A", "B", "C"]

函数的参数arguments对象和document.getElementsByTagName()获取的对象都是类数组对象。检测一个对象是否是类数组对象:

//判断o是否为类数组对象
//字符串和函数有length属性,使用typeof将其排除
//在DOM中,文本节点有length属性,要额外调用o.nodeType !== 3排除
function isArrayLike(o) {
  if(o && typeof o === "object" &&    //非null和undefined,并且是对象
      isFinite(o.length) &&
      o.length >= 0 &&
      o.length == Math.floor(o.length) &&   //o.length是整数
      o.length < Math.pow(2, 32)) {   //数组索引的上限是2^32-1
    retun true;
  } else {
    return false;
  }
}
4.3 作为数组的字符串

在ES5中,字符串的行为类似于只读的数组,可以使用charAt()访问单个字符,也可以通过[]访问单个字符。

var s = "test";
s.charAt(0);   // ==> "t"
s[1];   // ==> "e"

使用[]的索引形式访问字符串,可以简化charAt()的调用。因为字符串用于非负的整数length属性,每个字符的索引是非负整数,可以将字符串看做类数组对象,使用数组的方法来操作字符串。

调用的方法不能改变原字符串,splice()sort()reverse()pop()push()shift()unshift()方法不能使用,使用数组方法修改字符串会导致错误,但是没有错误提示

var s = "javascript";
Array.prototype.join.call(s, " ");  // ==>"j a v a s c r i p t"
总结

使用对象字面量方式创建数组

数组元素的读写都可以通过[]

对于稀疏数组,其length属性大于元素的个数

ES5之后,单纯的数组遍历可以使用forEach(),对于需要判断跳出循环遍历的,可以使用for循环替代、或使用try-catch

数组的操作方法都定义在Array.prototype原型对象上,可以对数组进行:forEach()map()every()some()filter()reduce()reduceRinght()indexOf()lastIndexOf()splice()sort()reverse()

类数组对象指有非负整数的length值,并且索引为非负整数的对象,类数组对象可以使用数组的方法来操作,使用Function.call()来间接调用即可

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/81845.html

相关文章

  • JavaScript专题系列文章

    摘要:专题系列共计篇,主要研究日常开发中一些功能点的实现,比如防抖节流去重类型判断拷贝最值扁平柯里递归乱序排序等,特点是研究专题之函数组合专题系列第十六篇,讲解函数组合,并且使用柯里化和函数组合实现模式需求我们需要写一个函数,输入,返回。 JavaScript 专题之从零实现 jQuery 的 extend JavaScritp 专题系列第七篇,讲解如何从零实现一个 jQuery 的 ext...

    Maxiye 评论0 收藏0
  • 数组 - Javascript语法基础 - Javascript核心

    摘要:数组创建数组数组字面量使用构造函数数组本质上是所以要判断是不是数组,需要通过判断。数组长度使用属性获取元素的个数。例如函数的对象就是这样 原文: http://pij.robinqu.me/JavaScript_Core/JavaScript_Basics/Array.html 源代码: https://github.com/RobinQu/Programing-In-...

    molyzzx 评论0 收藏0
  • Array的javascript数据结构描述

    摘要:几乎所有的编程语言都有类似的数据结构。然而的数组却略有不同。没有定义中说的那样的数据结构,作为替代。元素通过指定的分隔符进行分隔。返回该对象的源代码。把数组转换为本地数组,并返回结果。 写在前面的话: 经常看到技术博客上写的内容很详细,对于一个问题,大家的博客内容似乎都是一样的。不难发现这些技术博客已经成为各路教程的汇总地,而教程一般都是参考国外博客翻译或者直接在经典的书籍上大片抄录。...

    iOS122 评论0 收藏0
  • array数组类型

    摘要:中并没有明确的数组数据类型。创建一维数组具有以下几种方式二维数组数组是可以嵌套,这就意味着一个数组可以作为一个数组被包含在另外一个数组里面。利用这个特点,创建二维数组,即数组的数组。删除数组的最后一个元素并返回删除的元素。 Array 概述 一维数组 数组是一个有序的数据集合,可以通过数组名称和索引进行访问。JavaScript中并没有明确的数组数据类型。创建一维数组具有以下几种方式:...

    lemanli 评论0 收藏0
  • JavaScript面向对象编程——Array类型

    摘要:中并没有明确的数组数据类型。返回数组对象的原始值。专门用于筛选出数组中符合函数判断条件的元素组成的心数组。专门用于对原数组中每个元素执行相同的函数对象规定的操作。 概述 一维数组 数组是一个有序的数据集合,可以通过数组名称和索引进行访问。JavaScript中并没有明确的数组数据类型。 二维数组 数组是可以嵌套的,这就意味着一个数组可以作为一个袁旭被包含在另一个数组里面。利用JavaS...

    DirtyMind 评论0 收藏0
  • JavaScript学习总结(二)数组和对象部分

    摘要:属性是一个值或一组值以数组或对象的形式,是对象的成员。可以使用内置构造函数和创建包装对象。因此下面的代码将会使人很迷惑结果结果,此数组长度为应该尽量避免使用数组构造函数创建新数组。给数组对象添加返回数组中最大元素值的方法。 对象部分 Object类型 Object 是一个无序的集合,可以存放任意类型对象,所有其他对象都继承自这个对象。创建Object类型有两种,一种是使用new运算符,...

    Lsnsh 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<