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 函数。

相关推荐
BillKu28 分钟前
Vue3中AbortController取消请求的用法详解
前端·javascript·vue.js
heroboyluck1 小时前
rust 全栈应用框架dioxus
前端·rust·dioxus
不思念一个荒废的名字2 小时前
【黑马JavaWeb+AI知识梳理】后端Web基础01 - Maven
java·前端·maven
hunteritself2 小时前
OpenAI 上新:轻量版 Deep Research、GPT-4o 升级、o3 限额翻倍、生图 API 发布!| AI Weekly 4.21-4.27
前端·人工智能·科技·深度学习·chatgpt
刺客-Andy2 小时前
React 第三十六节 Router 中 useParams 的具体使用及详细介绍
前端·react.js·前端框架
黄同学real3 小时前
Vue 项目中运行 `npm run dev` 时发生的过程
前端·vue.js·npm
Kairo_013 小时前
在 API 模拟阶段:Apipost vs. Faker.js vs. Postman —— 为什么 Apipost 是最优选择
开发语言·javascript·postman
黄同学real3 小时前
vue 优化策略,大白话版本
前端·javascript·vue.js
xcLeigh3 小时前
HTML5好看的水果蔬菜在线商城网站源码系列模板8
java·前端·html5
周胡杰4 小时前
鸿蒙文件上传-从前端到后端详解,对比jq请求和鸿蒙arkts请求区别,对比new FormData()和鸿蒙arktsrequest.uploadFile
前端·华为·harmonyos·鸿蒙·鸿蒙系统