细说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-上卷》

相关推荐
m0_748247551 小时前
Web 应用项目开发全流程解析与实战经验分享
开发语言·前端·php
m0_748255022 小时前
前端常用算法集合
前端·算法
真的很上进2 小时前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
web130933203982 小时前
vue elementUI form组件动态添加el-form-item并且动态添加rules必填项校验方法
前端·vue.js·elementui
NiNg_1_2343 小时前
Echarts连接数据库,实时绘制图表详解
前端·数据库·echarts
如若1233 小时前
对文件内的文件名生成目录,方便查阅
java·前端·python
滚雪球~4 小时前
npm error code ETIMEDOUT
前端·npm·node.js
沙漏无语4 小时前
npm : 无法加载文件 D:\Nodejs\node_global\npm.ps1,因为在此系统上禁止运行脚本
前端·npm·node.js
supermapsupport4 小时前
iClient3D for Cesium在Vue中快速实现场景卷帘
前端·vue.js·3d·cesium·supermap
brrdg_sefg4 小时前
WEB 漏洞 - 文件包含漏洞深度解析
前端·网络·安全