写点什么

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

  • 2023-01-03
    浙江
  • 本文字数:1426 字

    阅读完需:约 5 分钟

经常会看到网上各种手写 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;}
复制代码


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


function Foo(a) {this.a = a}Foo.prototype.sayHi = function( ) {}let _Foo = Foo.bind(undefined, 'a')new _Foo() 
复制代码



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


function Foo(a) {this.a = a}Foo.prototype.sayHi = function( ) {}let _Foo = Foo.bind2(undefined, 'a')new _Foo() 
复制代码



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


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


var fNOP = function () {};fNOP.prototype = this.prototype;fBound.prototype = new fNOP();
复制代码


这段代码中,使用了一个空函数作为中转,相当于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())
复制代码


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


用户头像

还未添加个人签名 2022-07-31 加入

还未添加个人简介

评论

发布
暂无评论
面试官:能用JavaScript手写一个bind函数吗_JavaScript_helloworld1024fd_InfoQ写作社区