概要
我们在前端开发过程中,经常使用到各种数组的原生方法。为了更好的理解和使用这些原生方法,所以笔者试着重写了这些方法,并实现了相同的功能。
本文主要对find,findIndex,filter,forEach这四个原型方法进行重写。
代码实现
由于新方法也要定义在原型链上,为了避免重复,所以本文参考C#的LINQ方法命名。具体如下:
原型方法名称 | 重写后的方法名称 |
---|---|
find | First |
findIndex | firstIndex |
filter | Where |
forEach | Each |
find 方法重写
实现功能:在数组内找到第一个符合搜索条件的元素,并返回该元素,如果没有符合条件的元素,返回undefined。
不对原数组内元素进行修改,返回值如果是JS对象,直接返回数组内元素,不对返回值进行克隆。
Array.prototype.First = function(fn){
var self = this;
var length = self.length;
var newThis = arguments[1] || window;
var item;
for(var i=0; i<length; ++i){
item = deepClone(self[i]);
var res = fn.apply(newThis,[item, i, self]);
if (res){
return self[i];
}
}
return;
}
- 和JS原生的find方法一样,固定参数只有一个,类型为Fucntion,该方法返回一个bool值。
- 根据ES5的this使用规则,谁调用该方法,该方法内的this指向谁,所以该方法内的this指向未来调用该方法的数组对象。
- 和JS原生的find方法一样,First方法的第二个参数是可选的。它是新新的this,如果该参数不为空,则First方法内回调方法的this指向该对象。
- 遍历数组,数组元素在使用之前进行深度克隆,避免使用中被篡改,deepClone方法详见附录。
- 调用回调方法fn,检查传入的元素是否满足搜索条件。其中,fn方法的每个参数都是可选的,并且需要固定fn方法内的this,所以采用apply方法进行调用。
- 找到第一个符合搜索条件的元素后立刻返回。
findIndex 方法重写
实现功能:在数组内找到第一个符合搜索条件的元素,并返回该元素的索引,如果没有符合条件的元素,返回 -1。
不对原数组内元素进行修改,返回值如果是JS对象,直接返回数组内元素,不对返回值进行克隆。
Array.prototype.FirstIndex = function(fn){
var self = this;
var length = self.length;
var newThis = arguments[1] || window;
var item;
for(var i=0; i<length; ++i){
item = deepClone(self[i]);
var res = fn.apply(newThis,[item, i, self]);
if (res){
return i;
}
}
return -1;
}
- 和JS原生的findIndex方法一样,固定参数只有一个,类型为Fucntion,该方法返回一个bool值。
- 根据ES5的this使用规则,谁调用该方法,该方法内的this指向谁,所以该方法内的this指向未来调用该方法的数组对象。
- 和JS原生的findIndex方法一样,FirstIndex方法的第二个参数是可选的。它是新新的this,如果该参数不为空,则FirstIndex方法内回调方法的this指向该对象。
- 遍历数组,数组元素在使用之前进行深度克隆,避免使用中被篡改,deepClone方法详见附录。
- 调用回调方法fn,检查传入的元素是否满足搜索条件。其中,fn方法的每个参数都是可选的,并且需要固定fn内的方法内的this,所以采用apply方法进行调用。
- 找到第一个符合搜索条件的元素后立刻返回该元素的索引。
filter 方法重写
实现功能:在数组内找到第全部符合搜索条件的元素,并返以数组的形式返回所有符合条件的元素,如果没有符合条件的元素,返回空数组。
不对原数组内元素进行修改,返回值如果是JS对象,直接返回数组内元素,不对返回值进行克隆。
Array.prototype.Where = function(fn){
var self = this;
var length = self.length;
var newThis = arguments[1] || window;
var res = [];
var item;
for(var i=0; i<length; ++i){
item = deepClone(self[i]);
var filter = fn.apply(newThis,[item, i, self]);
filter && res.push(self[i]);
}
return res;
}
- 和JS原生的filter方法一样,固定参数只有一个,类型为Fucntion,该方法返回一个bool值。
- 根据ES5的this使用规则,谁调用该方法,该方法内的this指向谁,所以该方法内的this指向未来调用该方法的数组对象。
- 和JS原生的filter方法一样,Where方法的第二个参数是可选的。它是新新的this,如果该参数不为空,则Where方法内回调方法的this指向该对象。
- 遍历数组,数组元素在使用之前进行深度克隆,避免使用中被篡改,deepClone方法详见附录。
- 调用回调方法fn,检查传入的元素是否满足搜索条件,满足条件的放入返回数组中。其中,fn方法的每个参数都是可选的,并且需要固定fn方法内的this,所以采用apply方法进行调用。
forEach方法重写
实现功能:遍历数组内每个元素,并执行传入的方法,无返回值。不对原数组内元素进行修改。
Array.prototype.Each = function(fn){
var self = this;
var length = self.length;
var newThis = arguments[1] || window;
var item;
for(var i=0; i<length; ++i){
item = deepClone(self[i]);
fn.apply(newThis, [item, i, self]);
}
return;
}
- 和JS原生的filter方法一样,固定参数只有一个,类型为Fucntion,该方法无返回值。
- 根据ES5的this使用规则,谁调用该方法,该方法内的this指向谁,所以该方法内的this指向未来调用该方法的数组对象。
- 和JS原生的forEach方法一样,Each方法的第二个参数是可选的。它是新新的this,如果该参数不为空,则方法内回调方法的this指向该对象。
- 遍历数组,数组元素在使用之前进行深度克隆,避免使用中被篡改,deepClone方法详见附录。
- 调用回调方法fn,检查传入的元素是否满足搜索条件,满足条件的放入返回数组中。其中,fn方法的每个参数都是可选的,并且需要固定fn方法内的this,所以采用apply方法进行调用。
附录
function deepClone(origin, map = new WeakMap()){
if (origin == undefined || typeof origin !== "object"){
return origin;
}
if (origin instanceof Date){
return new Date(origin);
}
if (origin instanceof RegExp){
return new RegExp(origin);
}
var copied = map.get(origin);
if (!!copied){
return copied;
}
let target = new origin.constructor();
map.set(origin, target);
let keys = Reflect.ownKeys(origin);
for(let key of keys){
target[key] = deepClone(origin[key], map);
}
return target;
}