【日拱一卒】JavaScript手写apply、call、bind

这是我参与8月更文挑战的第26天,活动详情查看: 8月更文挑战

前言

这几天刷题目看文章的时候,发现手写apply、call、bind代码,我能看懂了。而且觉得缺少些有趣的细节。所以有了这篇文章,一方面是梳理这些函数的手写,一方面也是回顾JavaScript执行机制。

手写apply

call方法除了执行函数传参形式不太一样,其他地方都一样。

一些破局点:

  • 关于apply中的this指向。this指向apply方法的调用者,即执行函数本身。例如已声明函数f,f.apply(context, array)。此时,apply函数中的this指向了f

  • apply的功能就是将函数f中的this指向context并执行函数。this指向只有三种途径

  • 函数执行,this指向全局对象。

  • 作为对象的方法被调用,this指向对象。

  • new + 构造函数,this指向新生成的对象

  • 想改变函数中的this指向,明显是通过第二条途径,于是在对象上context添加一个方法f就很自然

  • 执行完之后再删除对象上的f方法,返回函数的返回值。一切又完好如初

    Function.prototype.apply = function (context, array) {
    context || (context=window)
    let func = this
    let caller = Symbol(“caller”)
    context[caller] = func
    let res = contextcaller delete context[caller]
    return res
    }

手写call

与上面相同,注意接收的参数的写法

Function.prototype.call = function (context, ...args) {
    context || (context=window)
    let func = this
    let caller = Symbol("caller")
    context[caller] = func
    let res = context[caller](...args)    
    delete context[caller]    return res
}
复制代码

手写bind

bind返回的是一个函数,而非函数的执行结果

  • 这里通过this instanceof F 判断是否是new的调用,并通过闭包保存了父作用域中的this还是很有味道的。

    Function.prototype.bind = function(context, …args) {
    if (typeof this !== ‘function’) {
    throw new Error(“Type Error”);
    }
    // 保存this的值
    var self = this;

    return function F() {
    // 考虑new的情况
    if(this instanceof F) {
    return new self(…args, …arguments)
    }
    return self.apply(context, […args, …arguments])
    }
    }

如果想参考一个复杂的版本

可以参考MDN bind之Polyfill

最后附上一个自己填补了一些异常校验的手写apply。

这里并没有考虑到new 操作符。主要是因为浏览器中实现的使用new去调用apply,浏览器会报一个f.apply is not a constructor的错误,而自己通过Function.prototype.apply去实现的apply,就算把prototype的constructor置为空,也不会走向这里,感觉这里深入下去阻力大了一些,目前的功力还是不够。

// 附加一个考虑更多异常情况的版本
Function.prototype.apply = function (context, array) {
    if(typeof context !=="object"&&typeof context !=="function"){
        throw new TypeError('context is not a object')
    }
    if(Object.getPrototypeOf(array)[@@iterator]){
        throw new Type Error("CreateListFromArrayLike called on non-object")
    }
    context||(context=window)
    let func = this
    let caller = Symbol("caller")
    context[caller] = func
    let res = context[caller](...array)
    delete context[caller]
    return res
}
复制代码

PS:手写代码试了试水,感觉到因为自己JS相关体系还是有明显薄弱处,以及对底层规范的畏惧感,导致了这并不是很高效的梳理。如果,抱着学习的心态,又难以谈出自己的想法,有点人云亦云的感受。只能以学习为主,围绕一两个感兴趣的点进行适当的挖掘,保护自己自信的同时也能有所收获。把注意力放在JS的知识体系的拾遗上,而不是过度研究细节。

简而言之,按自己目前的水平,理应把注意力放在面的扩展上,不要把时间过度浪费在对于某些点的深入探究。日拱一卒,而非毕其功于一役,心态要好,要更冷静一些。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享