js - javaScript 中的 this (v2)
零、序言
本篇是《你不知道的 javascript(上)》读书笔记。
v1 版本写得比较乱,传送门;
注意:如无特殊标注,本篇中的 this 指的是 es5 & 非严格模式下的 this。
一、总集
在 js 中, this 的值需要到函数的调用时才能明确,因此完全取决于函数的调用位置(执行 fn(...) 的时候)。
二、绑定规则
1. 默认绑定
这种情况最简单最常用,函数作为独立函数被调用,函数体中的 this 指向 window。同时这条规则也是其他规则不适合时的默认规则。
demo:
1 2 3 4 5 6 | function fn() { console.log( this .a); } var a = 'global' ; fn(); // global |
注意,如果在 fn 函数体中使用了严格模式,那么 fn 中的 this 会被绑定成 undefinded。
1 2 3 4 | function fn() { 'use strict' ; console.log( this ); // undefined } |
2. 隐式绑定
“另一条需要考虑的规则是调用位置是否有上下文对象,或者说是否被某个对象拥有或者包含,不过这种说法可能会造成一些误导。”
这条规则说穿了也很简单,就是当函数作为对象的一个属性,并被这个对象调用时,那么函数体中的 this 会指向这个对象。
先看个正常的 demo:
1 2 3 4 5 6 7 8 9 | function foo() { console.log( this .a ); } var obj = { a: 2, foo: foo }; obj.foo(); // 2 |
接着看一下会出问题的 demo:
1 2 3 4 5 6 7 8 9 10 | function foo() { console.log( this .a ); } var obj = { a: 2, foo: foo }; var bar = obj.foo; // 函数别名! var a = "oops, global" ; // a 是全局对象的属性 bar(); // "oops, global" |
var bar = obj.foo这句,本质上是把函数 foo 的内存地址传递给变量 bar,并不是真正地执行了函数 foo,真正的函数执行时 bar() 这一句,因此,在正函数执行的时候,是以独立函数的规则运行的,所以这里实际上是走的默认绑定的规则。
因此,在所有的类似这种仅仅传递函数的内存地址而不是执行函数的场景,都要注意存在的隐式丢失的问题。同时列举常见的需要注意的场景:
- setTimeout( obj.foo, 100 );
- function func( fn ) { fn() }; func( obj.foo );
3. 显式绑定
相对于隐式绑定,显式绑定时我们可以自主操控的,这就给了大牛们提供了黑操作的控件。也叫硬绑定。
js 中原生绑定的形式只有三个, call/apply/bind,其中 bind 是在 es5 中新加入的函数。对于这三个函数的用法和内部伪代码,请移步 call,apply 和 bind 方法。
除了我们自己使用上面的函数进行显式绑定,js 中的某些内置 api 也会实现显式绑定,例如 forEach:
1 2 3 4 5 6 7 8 9 10 11 | function fn(el) { console.log(el, this .id); } var obj = { id: 'awesome' , } // 调用 [1, 2, 3].forEach(foo, obj); // 1 awesome 2 awesome 3 awesome |
4. new 绑定
好消息是,这是最后一条绑定规则。
js 中 new 操作符的操作列在其下:
- 创建(或者说构造)一个全新的对象;
- 这个新对象会被执行 [[ 原型 ]] 链;
- 这个新对象会绑定到函数调用的 this;
- 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象;
ps: new 操作符的伪代码实现请移步 js new 与 return。
当然,真实的实现会更复杂,有兴趣的可自行查找资料。
三、注意事项
1. 显式绑定中, call/apply 绑定后会执行下函数, bind 绑定后会返回一个新的函数, 该函数内部的 this 会指向被绑定的对象。
2. 一般而言,显式绑定(call/apply/bind)之后的 this 不能够再次被修改或者绑定。(特殊情况见第 4 节)。
3. new 和 apply/call 不可同时使用。
4. new 和 bind 可以同时使用,bind 的源码在实现的时候会在内部判断有没有与 new 一起使用,如果是的话就会使用新创建的 this 替换硬绑定的 this。
四、例外
规则总有例外。
1. 被忽略的 this
null(undefinded) 作为一个特殊的 object,也是被允许使用在显式绑定中的。显式绑定在遇到这两个特殊的对象的时候,这些值的调用会被忽略,实际应用的是默认的绑定规则。
1 2 3 4 5 6 7 8 | function foo(a,b) { console.log( a, b, this ); } foo.apply( null , [2, 3] ); // 2 3 window var bar = foo.bind( null , 2); bar(3); // 2 3 window |
当然,使用 null 来忽略 this 的绑定也具有一定的副作用。如果某个函数确实使用了 this (比如第三方库中的一个函数), 那默认绑定规则会把 this 绑定到全局对象(window 等,当然,严格模式下会指向 undefined), 这见导致不可预见的后果(比如修改全局对象)。
因此我们可以使用如下的代码来防止这个副作用。
1 2 3 4 5 6 7 8 9 10 | function foo(a,b) { console.log(a, b, this ); } // 我们自定义的空对象 var ø = Object.create( null ); foo.apply( ø, [2, 3] ); // 2, 3, {} var bar = foo.bind( ø, 2 ) bar(3); // 2, 3, {} |
2.间接引用
1 2 3 4 5 6 7 8 9 | function foo() { console.log( this .a ); } var a = 2; var o = { a: 3, foo: foo }; var p = { a: 4 }; o.foo(); // 3 (p.foo = o.foo)(); // 2 |
放上这段代码的原因在于最后一句,一开始没懂,后来看了下执行过程,最后一句代码前半段是个赋值语句,整体等价于:
p.foo = o.foo; (function foo() { console.log( this.a ) })(); // 立即执行函数(IIFE)
3. es 6 箭头函数
箭头函数不会创建自己的 this,箭头函数不使用 this 的四种标准规则,而是根据外层(函数或全局)作用域来决定 this。
另外, 箭头函数不能作为构造器,与 new 一起使用会抛出错误。
五、其他资料和链接
1.对阮一峰《ES6 入门》中箭头函数 this 描述的探究;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· 单线程的Redis速度为什么快?
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
2019-05-28 jQuery 源码学习 - 02 - jQuery.fn.extend 与 jQuery.extend
2018-05-28 EChart.js 笔记一