本篇文章中,对forEach、filter、map、Every、Some、reduce和reduceRight等函数,讲述了如何自己编写代码实现它们的功能。
通过阅读源码,自己编写源码,能了解编写思想、熟悉设计模式,能锻炼自己编写组件、框架的能力。
试验对象
所有的函数源码编写出来后,以这段数据作为试验对象:
var personArr = [
{name: '王港', src: './src/img/3.png', des: '颈椎不好', sex: 'm', age: 20},
{name: '刘莹', src: './src/img/5.png',des: '我是谁', sex: 'f', age: 25},
{name: '王秀莹', src: './src/img/4.png', des: '我很好看', sex: 'f', age: 16},
{name: '刘金雷', src: './src/img/1.png', des: '你没有见过陌生的脸', sex: 'm', age: 35},
{name: '刘飞翔', src: './src/img/2.png', des: '瓜皮刘', sex: 'm', age: 40}
];
apply函数
本次所有的源码编写都使用到了apply函数,这里先对apply函数的作用进行简单的介绍。
apply(),能够接受obj和args两个参数(args为默认参数)。
function.apply(obj,args)
这两个参数会传递给调用apply方法的函数function,并按function内容执行(apply里的参数顺序,对应的是函数里的形参位置)。obj会替代function里的this对象,args是数组或零散的参数,这些参数会传递给调用apply方法的函数。
实现forEach,源码编写
forEach() 方法可以遍历数组中的每一个元素,并将传入的每一个数组元素按调用的函数执行。
所以forEach函数可传入两个参数:function和Object。而forEach中的参数function又自带三个形参element,index,arrayself。
其总的语法规则为:
Array.forEach(function(element,index,arrayself){},Object)
在编写myForeach之前,我们先定义一个myForeach内的第一个参数,执行元素的function,调用myForeach的数组中的每一个元素,都会被遍历然后被function执行。
function deal (ele, index, self) {
console.log(ele, index, self, this);
}
这个function的作用是将数组的每一个元素、索引值、数组本身及forEach第二个参数obj都打印在控制台上。
myForeach源码如下:
//给数组添加新的原型方法myForEach
Array.prototype.myForEach = function (func) {
var len = this.length; //获取数组的元素个数
var _this = arguments[1] != undefined ? arguments[1] : window;
for (var i = 0;i < len; i++){
//apply第一个参数_this将代替func里的this对象,而apply第二参数中的this指向的是personArr。
func.apply(_this,[this[i],i,this]);
}
}
这里要注意,当对象调用函数时obj.func();
,func()里面的this指向obj。在myForEach里定义了apply函数,记住它的第一位参数是改变this指向的,也就是将通过内部定义的_this将myForEach里的第二个参数或window传递给func。
执行一下这个程序:
var obj = {name : "zc"}; //外部定义一个对象作为myForEach第二个参数
personArr.myForEach(deal,obj);
-------------------------------------------
{name: "王港", src: "./src/img/3.png", des: "颈椎不好", sex: "m", age: 20} 0 (5) [{…}, {…}, {…}, {…}, {…}] {name: "zc"}
{name: "刘莹", src: "./src/img/5.png", des: "我是谁", sex: "f", age: 25} 1 (5) [{…}, {…}, {…}, {…}, {…}] {name: "zc"}
{name: "王秀莹", src: "./src/img/4.png", des: "我很好看", sex: "f", age: 16} 2 (5) [{…}, {…}, {…}, {…}, {…}] {name: "zc"}
{name: "刘金雷", src: "./src/img/1.png", des: "你没有见过陌生的脸", sex: "m", age: 35} 3 (5) [{…}, {…}, {…}, {…}, {…}] {name: "zc"}
{name: "刘飞翔", src: "./src/img/2.png", des: "瓜皮刘", sex: "m", age: 40} 4 (5) [{…}, {…}, {…}, {…}, {…}] {name: "zc"}
在personArr.myForEach里面,myForEach的this就是指向的personArr。而在myForEach里又单独定义了apply,它的第一位参数是改变this指向的,将myForEach里的第二个参数或window传递给func。
实现filter,源码编写
Array.prototype.filter它是对数组进行过滤,所以要基于数组的遍历。filter同样有参数,参数和forEach是一样的,是个函数,函数同样有四个形参function(element,index,arrayself,this),
注意!fileter执行完以后,返回的是一个新的数组。
Array.filter(function(element,index,arrayself){},Object)
那么过对forEach的详细解释后,接下来就都直接上源码和一些小解释了。
首先,还是先来编写一个function:
function deal(ele,index,self) {
return ele.sex == "m";
}
函数作用为,数组中的元素如果性别为male则返回true,否则返回false。
myFilter源码内容如下:
Array.prototype.myFilter = function (func) {
var arr = [];//创建一个空数组
var len = this.length;
//这里的判断为前面如果为真,后边的就不执行了,同样能达到三目运算符的效果。
var _this = arguments[1] || window;
for (var i = 0;i < len; i++){
//这里是前边为真还会继续向后执行,前面为假后边就直接不看了。
func.apply(_this,[this[i],i,this]) && arr.push(this[i])
//三目运算符 ? arr.push(this[i] : ""
}
return arr;
}
下面这段有些难理解
func.apply(_this,[this[i],i,this]) && arr.push(this[i])
代码转化一下可以像下面这样理解,deal函数执行为true则向aar添加元素,执行为false则不执行push函数。
deal([this[i])&& arr.push(this[i])
下面测试一下myFilter函数执行结果:
var obj = {name : "zc"}
var newArr = personArr.myFilter(deal,obj);
------------------------------------------
[{name: "王港", src: "./src/img/3.png", des: "颈椎不好", sex: "m", age: 20},
{name: "刘金雷", src: "./src/img/1.png", des: "你没有见过陌生的脸", sex: "m", age: 35},
{name: "刘飞翔", src: "./src/img/2.png", des: "瓜皮刘", sex: "m", age: 40}]
性别为male的元素被创建成新数组返回了。
剩下的数组内置函数的源码实现,将持续更新,请上个人主页浏览。。。。。