JavaScript精华笔记:ES5数组新增函数的源码实现(1)

本篇文章中,对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函数可传入两个参数:functionObject。而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的元素被创建成新数组返回了。

剩下的数组内置函数的源码实现,将持续更新,请上个人主页浏览。。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值