JS引用数据类型-数组

数组(数组对象)

数组的基本概念

数组是用中括号([])扩起来的一种有序的值的集合, 用于在单一变量中存储多个值, 值可以是其他数据类型或数组.

var ary = [1, 'xiao', true, null, undefined, {}, []]

数组也是对象数据类型(一种特殊类型的对象), 也是由键值对组成, 使用 typeof 运算符会返回 “object”.

console.log(ary)

/*
返回结果:
[1, "xiao", true, null, undefined, {…}, Array(0)]
0: 1
1: "xiao"
2: true
3: null
4: undefined
5: {}
6: []
length: 7
__proto__: Array(0)
*/

数组结构:
1. 以数字作为索引(属性名), 索引从 0 开始递增
2. 有一个 length 属性, 值是数组长度
3. 有一个 __proto__ 属性, 值指向数组对象 Array(0)

创建数组

  1. 使用字面量方式创建数组:
var ary = [1, 'xiao', true, null, undefined, {}, []]
  1. 使用new关键词(new可以省略)创建数组:
var ary = new Array(1, 'xiao', true, null, undefined, {}, [])
var ary = new Array(40)     // 该语句并不会创建一个包含40的数组, 而是创建一个长度为40但未定义的数组

出于简洁、可读性和执行速度的考虑,使用第一种字面量方法(数组文本方法)。

多维数组
数组中再包含数组.

var ary = [1, 'xiao', true, null, undefined, {}, [1, 2, 5]]

访问数组

访问完整数组:

可以直接使用数组变量名访问整个数组

访问数组元素:

通过索引可以访问数组中的元素.

改变数组元素
也是通过索引修改要改变的元素.

数组的属性和方法

先定义一个数组:

var arry = [1, 3, 5, 7, 6, 8, 4, 2, 0]

length属性

length 属性返回数组的长度(数组元素的数目)

console.log(arry.length);               // 9

// 访问数组的最后一个元素
console.log(arry[arry.length-1]);       // 0

// 遍历数组
for (var i = 0; i < arry.length; i++) {
    console.log(arry[i]);
}

push方法:

  • 作用: 向数组末尾追加元素
  • 参数: 追加的元素(可以是一个或多个)
  • 返回值: 新增后数组长度
  • 原有数组改变
console.log(arry.push(9, 10));          // 11
console.log(arry);                      // [1, 3, 5, 7, 6, 8, 4, 2, 0, 9, 10]

pop方法:

  • 作用: 删除数组最后一个元素
  • 参数: 无
  • 返回值: 被删除的元素
  • 原有数组改变
console.log(arry.pop());                // 10
console.log(arry);                      // [1, 3, 5, 7, 6, 8, 4, 2, 0, 9]

shift方法:

  • 作用: 删除数组第一个元素
  • 参数: 无
  • 返回值: 被删除的元素
  • 原有数组改变
console.log(arry.shift());              // 1
console.log(arry);                      // [ 3, 5, 7, 6, 8, 4, 2, 0, 9]

==shift最好不要用, 当删除第一项后后面的每一项的索引都会向前进1, 造成数组塌陷, 相当耗性能.==

unshift方法:

  • 作用: 向数组初始位置添加元素
  • 参数: 添加的元素, 可以是一个或多个
  • 返回值: 新增后数组长度
  • 原有数组改变
console.log(arry.unshift(0, 1));        // 11
console.log(arry);                      // [0, 1, 3, 5, 7, 6, 8, 4, 2, 0, 9]

==unshift也相当耗性能.==

splice方法:

  • 作用: 1.删除指定位置的元素; 2.向数组指定位置增加元素; 3.修改指定位置的元素
  • 参数: 根据不同作用参数有所不同
  • 返回值: 删除的元素组成的新数组
  • 原有数组改变
var arry = [0, 1, 3, 5, 7, 6, 8, 4, 2, 0, 9];

// 作用: 1.删除指定位置的元素
// 参数: splice(n,m) n 是第几个索引, m 是要删除的元素个数(从 n 算起)
// 返回值: 删除的元素组成的新数组
console.log(arry.splice(5, 2));         // [6, 8]
console.log(arry)                       // [0, 1, 3, 5, 7, 4, 2, 0, 9]
// 如果不给参数 m , 或者 m 值比较大, 则删除从 n 到数组最后一个元素. 清除整个数组可以设置 n 为 0 
console.log(arry.splice(6));            // [2, 0, 9]
console.log(arry)                       // [0, 1, 3, 5, 7, 4]

// 作用: 2.向数组指定位置增加元素
// 参数: splice(n,0,x,....) 从第 n 个索引开始, 删除 0 个元素, 将元素 x.... 插入索引 n 的前面
// 返回值: 空数组
console.log(arry.splice(2, 0, "x", 2)); // []
console.log(arry)                       // [0, 1, "x", 2, 3, 5, 7, 4]

