JavaScript中自定义实现Array方法

JavaScript中自定义实现Array方法

isArray

  • 判断是否为数组
  • 提供一下方法
function isArray(arr) {
    // 方法1
    return arr.constructor == Array;
    
    // 方法2
    // https://github.com/nianxiongdi/fore-end/issues/4

    // 方法3
    // Array.isArray

    // 方法4 
    // arr instanceof Array
}

console.log(isArray([1,2,3])) // true
console.log(isArray(123)) // false

join

  • 连接字符串
console.log([1,2,3].join(',')); // 1,2,3

//通过call方法,这个方法也可以用于字符串或类似数组的对象。
console.log(Array.prototype.join.call("abc", '-')); //a-b-c

console.log(Array.prototype.join.call([1,2,3], '-')); //1-2-3

var obj = { 0: 'a', 1: 'b', length: 2 };
Array.prototype.join.call(obj, '-') // 'a-b'

concat

  • 多个数组的连接, 返回新的数组
  • 然后返回一个新数组,原数组不变。

[1, 2, 3].concat(4, 5, 6)
// [1, 2, 3, 4, 5, 6]

[1, 2, 3].concat([4, 5, 6])
// [1, 2, 3, 4, 5, 6]

[].concat({a: 1}, {b: 2})
// [{ a: 1 }, { b: 2 }]

[2].concat({a: 1})
// [2, {a: 1}]

reverse

  • 数组的翻转
  • 返回改变后的数组。注意,该方法将改变原数组
let a = [1,2,3];
let arr = a.reverse()
arr // [3,2,1]

slice

  • 对数组进行分割
  • 返回新数组,原数组不变。
  • arr.slice(开始位置, 结束位置(不包含));

a = ['asdsa','dsadassa','csadsadsa']
a.slice(0) //["asdsa", "dsadassa", "csadsadsa"]
a.slice(1) // ["dsadassa", "csadsadsa"]
a.slice(1,2) //["dsadassa"]
a.slice(2,6) //["csadsadsa"]
a.slice() //["asdsa", "dsadassa", "csadsadsa"]

应用 - 类似数组的对象转为真正的数组

  • 参数都不是数组,但是通过call方法,在它们上面调用slice方法,就可以把它们转为真正的数组。
Array.prototype.slice.call({ 0: 'a', 1: 'b', length: 2 })
// ['a', 'b']

Array.prototype.slice.call(document.querySelectorAll("div"));
Array.prototype.slice.call(arguments);

splice

  • 删除原来的数组元素,并且可以在删除数组元素的位置,添加新的元素
  • 返回删除元素的列表
  • arr.splice(start, length, addElement1, addElement2, …); length代表删除元素的长度

a //["a", "b", "c", "d", "e", "f"]

a.splice(4,2) // 返回删除的元素 ["e", "f"]

a // ["a", "b", "c", "d"]

a.splice(1, 2, 'add1', 'add2'); // ["b", "c"]

a //["a", "add1", "add2", "d"]

应用 - 插入元素

  • 如果只是单纯地插入元素,splice方法的第二个参数可以设为0。

a = ["a", "b", "c"]

//插入到第一个元素的后面
a.splice(1,0,2); //[] 因为没有删除元素,所以返回为[]

a // ["a", 2, "b", "c"]

sort

  • sort方法对数组成员进行排序,默认是按照字典顺序排序。排序后,原数组将被改变。
['d', 'c', 'b', 'a'].sort()
// ['a', 'b', 'c', 'd']

[4, 3, 2, 1].sort()
// [1, 2, 3, 4]


[10111, 1101, 111].sort(function (a, b) {
  return a - b;
})
// [111, 1101, 10111]

// 对象排序
[
  { name: "张三", age: 30 },
  { name: "李四", age: 24 },
  { name: "王五", age: 28  }
].sort(function (o1, o2) {
  return o1.age - o2.age;
})
// [
//   { name: "李四", age: 24 },
//   { name: "王五", age: 28  },
//   { name: "张三", age: 30 }
// ]

map

  • [].map(function(elem, index, arr){})
    • map方法的回调函数有三个参数,elem为当前成员的值,index为当前成员的位置,arr为原数组
  • 将成员依次传入函数,每次执行的结果,返回一个新的数组
  • 返回新的数组

