• 面试官:能用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过程中遇到的问题,记录探究的过程。通过一系列手写原生方法,锻炼了我们对于原理的进一步认识。但是也要注意验证,实际去操作几次,可能得出自己的经验。如果有更多的两者对比的发现,可以在评论里告诉我,欢迎各位大佬斧正。

  • 相关阅读:
    java 循环跳转控制语句
    webpack react npm start报错解决 ERR_OSSL_EVP_UNSUPPORTED
    AI 入门指南二 :AI提示词(Prompt)
    MySQL 基础
    【Opencv实战】识别水果的软件叫什么?一款超好用的识别软件分享,一秒鉴定(真是活~久~见~啊)
    MyBatis学习:使用占位符#
    Nginx原理以及基础知识详解
    Lodop 实现局域网打印
    Next.js项目初始化(附gitHub地址)
    Spring Kafka—— KafkaListenerEndpointRegistry 隐式注册分析
  • 原文地址:https://blog.csdn.net/helloworld1024fd/article/details/127608892