// 作用: 3.修改指定位置的元素
// 参数: splice(n,m,x,....) 从第 n 个索引开始, 删除 m 个元素, 将元素 x.... 插入索引 n 的前面完成修改
// 返回值: 删除的元素组成的新数组
console.log(arry.splice(2, 2, 9, 0));   // ["x", 2]
console.log(arry)                       // [0, 1, 9, 0, 3, 5, 7, 4]

思考:
1. 删除数组最后一个元素有几种方法.
– arry.pop()
– arry.splice(arry.length-1)
– arry.length–

  1. 向数组末尾追加元素有几种方法.
  • arry.push(n)
  • arry.splice(arry.length, 0, n)
  • arry[arry.length] = n

slice方法:

  • 作用: 在一个数组中, 按照条件查找出其中的部分内容(切片)
  • 参数: 两个参数(n/m), 从索引n开始找到索引m前一位(前包后不包)
  • 返回值: 以一个新数组存储查找的内容
  • 原有数组不变
var ary = [12, 23, 34, 45, 56, 67, 78, 89, 90];
// 常规写法
console.log(ary.slice(3,6))         // [45, 56, 67]
// 不添加参数m, 则从索引3切到数组末尾
console.log(ary.slice(3,))          // [45, 56, 67, 78, 89, 90]
console.log(ary.slice(3))           // [45, 56, 67, 78, 89, 90]
// 参数n和m都不添加, 克隆整个数组(产生一个新数组和原数组存储不同内存地址, 浅克隆)
console.log(ary.slice(0))           // [12, 23, 34, 45, 56, 67, 78, 89, 90]
console.log(ary.slice())            // [12, 23, 34, 45, 56, 67, 78, 89, 90]
// n和m是负数, 倒着数(相当于n或m加上数组长度)
console.log(ary.slice(-4,-1))       // [67, 78, 89]

concat方法:

  • 作用: 实现多个数组或值的拼接
  • 参数: 数组或值
  • 返回值: 拼接后的新数组
  • 原有数组不变
var ary1 = [12, 23];
var ary2 = [100, 200];
var ary3 = [1000, 2000];
console.log(ary1.concat(ary2, ary3));               // [12, 23, 100, 200, 1000, 2000]
console.log(ary1.concat(ary2, 'xiaomi'));           // [12, 23, 100, 200, "xiaomi"]
// 空数组打头, 拼接后的顺序自己安排
console.log([].concat(ary3, ary1, ary2));           // [1000, 2000, 12, 23, 100, 200]

toString方法:

  • 作用: 把数组转换为字符串
  • 参数: 无
  • 返回值: 数组中每一项用逗号分隔的字符串
  • 原有数组不变

join方法:

  • 作用: 把数组转换为字符串, 可以设置每一项的连接符
  • 参数: 连接符
  • 返回值: 字符串
  • 原有数组不变
var ary = [12, 23, 34, 45];
console.log(ary.join());            // 12,23,34,45
console.log(ary.join(","));         // 12,23,34,45
console.log(ary.join("+"));         // 12+23+34+45
console.log(eval(ary.join("+")));   // 114

reverse方法:

  • 作用: 翻转数组内所有项
  • 参数: 无
  • 返回值: 翻转后的新数组
  • 原有数组改变
var ary = [12, 23, 34, 45];
console.log(ary.reverse());         // [45, 34, 23, 12]
console.log(ary);                   // [45, 34, 23, 12]

sort方法:

  • 作用: 将数组排序
  • 参数: 无/函数
  • 返回值: 排序后的新数组
  • 原有数组改变
// sort在没有参数传递的情况下只能对10以内的数字进行排序
var ary = [2, 3, 1, 6, 9, 5, 4, 8, 7];
console.log(ary.sort());            // [1, 2, 3, 4, 5, 6, 7, 8, 9]
var ary1 = [2, 7, 12, 21, 14, 113, 33];
console.log(ary1.sort());           // [113, 12, 14, 2, 21, 33, 7]

// 真实项目中sort排序都是要传参的
console.log(ary1.sort((function (a, b) {
    // a - b 升序
    // b - a 降
    return a - b;
})));                               // [2, 7, 12, 14, 21, 33, 113]

indexOf/lastIndexOf方法:

  • 作用: 查找参数中的值在数组中第一次/最后一次出现位置的索引
  • 参数: 要查找的值
  • 返回值: 返回索引
  • 原有数组不变
  • indexOf/lastIndexOf不兼容ie低版本浏览器
var ary = [2, 3, 1, 3, 1, 5, 5, 8];
console.log(ary.indexOf(1));        // 2
// 如果没有查找的值则返回-1
console.log(ary.indexOf(6));        // -1

数组去重

数组去重示例一: 数组中没有连续的重复项

