Javascript中bind函数和new对于this的影响分析

1. 问题描述

js 复制代码
function fn(name) {
  this.name = name;
}
const obj = {}
const fn1 = fn.bind(obj);
const instance = new fn1("abc");
console.log(instance.name);
console.log(obj.name);

上面这段代码的输出结果是:

js 复制代码
abc
undefined

在这段代码中我们首先声明了一个函数 fn, 函数内部将参数 name 赋值给 thisname 属性;随后将变量 fn 通过 bind 函数绑定了一个空对象 obj;之后通过 new fn1("abc") 创建了实例 instance; 通过打印,可以发现 instancename 属性是实例化时传入的参数 "abc",而 objname 属性不存在。

根据这个现象我们可以推断,在 fn 中获取到的 thisinstance,而不是通过 bind 函数绑定的 obj。因此我们可以得到结论,new 绑定对于 this 指向的优先级要高于 bind 函数

2. 原因解释

想要知道其中的原因,必然要知道 bind 的实现原理。参考 core-jsbind 函数的 polyfillgithub.com/zloirock/co... ),我们可以对 bind 函数实现原理有一个大致认识,以下是部分简化后的代码:

js 复制代码
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;
};

代码中 aCallable 就是判断传入参数是否可调用,如果是则返回参数本身。construct 函数是将传入的 F 实例化。其它如 arraySliceconcatisObject 函数的作用结合函数名都比较好理解。

这里就可以很清晰的看到,bind 函数最终返回了 boundFunction,而在这个函数内部对 this 进行了判断,这里的 this 的值根据我们调用方式的不同就会有差别:

  • 通过 fn.bind(obj)(xxx) 直接调用

此时 bind 函数获取到的 that 参数为 obj,而 bind 函数中的 this 对象则是 fnfn.bind(obj) 返回了 boundFunction 函数,相当于调用 boundFunction(xxx),这时 boundFunction 函数没有显示的调用对象,因此 this 指向全局对象。

  • 通过 new fn.bind(obj)(xxx) 形式调用

fn.bind(obj) 函数返回的是函数 boundFunction,这里通过 new 调用,所以相当于 new boundFunction(xxx),那么显然在 boundFunction 中获取到的 this 就是 boundFunction 实例化后的对象。

我们在前面的代码中执行了 new fn.bind(obj)("abc"),也就相当于执行 new boundFunction(obj)("abc")

我们继续看 boundFunctionreturn 的内容。这一部分代码首先对 this instanceof boundFunction 进行了判断,又可以分两种情况讨论:

  • 如果我们是直接调用的话 this 指向全局对象,那么显然判断条件不成立,因此会走入 F.apply(that, args),这里的 that 根据前文分析是传入 bind 的参数 obj 对象,这种形式就是我们将函数 Fthis 显式绑定了对象 obj

  • 如果我们通过 new 进行调用,this 指向 boundFunction 的实例对象,那么判断条件成立,执行 construct(F, args.length, args),相当于 new F(...args),这里的 F 就是 bind 函数获取到的 this,也就是 fn 函数。因此最终我们执行的实际上是 new fn(xxx),很明显此时我们在 fn 函数中获取到的 this 就是 fn 实例化后的对象,而非我们在 bind 中传递的参数 obj

3. 小结

我们在同时用 newbind 函数去改变 this 指向时,newthis 的影响优先级要高于 bind 函数。这是由于在 bind 函数中我们返回了一个新函数 boundFunction,并且在这个函数中对 this instanceof boundFunction 进行了判断。调用方式的不同会导致判断结果的不同,最终导致了 newthis 指向的影响优先级高于 bind 函数。

相关推荐
江号软件分享9 分钟前
无接触服务的关键:二维码生成识别技术详解
前端
江号软件分享9 分钟前
如何利用取色器实现跨平台色彩一致性
前端
灰海13 分钟前
封装WebSocket
前端·网络·websocket·网络协议·vue
前端小巷子24 分钟前
深入理解TCP协议
前端·javascript·面试
万少25 分钟前
鸿蒙外包的十大生存法则
前端·后端·面试
开开心心就好1 小时前
电脑息屏工具,一键黑屏超方便
开发语言·javascript·电脑·scala·erlang·perl
江号软件分享1 小时前
有效保障隐私,如何安全地擦除电脑上的敏感数据
前端
web守墓人2 小时前
【前端】ikun-markdown: 纯js实现markdown到富文本html的转换库
前端·javascript·html
Savior`L2 小时前
CSS知识复习5
前端·css
许白掰2 小时前
Linux入门篇学习——Linux 工具之 make 工具和 makefile 文件
linux·运维·服务器·前端·学习·编辑器