JS的bind方法用法是怎样,如何实现完整的bind

Admin 2022-08-06 群英技术资讯 342 次浏览

今天这篇给大家分享的知识是“JS的bind方法用法是怎样,如何实现完整的bind”,小编觉得挺不错的,对大家学习或是工作可能会有所帮助,对此分享发大家做个参考,希望这篇“JS的bind方法用法是怎样,如何实现完整的bind”文章能帮助大家解决问题。


之前写了一篇关于 JS 中 bind 方法的实现的文章,并给出了实现:
Function.prototype.myBind = function(thisArg, ...prefixArgs) {
  const fn = this;
  return function(...args) {
    return fn.call(thisArg, ...prefixArgs, ...args);
  }
}

但没有处理 通过 new 创建实例 的情况。

因为很少会遇到给 bind 返回的函数做 new 操作的场景,所以我没去考虑这种特殊情况。

但面试中还是会涉及到的,我们还是实现一下兼容 new 操作的 bind 写法,顺便学习一下 new 操作符。

因为存在一定上下文,在阅读本文前,建议先阅读前一篇文章:《前端面试题:手写 bind》。

new

我们先学习一下 new 操作符。

new 用于通过函数来创建一个对象实例,在很多语言中都能看到。

JS 的函数,除了可以是普通函数,比如:

function sum(a, b) {
  return a + b;
}

还可以是构造函数,只需要在构造时在它前面加一个 new:

function Person(name, age) {
 this.name = name;
 this.age = age;
}

const person = new Person('前端西瓜哥', 100)
// Person {name: '前端西瓜哥', age: 100}

new 创建一个新对象,做了下面几件事:

  • 创建一个空对象 {}
  • 空对象的原型属性 __proto__ 指向构造函数的原型对象 Person.prototype
  • 函数中的 this 设置为这个空对象;
  • 如果该函数不返回一个对象,就返回这个 this,否则返回这个对象。

判断函数是否通过 new 被调用

怎么判断一个函数正在被 new 操作符调用?

答案是 使用 instanceof 判断 this 是否为当前函数的实例,即 this instanceof Fn 为 true,表示在通过 new 构建实例。

我们看一个例子:

function Person() {
  if (this instanceof Person) {
    console.log('通过 new 构建实例');
  } else {
    console.log('普通调用')
  }
}

Person()     // 输出:普通调用
new Person() // 输出:通过 new 构建实例

在 Vuejs 的源码,你会看到下面代码,这里也用到了这个技巧。

function Vue(options) {
  if (__DEV__ && !(this instanceof Vue)) {
    warn('Vue is a constructor and should be called with the `new` keyword')
  }
  this._init(options)
}

你在开发环境如果不通过 new 来使用 Vue 对象,会在控制台提示你要通过 new 来调用 Vue。

new 和 bind

如果我们 new 的是 Function.prototype.bind 返回的新函数,会发生什么事情?

function Person(name, age) {
 this.name = name;
 this.age = age;
}

const BoundPerson = Person.bind(null, '前端西瓜哥');
const boundPerson = new BoundPerson(100);
// Person {name: '前端西瓜哥', age: 100}

boundPerson.__proto__ === Person.prototype
// true

结果等价于直接去 new 原始函数。

不同的是,仍旧可以进行参数的预置。可以看到,构造函数的第一个参数,在调用 bind 的时候就提前设置为 '前端西瓜哥'。

实现完整的 bind

完整实现如下:

Function.prototype.myBind = function(thisArg, ...prefixArgs) {
  const fn = this;
  const boundFn = function(...args) {
    // 通过 new 使用当前函数
    if (this instanceof boundFn) {
      return new fn(...prefixArgs, ...args);
    }
    // 普通的方法调用当前函数
    return fn.call(thisArg, ...prefixArgs, ...args);
  }
  boundFn.prototype = fn.prototype;
  return boundFn;
}

这里我通过 this instanceof boundFn 来判断是否用了 new,如果是,就直接 new 原始函数然后返回,记得带上 bind 预置好的参数。

其他保持原样。

boundFn.prototype = fn.prototype;  这个可写可不写,只是让 bind 返回的新函数的 prototype 指向原函数的 prototype。

如果是原生 bind 返回的函数,它是没有 protoype 属性的,可以认为它是一种特别的函数,而我们实现的 bind 返回的却是一个普通函数,所以并不能完全模拟的。

如果你 追求完美的实现,可以研读一下  Function.prototype.bind 的标准:

然后再看看知名的 core.js 库中对 bind 的实现:

其中核心实现为:

// `Function.prototype.bind` method implementation
// https://tc39.es/ecma262/#sec-function.prototype.bind
module.exports = Function.bind || function bind(that /* , ...args */) {
  var F = aCallable(this);
  var Prototype = F.prototype;
  var partArgs = arraySlice(arguments, 1);
  var boundFunction = function bound(/* args... */) {
    var args = concat(partArgs, arraySlice(arguments));
    return this instanceof boundFunction ? construct(F, args.length, args) : F.apply(that, args);
  };
  if (isObject(Prototype)) boundFunction.prototype = Prototype;
  return boundFunction;
};

这里有更多的细节:

  • 这里判断了 this 是否为函数类型,不是函数会报错;
  • F.prototype 需要是一个对象或函数,才能赋值给新函数;
  • 使用了普通函数和 arguments,这是为了兼容 ES5。

结尾

手写 bind,现在想来并不简单,需要掌握好很多知识点:

  • bind 的详尽用法:包括改变 this、预置参数、new 的表现;
  • 闭包的使用:保存一些私有变量;
  • 通过原型链的方式(this instanceof boundFn)判断是否通过 new 调用当前函数;
  • 使用 call 在执行时改变函数的 this 指向。

现在大家对于JS的bind方法用法是怎样,如何实现完整的bind的内容应该都有一定的认识了吧,希望这篇能对大家有所帮助。最后,想要了解更多,欢迎关注群英网络,群英网络将为大家推送更多相关的文章。 群英智防CDN,智能加速解决方案
标签: JS bind方法

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。

猜你喜欢

成为群英会员,开启智能安全云计算之旅

立即注册
专业资深工程师驻守
7X24小时快速响应
一站式无忧技术支持
免费备案服务
免费拨打  400-678-4567
免费拨打  400-678-4567 免费拨打 400-678-4567 或 0668-2555555
在线客服
微信公众号
返回顶部
返回顶部 返回顶部
在线客服
在线客服