var ary = [1, 2, 3, 2, 3, 4, 3, 4, 3, 5];
// 解决方案:
// 1. 依次拿出数组中的每一项(排除最后一项, 最后一项后面没有可比的项)
// 2. 将拿出的这一项和它后面的每一项依次进行比较
// 3. 如果有相等的项, 就可以用splice方法将其从数组删除
var ary = [1, 2, 3, 2, 3, 4, 3, 4, 3, 5];
for (var i = 0; i < ary.length - 1; i++) {
    // item: 依次拿出的每一项
    // i: 当前拿出项的索引
    var item = ary[i];
    // 和当前项的后面项进行比较: 起始索引应该是 i + 1 < ary.length , 一直到末尾依次比较
    for (var j = i + 1; j < ary.length; j++) {
        // ary[j]: 为后面要比较的项
        // 两项相等: 重复了, 则将后面的 j 项删除
        if (item == ary[j]) {
            ary.splice(j, 1);
        }
    }
}
console.log(ary)            // [1, 2, 3, 4, 5]

数组去重示例二: 数组中有连续的重复项

var ary = [1, 2, 3, 2, 2, 3, 4, 3, 4, 3, 5];
for (var i = 0; i < ary.length - 1; i++) {
    var item = ary[i];
    for (var j = i + 1; j < ary.length; j++) {
        if (item == ary[j]) {
            ary.splice(j, 1);
        }
    }
}
console.log(ary)            // [1, 2, 3, 2, 4, 5]
// 发现并没有完全去重, 原因就是数组塌陷
// 数组塌陷: 当删除数组中一项时, 其后面的每一项都会向前进一项, 导致原有数组中的项的索引发生改变
// 此时j继续累积1, 就会跳过一项

// 解决方案一: 不让 j 累加
for (var i = 0; i < ary.length - 1; i++) {
    var item = ary[i];
    for (var j = i + 1; j < ary.length; j++) {
        if (item == ary[j]) {
            ary.splice(j, 1);
            // 删除后不让 j 减减, 再加加的时候相当于没加没减
            j--;
        }
    }
}
console.log(ary)            // [1, 2, 3, 4, 5]

// 解决方案二: 从后往前进行比较
for (var i = ary.length - 1; i > 0; i--) {
for (var j = i - 1; j > 0; j--) {
if (ary[i] == ary[j]) {
ary.splice(j, 1)
}
}
}
console.log(ary)            // [1, 2, 4, 3, 5]

// 解决方案三: 基于对象键值对进行去重
// 数组越大双循环性能越低
// 利用对象属性名不能重复, 实现高性能去重

// 1. 创建一个空对象
// 2. 依次遍历数组的每一项, 把数组的每一项的值当作对象属性名和属性值进行存储
// 第一次循环 {1:1}
// 第二次循环 {1:1, 2:2}
// 第三次循环 {1:1, 2:2, 3:3}
// 第四次循环 {1:1, 2:2, 3:3, 2:2}
// 所以我们应该在存储之前做一个判断, 判断当前对象是否已经存在该属性名, 如果存在说明前面已经存储过这个属性名, 也即相当于数组中有这一项重复值, 现在只需删除当前项即可
// 如何判断对象中已经存在这个属性: 如果没有这个属性, 属性值是undefined
var ary = [1, 2, 3, 2, 2, 3, 4, 3, 4, 3, 5];
var obj = {};
for (var i = 0; i< ary.length; i++){
    var item = ary[i];
    // 存储之前做判断, 是否存在该属性
    if ( typeof obj[item] !== 'undefined'){
        ary.splice(i, 1);
        // 防止数组塌陷
        i--;
        // 结束本次循环, 下面的obj[item] = item就不再执行
        continue;
    }
    // 把这一项作为对象的属性名和属性值进行存储
    obj[item] = item;
}
console.log(ary)            // [1, 2, 3, 5, 4]

// 但是上面这个方法还是会出现当数组很大时删除效率很低的情况, 其主要原因还是因为splice在删除的时候后面所有的项的索引都要重新计算

// 改进方案:
var ary = [1, 2, 3, 2, 2, 3, 4, 3, 4, 3, 5];
var obj = {};
for (var i = 0; i< ary.length; i++){
    var item = ary[i];
    if ( typeof obj[item] !== 'undefined'){
        // 将数组的最后一项替换当前项
        // 然后删除数组最后一项
        ary[i] = ary[ary.length-1];
        ary.length--;
        // 这里的i--不是为了防止数组塌陷
        i--;
        continue;
    }
    obj[item] = item;
}
console.log(ary)            // [1, 2, 3, 5, 4]

// 最简洁的去重: 利用Set类型自动去重
var ary = [1, 2, 3, 2, 2, 3, 4, 3, 4, 3, 5];
ary = Array.from(new Set(ary));
console.log(ary)            // [1, 2, 3, 4, 5]

原创文章,作者:tipak,如若转载,请注明出处:http://www.myqqu.com/note/javascript_lessons/js-yinyongshujuleixing-shuzu.html