资讯专栏INFORMATION COLUMN

JS手写bind之处理new的情况详解

3403771864 / 480人阅读

  你有遇见过给bind返回的函数做new操作的场景,本篇主要讲述的就是实现一下兼容new操作的bind写法,顺便学习一下new操作符,为大家提供下参考。

  大家可以去看下关于 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 预置好的参数。

  和原本一样(参照《前端面试题:手写 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 指向。

  以上就是全部内容,欢迎大家继续关注后续更多精彩内容。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/128375.html

相关文章

  • 22道JavaScript高频手写面试题

    JavaScript笔试部分 点击关注本公众号获取文档最新更新,并可以领取配套于本指南的 《前端面试手册》 以及最标准的简历模板. 实现防抖函数(debounce) 防抖函数原理:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。 那么与节流函数的区别直接看这个动画实现即可。 showImg(https://segmentfault.com/img/remote/146000002...

    Alan 评论0 收藏0
  • 「中高级前端面试」JavaScript手写代码无敌秘籍

    摘要:第一种直接调用避免在不必要的情况下使用,是一个危险的函数,他执行的代码拥有着执行者的权利。来自于此外,实现需要考虑实例化后对原型链的影响。函数柯里化的主要作用和特点就是参数复用提前返回和延迟执行。手写路径导航 实现一个new操作符 实现一个JSON.stringify 实现一个JSON.parse 实现一个call或 apply 实现一个Function.bind 实现一个继承 实现一个J...

    Zhuxy 评论0 收藏0
  • 面试题里那些各种手写

    摘要:最近准备初级前端面试,发现有很多手写实现什么的,例如什么手写实现,。后面以这道题为引线面试官可能会追问什么是执行上下文的判断,的区别手写一个函数实现斐波那契数列首先拷一个阮神在他教程里的一个写法。 最近准备初级前端面试,发现有很多手写实现什么的,例如什么手写实现bind,promise。手写ajax,手写一些算法。翻阅了很多书籍和博客。 这里做一个总结改进,算是对我后面大概为期一个月找...

    wh469012917 评论0 收藏0
  • 2019-我前端面试题

    摘要:先说下我面试情况,我一共面试了家公司。篇在我面试的众多公司里,只有同城的面问到相关问题,其他公司压根没问。我自己回答的是自己开发组件面临的问题。完全不用担心对方到时候打电话核对的问题。 2019的5月9号,离发工资还有1天的时候,我的领导亲切把我叫到办公室跟我说:阿郭,我们公司要倒闭了,钱是没有的啦,为了不耽误你,你赶紧出去找工作吧。听到这话,我虎躯一震,这已经是第2个月没工资了。 公...

    iKcamp 评论0 收藏0

发表评论

0条评论

3403771864

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<