细说this的绑定和指向

this绑定的分类

  1. 默认绑定
  2. 隐式绑定
  3. 显式绑定
  4. new绑定

前置知识

this的绑定和函数声明的位置没有任何关系,只取决于函数的实际调用位置

默认绑定

js 复制代码
function foo () {
    console.log(this.a);
}
var a = 5;
foo(); // 5

分析 使用var在全局作用域(window)声明了一个a属性,这跟在window对象中声明一个a属性是等价的,就像硬币的正反两面。由于foo在全局作用域上调用,所以this指向window对象,所以结果是5。这就是默认绑定,默认绑定到window对象。但如果我们将var改为const或者let,结果会是undefined,因为const和let声明的变量并不会挂在window对象上。

隐式绑定

当函数调用有上下文时,应用的就是隐式绑定,此时this一般指向该上下文对象。

js 复制代码
function foo () {
    console.log(this.a);
}

const obj = {a: 'obj:a', foo: foo};
var a = 'global:a'

obj.foo(); // obj:a

分析 obj.foo()虽然是在全局环境调用函数的,但foo的调用上下文是obj,或者说是obj持有了foo,所以此时的this指向了obj上下文对象。

为了加深印象,我们再举个🌰:

js 复制代码
function foo() { 
    console.log(this.a); 
} 
var obj2 = { a: 42, foo: foo, }
var obj1 = { a: 2, obj2: obj2 } 
obj1.obj2.foo(); // 42, obj2为foo的直接上下文

分析 由代码可知,obj1包含了obj2,obj2包含了foo,但最后调用foo时,obj2为foo的直接上下文,此时this指向了obj2,它并不会受obj1的影响。

隐式丢失

但隐式绑定在一些场景下会丢失绑定的上下文对象,从而应用了默认绑定。

举个🌰:

js 复制代码
function foo() { 
    console.log(this.a);
} 
var obj = { a: 2, foo: foo };
var bar = obj.foo;
var a = "oops, global"; // a是全局对象的属性 
bar(); // "oops, global"

分析 由于obj.foo的函数引用赋值给了bar,所以实际上bar是foo函数的函数别名,因此,在全局环境调用bar实际上跟在全局环境直接调用foo函数得到的结果是一样的。this都指向了全局window对象。

显式绑定

通过call,apply等来实现显式绑定

js 复制代码
function foo() { 
    console.log(this.a);
}
var obj = { a: 2 };

var a = 'global';
foo.call(obj); // 2

分析 显式绑定就相对容易理解很多,往call或者apply传递的第一个参数,实际上就是此函数调用的上下文对象,所以this会指向这个对象。

被忽略的this

如果往call或者apply传递一个null(空上下文),此时this会指向全局对象

举个🌰:

js 复制代码
function foo() { 
    console.log(this.a);
}

var a = 2;
foo.call(null); // 2 使用了默认绑定

new绑定

new绑定是js中绑定优先级最高的绑定规则,也是在js继承中常见的。

js 复制代码
function foo(a) { 
    this.a = a;
}

var bar = new foo(2);
bar.a // 2;

分析 由于new的过程实际上内部会将构造函数的this指向实例对象,所以this会指向bar。

this绑定优先级

那这四种this绑定,优先级怎么排列呢?

答案是: new绑定 > 显示绑定 > 隐式绑定 > 默认绑定

this绑定的判断规则

当我们的代码中同时出现这四种绑定规则中的两种及以上时,我们应该怎么正确判定this的指向呢?

规则如下:

1.函数是否在new中调用(new绑定)?如果是的话this绑定的是新创建的对象,因为new绑定的优先级最高!

2.函数是否通过call,apply(显示绑定)或者硬绑定调用?如果是的话,this绑定的是指定的对象。

3.函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this绑定的是该上下文对象。

4.如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定到全局对象

箭头函数中的this绑定

箭头函数不会使用this的四条标准的绑定规则,而是根据当前的词法作用域来决定this,具体来说,箭头函数会继承外层函数调用的this绑定,而且无法修改。

js 复制代码
function foo() { 
    // 返回一个箭头函数 
    return (a) => {
        // this继承自foo() 
        console.log(this.a); 
    };  
}
var obj1 = { a:2 }; 
var obj2 = { a:3 }; 
var bar = foo.call(obj1); 
bar.call(obj2); // 2 不是3!!

一些建议

在实际的项目开发中,为了保证代码风格的统一性和便于理解,我们不应混用this绑定的四种规则语法和箭头函数!

总结

this的绑定指向一直是一个令人难以理解的东西,特别是在阅读一些框架源码或者工具函数的时候。为了提升阅读源码的个人能力,了解和掌握this的规则势在必行!通过阅读此篇文章,我们可以很容易掌握this的绑定规则有哪些,以及他们的使用方式和优先级排列。

参考资料

《你不知道的Javascript-上卷》

相关推荐
用户30816998764858 分钟前
同一个端口能被两个程序监听吗?TCP 11 组绑定实测
前端
deli00715 分钟前
红绿灯配时实测:固定 60 秒 vs 自适应,2400 辆车差多少
前端
去伪存真15 分钟前
井道无信号、满手黑机油,我们如何用 DeepSeek V4 打造不“犯浑”的电梯维保 Agent?
前端·agent
deli00718 分钟前
60fps 为什么还是一顿一顿?帧时间曲线实测给你看
前端
时间的情敌1 小时前
Cesium 流动管线、管道动画、管线流向仿真,管网、油气、水利孪生高频需求
前端·交互·cesium
yuyuxun11 小时前
协同过滤的校园二手物品智能推荐系统的设计与实现 毕业设计-附源码40082
javascript·vue.js·spring boot·python·数据分析·django·课程设计
wangxiaoxuan2172 小时前
SLAM 的基本结构:前端、后端、回环与地图优化
前端·机器人·科沃斯八界
总要学点什么4 小时前
第105篇:三维特效-流动图片墙体效果——让箭头沿墙长滚,它俩竟是同一份代码
开发语言·javascript·ecmascript·gis·webgl·材质·cesium
Agent世界5 小时前
用 CDP 做发布自动化:四道机器闸门与产物验收
前端·javascript
FungLeo5 小时前
成为全栈·Vue 管理后台篇·编辑器图片上传:从文件选择到文章附件关联
前端·vue.js·编辑器·markdown·文件上传·formdata·附件管理