JavaScript this 底层机制剖析
1. this 的指向
this 是函数执行时的一个内部绑定,它的值取决于函数被调用的方式 ,而不是函数定义的位置。在全局执行环境中,this 指向全局对象(浏览器中是 window,Node.js 中是 global)。在函数内部,this 的指向由调用时的绑定规则唯一确定。
javascript
function identify() {
console.log(this);
}
identify(); // 全局对象(非严格模式)或 undefined(严格模式)
2. this 的指向原理
this 的绑定发生在函数调用阶段 ,每个函数被调用时会创建一个新的执行上下文(Execution Context)。在执行上下文的创建过程中,引擎会确定 ThisBinding 的值。具体原理如下:
- 引擎分析调用位置(Call Site)------ 即函数在代码中被调用的位置。
- 根据调用位置和调用栈,按照优先级规则为该次调用绑定一个
this值。 this本质上是执行上下文对象的一个内部属性,一旦确定,在该上下文中不会改变。
底层实现中,JavaScript 引擎会将函数调用转换为类似 func.call(thisArg, arguments) 的形式,其中 thisArg 根据调用方式计算得出。
3. this 的四种绑定方式
JavaScript 中 this 的绑定遵循四种规则,按优先级从低到高排列如下:
3.1 默认绑定
独立函数调用,不带任何修饰。非严格模式下 this 绑定到全局对象;严格模式下绑定到 undefined。
javascript
function foo() {
"use strict";
console.log(this); // undefined
}
foo();
3.2 隐式绑定
通过对象属性调用函数,this 绑定到该对象。
javascript
const obj = {
name: "JavaScript",
log: function() { console.log(this.name); }
};
obj.log(); // "JavaScript"
3.3 显式绑定
通过 call、apply 或 bind 方法强制指定 this。
javascript
function bar() {
console.log(this.value);
}
const context = { value: 42 };
bar.call(context); // 42
3.4 new 绑定
使用 new 调用构造函数时,this 绑定到新创建的实例对象。
javascript
function Person(name) {
this.name = name;
}
const p = new Person("Alice");
console.log(p.name); // "Alice"
优先级 :new 绑定 > 显式绑定 > 隐式绑定 > 默认绑定。
4. this 的默认绑定全局的缺点
默认绑定将 this 指向全局对象会带来以下弊端:
- 意外的全局变量污染 :函数内部如果忘记使用
var/let/const而直接给this添加属性,会意外创建全局变量。 - 代码难以维护 :嵌套函数或回调函数中,
this会丢失预期指向而回退到默认绑定,导致逻辑错误且难以调试。 - 严格模式下的不一致性:非严格模式与严格模式行为不同,容易导致跨环境移植时出现隐蔽问题。
- 安全性隐患:全局对象可被任意代码读写,默认绑定使得内部函数可能无意中暴露或修改全局状态。
javascript
function leak() {
this.globalVar = "污染"; // 非严格模式下 this 指向全局对象
}
leak();
console.log(window.globalVar); // "污染"
5. this 与执行上下文之间的关系
每个执行上下文(全局上下文、函数上下文、Eval 上下文)都包含一个 ThisBinding 组件。ThisBinding 是执行上下文的一个内部字段,用于存储当前上下文中 this 的值。两者的关系具体表现为:
- 全局执行上下文 :
ThisBinding就是全局对象,在整个脚本生命周期中保持不变。 - 函数执行上下文 :当进入函数上下文时,
ThisBinding根据调用方式(即四种绑定规则)被初始化,并在该上下文中保持不变直到函数返回。 - 箭头函数 :没有自己的
ThisBinding,它会捕获外层(词法作用域)执行上下文的ThisBinding作为自己的this。
执行上下文的生命周期包括:创建阶段(确定 ThisBinding、变量对象、作用域链)和代码执行阶段。this 的值在创建阶段就已固定,不会因后续代码的执行而改变。
javascript
function demo() {
console.log(this); // 当前上下文的 ThisBinding
function inner() {
console.log(this); // 内部函数拥有自己的执行上下文和 ThisBinding
}
inner();
}
demo.call({ x: 1 });
// 输出:{ x: 1 } → 全局对象(inner 默认绑定)
综上,this 是执行上下文的核心组成部分,理解 this 必须从调用机制和执行上下文的创建过程入手。