一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第9天,点击查看活动详情。
前言
昨天手写了一个call()的实现,具体的内容可以点击链接查看,今天来写一下apply()的实现,来加深一下记忆。
apply()和call()
在手写apply()之前就不得不提一嘴apply和call的关系了,它们两个既有共同点又有不通点。
- 共同点
- 它们都可以改变this的指向
- 都可以传递参数给目标函数
- 不同点
- 它们的参数形式不一样
基于上面的不同点和相同点,我们可以很容易的得出结论,那就是apply的手写代码和call的手写代码的主要区别应该就在传递参数的处理上。首先apply我们已经明确了它的参数个数,那我们就不用使用arguments了。下面看代码
Function.prototype.myApply= function(context,arr){
context = context || window
context.fn = this
var result;
if(!arr){
result = context.fn()
}else{
var args = []
for(var i=0;i<arr.length;i++){
args.push(arr[i])
}
result = context.fn(...args)
}
delete context.fn
return result
}
注:以上代码直接使用es6中的展开运算符,这里如果要求不让用es6的东西,那么也可以像call的实现那样,使用eval()函数即可(eval('context.fn(' + args +')'))。下面测试一下看有没有什么问题。首先测试一下this的指向是否改变了
animal = {color:'yellow'}
function dog(){
console.log(this.color)
}
console.log(dog.myApply(animal))
结果如下图
this指向成功改变了,下面再测试一下传递参数是否会有问题。
animal = {color:'yellow'}
function dog(name,age){
console.log(this.color)
console.log(name)
console.log(age)
return {
name:name,
age,age,
color:this.color
}
}
console.log(dog.myApply(animal,['小白',6]))
结果如下图
传递进去的参数成功的打印了出来,说明apply的主要功能基本实现了
总结
call()和apply()的实现逻辑其实差不多,主要的区别就是在使用的时候参数的类型及个数问题。但其实上面的实现还存在这一些问题。
- 代码中的fn是我们假定的context中没有这个属性,如果context已经有这个属性了那就会出问题了(这里的解决办法可以使用var fn = Symbol())但这用到了es6的特性,如果不让这么用就写一个随机数和fn的组合,然后判断一下context是否含有这个字段了,如果含有就从新生成,知道没这个属性为止。
2.传入的context可能不是一个对象(有个能是个基本的数据类型,比如string),这时候我们要对context做一下处理,那就
context= Object(context) || window,这样就完美了。