• 面试官:能用JavaScript手写一个bind函数吗


    经常会看到网上各种手写bind的教程,下面是我在自己实现手写bind的过程中遇到的问题与思考。如果对于如何实现一个手写bind还有疑惑的话,那么可以先看看上面两篇文章。

    手写bind vs 原生bind

    我们先使用一个典型的手写bind的例子,代码如下:

    Function.prototype.bind2 = function (context) {
        if (typeof this !== "function") {
          throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
        }
    
        var self = this;
        var args = Array.prototype.slice.call(arguments, 1);
    
        var fNOP = function () {};
    
        var fBound = function () {
            var bindArgs = Array.prototype.slice.call(arguments);
            return self.apply(this instanceof fNOP ? this : context, args.concat(bindArgs));
        }
    
        fNOP.prototype = this.prototype;
        fBound.prototype = new fNOP();
        return fBound;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    我们首先用原生bind运行一下代码

    function Foo(a) {this.a = a}
    Foo.prototype.sayHi = function( ) {}
    let _Foo = Foo.bind(undefined, 'a')
    new _Foo() 
    
    • 1
    • 2
    • 3
    • 4

    原生bind

    然后使用手写版代码,运行同样的代码

    function Foo(a) {this.a = a}
    Foo.prototype.sayHi = function( ) {}
    let _Foo = Foo.bind2(undefined, 'a')
    new _Foo() 
    
    • 1
    • 2
    • 3
    • 4

    多一层__proto__

    我们可以看到相比原生bind方法,手写版的bind方法返回的构造函数,构造出来的新对象会比原生的多一层__proto__。而这个__proto__产生的原因就是在很多教程中提到的防止原型链篡改

    这也就是为什么很多的文章会告诉你,为什么要添加下面的代码。

    var fNOP = function () {};
    fNOP.prototype = this.prototype;
    fBound.prototype = new fNOP();
    
    • 1
    • 2
    • 3

    这段代码中,使用了一个空函数作为中转,相当于Object.create(fBound.prototype)。具体可以查看文章开头给出的文章,里面的详细的说明。参考:前端手写面试题详细解答

    规范中的bind

    既然说道,加上面的代码是为了防止原型链篡改。我就想看看原生的bind如何处理这个问题的呢?

    function Foo(a) {this.a = a}
    Foo.prototype.sayHi = function( ) {}
    let _Foo = Foo.bind(undefined, 'a')
    _Foo.prototype.sayHi = function( ) {console.log('篡改的_Foo的sayHi方法')}
    (new _Foo().sayHi())
    
    • 1
    • 2
    • 3
    • 4
    • 5

    我发现在运行上面的代码,程序执行到修改_Foo的原型方法的语句时,就已经报错了。提示表明_Foo没有prototype属性!既然没有prototype属性,那么是不是也就不用处理原型链篡改的问题了呢?

    之后,我查了一下规范, 在NOTE中,有下面一段话。明确指出了bind返回的函数是没有prototype属性,这也多少印证了上面的猜想。

    Function objects created using Function.prototype.bind do not have a prototype property or the [[Code]], [[FormalParameters]], and [[Scope]] internal properties.

    其中需要注意的有一点是这条:

    1. Set the [[Prototype]] internal property of F to the standard built-in Function prototype object as specified in 15.3.3.1.

    我自己理解的意思是是bind出来的函数对象的prototype属性是内建的Function.prototype属性, 这里应该是说明了为什么原生的bind不会多一层__proto__属性

    小结

    写这篇的目的是总结下自己在实现bind过程中遇到的问题,记录探究的过程。通过一系列手写原生方法,锻炼了我们对于原理的进一步认识。但是也要注意验证,实际去操作几次,可能得出自己的经验。如果有更多的两者对比的发现,可以在评论里告诉我,欢迎各位大佬斧正。

  • 相关阅读:
    【力扣每日一题】2023.9.12 课程表Ⅳ
    java18-枚举类和注解
    RK3399平台开发系列讲解(进程篇)15.36、理解进程和协程
    Java异常、SpringBoot中全局捕获处理异常
    数字化时代的探索,企业如何做好数字化转型?
    2021-09-29破解小米“铁蛋”,只需9999元,你也可以做一个四足机器人!
    QT笔记——qInstallMessageHandler消息日志
    SSH登陆无权限
    11-Java中常用的API
    linux 拾遗之一
  • 原文地址:https://blog.csdn.net/helloworld1024fd/article/details/127608892