数组方法、类数组

一、数组

1、三种数组声明方式

// 数组字面量
var arr1 = [];
// 不推荐:通过系统内置的Array构造函数声明数组
var arr2 = new Array();
// 不实用:第三种声明方式
var arr3 = Array();
// 所有数组都继承于 Array.prototype

// 数组方法: 都继承于 Array.prototype 上面的方法

2、稀松数组

// 数组字面量声明中:只能识别开头和中间的空值,末尾逗号不识别,会忽略
var arr = [, 1, 2, , 3,]; // 稀松数组
console.log(arr.length); // 2

3、push

// push:数组最后一位添加,返回值为执行了方法以后数组长度
var arr = [1, 2, 3];
console.log(arr.push(4, 5)); // 5
console.log(arr); // [1, 2, 3, 4, 5]

4、unshift

// unshift: 数组第一位添加,返回值为执行了方法以后数组长度
var arr = [1, 2, 3];
console.log(arr.unshift('a')); // 6
console.log(arr); // ["a", 1, 2, 3]

5、pop

// pop:删除数组最后一位,返回值为 删除的元素
var arr = [1, 2, 3];
console.log(arr.pop()); // 3
console.log(arr); // [1, 2]

6、shift

// shift:删除数组第一位,返回值为 删除的元素
var arr = [1, 2, 3];
console.log(arr.shift()); // 1
console.log(arr); // [2, 3]

7、reverse

// reverse: 倒序
var arr = [1, 2, 3];
arr.reverse();
console.log(arr); // [3, 2, 1]

8、splice

// splice: 剪切数组的数据,并在删除位置加入新数据
// arr.splice(开始项的下标, 剪切长度, 剪切以后最后一位开始添加数据);
var arr = ['a', 'b', 'c'];
arr.splice(1, 1, 1, 2, 3);
console.log(arr); // ["a", 1, 2, 3, "c"]

// 在 c 之后添加 d
var arr = ['a', 'b', 'c', 'e'];
arr.splice(3, 0, 'd'); // 正从0开始
// arr.splice(-1, 0, 'd'); // 负从-1开始
console.log(arr); // ["a", "b", "c", "d", "e"]

// splice 的原理
function splice(arr, index) {
    if (index >= 0) {
        index += 0;
    } else {
        index += arr.length;
    }
    // 返回数组下标
    return index;
}

9、sort

// sort: 按照 Ascii 码来排序,返回排序之后的数组
// 自定义排序:
// 1、参数a, b
// 2、返回值:1、负值 a就排前面; 2、正值 b就排前面; 3、0 保持不动
var arr = [27, 49, 5, 7];
// 自定义排序-冒泡排序
arr.sort(function(a, b){
    // if (a > b) {
    //     return 1;
    // } else {
    //     return -1;
    // }
    // 升序
    return a - b;
    // 降序
    // return b - a;
});
console.log(arr); // [5, 7, 27, 49]

// 随机排序
var arr = [1, 2, 3, 4, 5, 6];
// Math.random() -> 0 - 1 开区间
arr.sort(function(a, b){
    // var rand = Math.random();
    // if (rand - 0.5 > 0) {
    //     return 1;
    // } else {
    //     return -1;
    // }

    return Math.random() - 0.5;
});
console.log(arr); // [1, 6, 3, 4, 5, 2]

// 对象排序
var arr = [
    {son: 'Ben', age: 10},
    {son: 'Lucy', age: 3},
    {son: 'Li', age: 16},
    {son: 'Jone', age: 9}
];

arr.sort(function(a, b) {
    if (a.age > b.age) {
        return 1;
    } else {
        return -1;
    }
});
console.log(arr); 
// 0: {son: "Lucy", age: 3}
// 1: {son: "Jone", age: 9}
// 2: {son: "Ben", age: 10}
// 3: {son: "Li", age: 16}


// 按照字符串长度排序
var arr = ['12345', '1', '1234', '12', '1234567'];
arr.sort(function(a, b){
    // if (a.length > b.length) {
    //     return 1;
    // } else {
    //     return -1;
    // }

    return a.length - b.length;
});
console.log(arr); //  ["1", "12", "1234", "12345", "1234567"]

10、concat

// concat: 数组拼接
var arr1 = ['a', 'b'];
var arr2 = ['c', 'd'];
var arr3 = arr1.concat(arr2);
console.log(arr3); // ["a", "b", "c", "d"]

11、toString

// toString(): 转化为以逗号隔开的字符串
var arr = ['a', 'b', 'c'];
console.log(arr.toString()); // 'a,b,c'

12、slice

// slice(start, end): 截取数组[闭区间, 开区间)
var arr = ['a', 'b', 'c', 'd', 'e'];
// 不传参数: 完全复制一个新数组
var arr1 = arr.slice();
console.log(arr1); // ["a", "b", "c", "d", "e"]