简单的实现map


/**
 * 参数1 - 回调函数
 * 参数2 - 改变this指向,此时指向obj 
 **/
Array.prototype.map = function() {

    let len = arguments.length;
    let new_arr = [];
    if(len === 1) {
        for(let i=0; i<this.length; i++) {
            new_arr.push( arguments[0].call(this, this[i]) );
        }
    }else if(len === 2) {
            new_arr.push( arguments[0].call(arguments[1], this[i]) );;
        }
    }

    return new_arr;
}

// test1
let arr = [1, 2, 3, 4, 5];
let new_arr = arr.map(function(item) {
    return item * item;
});
console.log( new_arr ); // [1, 4, 9, 16, 25];

// test2

arr = ['a', 'b', 'c'];
let new_arr = [1, 2].map(function (e) {
    return this[e];
}, arr);
console.log(new_arr); // ['b', 'c']

// test3

var f = function (n) { return 'a' };
console.log([1, undefined, 2].map(f)) // ["a", "a", "a"] map方法不会跳过undefined和null,但是会跳过空位。

forEach

  • 两个参数
    • 参数1,是一个函数,接收三个参数当前值当前位置整个数组
    • 绑定参数函数的this变量。
  • 注意,forEach方法无法中断执行,总是会将所有成员遍历完。如果希望符合某种条件时,就中断遍历,要使用for循环。
    • arr.forEach(function(item) {console.log(item);break;}); //Uncaught SyntaxError: Illegal break statement - 原因 - break只能在for循环中出现这里相当于是 funcrion func(){ break; } 会直接报错误的
  • 相当于是for循环
 let arr = [1, 2, 3, 4, 5];

Array.prototype.forEach = function() {
    if(arguments.length === 1) {
        for(let i=0; i<this.length; i++) {
            arguments[0].call(this, this[i]);
        }
    }else if(arguments.length === 2) {
        for(let i=0; i<this.length; i++) {
            arguments[0].call(arguments[1], this[i]);
        }
    }  
}

// test1
arr.forEach(function(item) {
    console.log(item);
}); 

// test2
var out = [];
[1, 2, 3].forEach(function(elem) {
    this.push(elem * elem);
}, out);
console.log( out ); // [1, 4, 9]

filter

  • 过滤数组成员满足条件的成员组成一个新数组返回
  • 参数
    • 参数1 - 一个函数,所有数组成员依次执行该函数,返回结果为true的成员组成一个新数组返回。该方法不会改变原数组。 此函数有三个参数:当前成员当前位置整个数组
    • 参数2 - 用来绑定参数函数内部的this变量。

简单的实现filter

let arr = [1, -2, 3, -4, 5];

Array.prototype.filter = function() {
    let new_arr = [];
    if(arguments.length === 1) {
        for(let i=0; i<this.length; i++) {
            const flag = arguments[0].call(this, this[i]); // 我这边只传递了当前成员, `当前位置`和`整个数组`。没有进行传递,加上后面就可以了
            flag&&new_arr.push(this[i]);
        }
    }else if(arguments.length === 2) {
        for(let i=0; i<this.length; i++) {
            const flag = arguments[0].call(arguments[1], this[i]);
            flag&&new_arr.push(this[i]);
        }
    }
    return new_arr;
}
// test1
let new_arr = arr.filter(function (item) {
    return (item > 0);
})
console.log( new_arr );//[1, 3, 5]

// test2
var obj = { MAX: 3 };
var myFilter = function (item) {
    if (item > this.MAX) 
        return true;
    };
arr = [2, 8, 3, 4, 1, 3, 2, 9];
new_arr = arr.filter(myFilter, obj) // [8, 4, 9]

some

  • 断言”(assert),返回一个布尔值,表示判断数组成员是否符合某种条件
  • some方法是只要一个成员的返回值是true,则整个some方法的返回值就是true,否则返回false。
  • 参数:
    • 参数1: 参数是函数,也有三个参数:当前成员当前位置整个数组
    • 参数2: some和every方法还可以接受第二个参数,用来绑定参数函数内部的this变量。

简单的实现some


 let arr = [1, -2, 3, -4, 5];

