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方法的回调函数有三个参数,elem为
- 将成员依次传入函数,每次执行的结果,返回一个新的数组
- 返回新的数组
简单的实现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变量。
- 参数1,是一个函数,接收三个参数
- 注意,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变量。
- 参数1 - 一个函数,所有数组成员依次执行该函数,返回结果为true的成员组成一个新数组返回。该方法不会改变原数组。 此函数有三个参数:
简单的实现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变量。
- 参数1: 参数是函数,也有三个参数:
简单的实现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变量。
- 参数1: 参数是函数,也有三个参数:
简单的实现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是唯一一个不等于自身的值。