// [start]: 开始元素的下标,结果包含该下标的元素
// [end]: 结束元素的下标,结果不包括该下标的元素
// 正值:从左往右从0开始
var arr2 = arr.slice(1, 4);
console.log(arr2); // ["b", "c", "d"]
// 负值:从右往左从-1开始
var arr3 = arr.slice(-3, 4);
console.log(arr3); // ["c", "d"]
var arr3 = arr.slice(-3, -2);
console.log(arr3); // ["c"]

13、join

// join: 数组元素转为字符串,以参数隔开,默认为逗号
var arr = ['a', 'b', 'c'];
var str1 = arr.join();
console.log(str1); // 'a,b,c'
var str1 = arr.join('');
console.log(str1); // 'abc

14、split

// split: 把字符串转为数组
var str1 = 'a-b-c';
var arr1 = str1.split('-');
console.log(arr1); // ["a", "b", "c"]
var arr2 = str1.split('-', 1); // 第二个参数为 截取数组的长度
console.log(arr2); // ["a"]

15、forEach

// forEach: 遍历数组,代替for循环
const strs = ["a", "b", "c"];
// 简单操作
strs.forEach(item => {
 console.log(item); //  a  b  c
});

// 复杂操作  item 当前元素 index 当前元素的索引 arr 当前元素所在的数组
strs.forEach((item, index, arr) => {
 console.log(item, index, arr);
});

// 结果
a 0 (3) ["a", "b", "c"]
b 1 (3) ["a", "b", "c"]
c 2 (3) ["a", "b", "c"]

15、map

// map: 数组映射, 返回结果数组
const ninjas = [
 {name: "Yagyu", weapon: "shuriken"},
 {name: "Yoshi", weapon: "katana"}
];
// 简单操作
const weapons = ninjas.map(ninja => ninja.weapon);
console.log(weapons);
// ["shuriken", "katana"]

// 复杂操作  item 当前元素 index 当前元素的索引 arr 当前元素所在的数组
ninjas.forEach((item, index, arr) => {
 console.log(item, index, arr);
});
// {name: "Yagyu", weapon: "shuriken"} 0 (2) [{…}, {…}]
// {name: "Yoshi", weapon: "katana"} 1 (2) [{…}, {…}]

16、every

// every: 数组全部元素满足某个条件, 返回true或false
const ninjas = [
 {name: "Yagyu", weapon: "shuriken"},
 {name: "Yoshi", weapon: "katana"}
];
// 简单操作
const weapons = ninjas.every(ninja => "name" in ninja);
console.log(weapons);
// true

// 复杂操作  item 当前元素 index 当前元素的索引 arr 当前元素所在的数组
ninjas.every((item, index, arr) => {
 console.log(item, index, arr);
});
// {name: "Yagyu", weapon: "shuriken"} 0 (2) [{…}, {…}]

17、some

// some: 数组部门元素满足某个条件, 返回true或false
const ninjas = [
 {name: "Yagyu", weapon: "shuriken"},
 {weapon: "katana"}
];
// 简单操作
const weapons = ninjas.some(ninja => "name" in ninja);
console.log(weapons);
// true

// 复杂操作  item 当前元素 index 当前元素的索引 arr 当前元素所在的数组
ninjas.some((item, index, arr) => {
 console.log(item, index, arr);
});
// {name: "Yagyu", weapon: "shuriken"} 0 (2) [{…}, {…}]
// {weapon: "katana"} 1 (2) [{…}, {…}]

18、find

// find: 数组单个查找,返回单个数组
const ninjas = [
 {name: "Yagyu", weapon: "shuriken"},
 {weapon: "katana"}
];
// 简单操作
const weapons = ninjas.find(ninja => ninja.weapon === "shuriken");
console.log(weapons);
// {name: "Yagyu", weapon: "shuriken"}

// 复杂操作  item 当前元素 index 当前元素的索引 arr 当前元素所在的数组
ninjas.find((item, index, arr) => {
 console.log(item, index, arr);
});
// {name: "Yagyu", weapon: "shuriken"} 0 (2) [{…}, {…}]
// {weapon: "katana"} 1 (2) [{…}, {…}]

19、filter

// filter: 数组过滤,返回满足条件的所有数组
const ninjas = [
 {name: "Yagyu", weapon: "shuriken"},
 {weapon: "katana"}
];
// 简单操作
const weapons = ninjas.filter(ninja => "weapon" in ninja);
console.log(weapons);
//  [{…}, {…}]

// 复杂操作  item 当前元素 index 当前元素的索引 arr 当前元素所在的数组
ninjas.filter((item, index, arr) => {
 console.log(item, index, arr);
});
// {name: "Yagyu", weapon: "shuriken"} 0 (2) [{…}, {…}]
// {weapon: "katana"} 1 (2) [{…}, {…}]