Array.prototype.some = function() {

    let fun = arguments[0];

    // 类型判断
    if(typeof fun != 'function') {
        throw new TypeError();
    }

    for(let i=0; i<this.length; i++) {
        if(fun.call(arguments[0], this[i])) 
            return true;
    }

    return false;
}
// test1
isArr = arr.some(function(item) {
    return item > 3;
});
console.log(isArr); // true

// test2
isArr = arr.some(function(item) {
    return item > 10;
});
console.log(isArr); // false

every

  • 断言”(assert),返回一个布尔值,表示判断数组成员是否符合某种条件
  • 所有成员的返回值都是true,整个every方法才返回true,否则返回false。
  • 参数:
    • 参数1: 参数是函数,也有三个参数:当前成员当前位置整个数组
    • 参数2: some和every方法还可以接受第二个参数,用来绑定参数函数内部的this变量。

简单的实现every

let arr = [1, -2, 3, -4, 5];

Array.prototype.every = function() {

    let fun = arguments[0];

    // 类型判断
    if(typeof fun != 'function') {
        throw new TypeError();
    }

    for(let i=0; i<this.length; i++) {
        if( !fun.call(this, this[i], i, this)) 
            return false;
    }

    return true;
}

//test1
isArr = arr.every(function(item) {
    return item > -10;
});
console.log(isArr); // true

// test2
isArr = arr.every(function(item) {
    return item > 10;
});
console.log(isArr); // false

// test3
function isEven(x) { return x % 2 === 0 }
[].some(isEven) // false
[].every(isEven) // true

reduce

  • 最终累计为一个值
  • 参数
    • 参数1: 为函数,接受两个参数;(累积变量(默认为数组的第一个成员), 当前值(默认为数组的第二个成员), 当前位置(从0开始), 原数组;
    • 参数2: 默认值,处理空数组时尤其有用。

简单的实现reduce


let arr = [ ];

Array.prototype.reduce = function() {

    let fun = arguments[0];

    // 类型判断
    if(typeof fun != 'function') {
        throw new TypeError();
    }

    let total = 0;
    if(arguments.length === 1) {
        for(let i=0; i<this.length; i++) {
            total = arguments[0].call(this, total, this[i]);
        }
        return total;
    }else if(arguments.length === 2) {
        total = arguments[1];
        for(let i=0; i<this.length; i++) {
            total = arguments[0].call(this, total, this[i]);
        }
        return total;
    }

}

// test1
let sum = [1, 2, 3, 4, 5].reduce(function (total, next) {
    return total + next;
})
console.log(sum); // 15

// test2
sum = [1, 2, 3, 4, 5].reduce(function (total, next) {
    return total + next;
}, 5)
console.log(sum); // 20



reduceRight

  • 则是从右到左(从最后一个成员到第一个成员),其他完全一样。
[1, 2, 3, 4, 5].reduce(function (a, b) {
  console.log(a, b);
  return a + b;
})
// 1 2
// 3 3
// 6 4
// 10 5
//最后结果:15


// 理解reduce和reduceRight
function substract(prev, cur) {
  return prev - cur;
}

[3, 2, 1].reduce(substract) // 0
[3, 2, 1].reduceRight(substract) // -4

应用 - 找出最长的字符串

function findLongest(entries) {
  return entries.reduce(function (longest, entry) {
    return entry.length > longest.length ? entry : longest;
  }, '');
}

findLongest(['aaa', 'bb', 'c']) // "aaa"

indexOf

  • 方法返回给定元素在数组中第一次出现的位置,如果没有出现则返回-1
  • indexOf方法还可以接受第二个参数,表示搜索的开始位置。
var a = ['a', 'b', 'c'];

a.indexOf('b') // 1
a.indexOf('y') // -1

['a', 'b', 'c'].indexOf('a', 1) // -1

lastIndexOf

  • 返回给定元素在数组中最后一次出现的位置,如果没有出现则返回-1
  • indexOf和lastIndexOf注意,这两个方法不能用来搜索NaN的位置,即它们无法确定数组成员是否包含NaN。
var a = [2, 5, 9, 2];
a.lastIndexOf(2) // 3
a.lastIndexOf(7) // -1

[NaN].indexOf(NaN) // -1
[NaN].lastIndexOf(NaN) // -1
// 这是因为这两个方法内部,使用严格相等运算符(===)进行比较,而NaN是唯一一个不等于自身的值。

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值