apply()的实现

266 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第9天,点击查看活动详情

前言

昨天手写了一个call()的实现,具体的内容可以点击链接查看,今天来写一下apply()的实现,来加深一下记忆。

apply()和call()

在手写apply()之前就不得不提一嘴apply和call的关系了,它们两个既有共同点又有不通点。

  • 共同点
  1. 它们都可以改变this的指向
  2. 都可以传递参数给目标函数
  • 不同点
  1. 它们的参数形式不一样

基于上面的不同点和相同点,我们可以很容易的得出结论,那就是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))

结果如下图

image.png

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]))

结果如下图

image.png

传递进去的参数成功的打印了出来,说明apply的主要功能基本实现了

总结

call()和apply()的实现逻辑其实差不多,主要的区别就是在使用的时候参数的类型及个数问题。但其实上面的实现还存在这一些问题。

  1. 代码中的fn是我们假定的context中没有这个属性,如果context已经有这个属性了那就会出问题了(这里的解决办法可以使用var fn = Symbol())但这用到了es6的特性,如果不让这么用就写一个随机数和fn的组合,然后判断一下context是否含有这个字段了,如果含有就从新生成,知道没这个属性为止。 2.传入的context可能不是一个对象(有个能是个基本的数据类型,比如string),这时候我们要对context做一下处理,那就context= Object(context) || window,这样就完美了。