20、indexOf、lastIndexOf、findIndex

// indexOf:  查找下标,返回第一次满足条件的下标
const strs = ["a", "b", "c"];
const tmp = strs.indexOf("a");
console.log(tmp); // 0

// lastIndexOf:返回最后一次满足条件的下标
const strs = ["a", "b", "c", "a"];
const tmp = strs.lastIndexOf("a");
console.log(tmp); // 3

// findIndex:使用回调函数获取数组下标,返回第一个满足条件的下标
const strs = ["a", "b", "c", "a"];
const tmp = strs.findIndex(item => item === "c");
console.log(tmp); // 2

21、reduce

// reduce:  合计数组元素
const numbers = [1, 2, 3, 4];
// total 初始值  number 当前元素  index 当前元素的索引 arr 当前元素所属的数组对象 0 标识初始值
const sum = numbers.reduce((total, number, index, arr) => total + number, 0); 
console.log(sum); // 10

二、类数组

类数组:对象表示数组,特点如下:

  1. 具有索引属性(数字)
  2. 有length属性;
  3. 最好加上push属性

1、类数组示例:

function test() {
    // arguments 没有继承 Array.prototype
    console.log(arguments);
}
test(1, 2, 3, 4, 5); // Arguments(5) [1, 2, 3, 4, 5]
// Arguments(5) [
//     0: 1
//     1: 2
//     2: 3
//     3: 4
//     4: 5
//     length: 5
// ]

2、类数组面试题:

var obj = {
    '2': 3,
    '3': 4,
    'length': 2,
    'splice': Array.prototype.splice,
    'push': Array.prototype.push
}

obj.push(1);
obj.push(2);
console.log(obj); // Object(4) [empty × 2, 1, 2, length: 4]

// 类数组 push 原理
// Array.prototype.push = function(elem) {
//     this[this.length] = elem;
//     this.length++;
// }

// obj[2] = 1;
// obj[3] = 2;

3、类数组转化为数组

// 类数组转化为数组
function test() {
    // 由于slice()不传参数为copy一个新数组,call可以转化为数组
    var arr = Array.prototype.slice.call(arguments)
    console.log(arr);
}
test(1, 2, 3); // [1, 2, 3]

四、封装示例

1、自定义 unshift

// 自定义 unshift
var arr = ['d', 'e', 'f'];

// 1、使用类数组转化为数组实现
Array.prototype.myUnshift = function() {
    var argArr = Array.prototype.slice.call(arguments);
    var newArr = argArr.concat(this);
    return newArr;
}
var newArr = arr.myUnshift('a', 'b', 'c');
console.log(newArr); // ["a", "b", "c", "d", "e", "f"]

// 2、循环实现
Array.prototype.myUnshift = function() {
    var pos = 0;
    for (var i = 0; i < arguments.length; i++) {
        this.splice(pos, 0, arguments[i]);
        pos ++;
    }
    return this.length;
}
var newArr = arr.myUnshift('a', 'b', 'c');
console.log(newArr); // 6
console.log(arr); // ["a", "b", "c", "d", "e", "f"]

2、原型上自定义数组去重方法

// 原型上自定义数组去重方法
var arr = [0,0,1,1,1,2,2,2,3,3,3,'a','a'];

Array.prototype.unique = function() {
    var temp = {},
        newArr = [];

    for (var i = 0; i < this.length; i++) {
        // 解决0的过滤
        if (!temp.hasOwnProperty(this[i])) {
            // tmp[0] = 0
            temp[this[i]] = this[i];
            newArr.push(this[i]);
        }
    }
    return newArr;
}

console.log(arr.unique()); // [0, 1, 2, 3, "a"]

3、字符串去重

// 字符串去重
var str = '11112222000aaabbbb';
String.prototype.unique = function() {
    var temp = {},
        newStr = '';

    for (var i = 0; i < this.length; i++) {
        // 解决0的过滤
        if (!temp.hasOwnProperty(this[i])) {
            // tmp[0] = 0
            temp[this[i]] = this[i];
            newStr += this[i];
        }
    }
    return newStr;
}

console.log(str.unique()); // '120ab'

4、面试题:找出第一个不重复的字符

// 找出第一个不重复的字符
var str = 'trueahahhassjekkekekckckkclll';
function test(str) {
    var temp = {};

    // 先计算实现
    for (var i = 0; i < str.length; i++) {
        if (temp.hasOwnProperty(str[i])) {
            temp[str[i]]++;
        } else {
            temp[str[i]] = 1;
        }
    }

    // 找出计算为1的元素
    for(var key in temp) {
        if (temp[key] === 1) {
            return key;
        }
    }
}

console.log(test(str)); // 't'