# 深入理解 JavaScript 中的 this 与执行上下文:从理论到实战

132 阅读3分钟

深入理解 JavaScript 中的 this 与执行上下文:从理论到实战

JavaScript 中的 this 关键字是必须掌握的核心概念之一。本文将通过代码示例和原理剖析,带你彻底理解 this 的绑定规则、new 操作符的本质以及箭头函数的特性,助你写出更优雅的代码。


一、为什么需要 this?

this 的本质是一个动态指向执行上下文的代词,它让函数能够自动引用合适的上下文对象。试想当多个对象共享相同方法时,this 能帮助我们避免硬编码对象引用,实现代码复用。

二、this 的五大绑定规则

1. 默认绑定(独立调用)

函数独立调用时,this 指向全局对象(浏览器中为 window)。

function foo() {
  console.log(this); // window(严格模式下为 undefined)
}
foo();

2. 隐式绑定(上下文调用)

当函数的引用有上下文对象 (当函数被某一个对象所拥有且调用), this 指向该上下文对象

const obj = { 
  name: "Alice",
  sayHello() { console.log(`Hello, I'm ${this.name}!`); }
};
obj.sayHello(); // 输出: Hello, I'm Alice!

3. 隐式丢失

当函数的引用有一连串上下文对象,this 指向最近的那个对象

const obj = {
    name: "Alice",
    sayHello: function() {
      console.log(`Hello, I'm ${this.name}!`);
    }
  };
  
//   obj.sayHello(); // 输出: "Hello, I'm Alice!"

const obj2 = {
    name: "Bob",
    sayHello: obj.sayHello 
};
  
obj2.sayHello(); // 输出: "Hello, I'm Bob!"

4. 显式绑定(call/apply/bind)

call apply bind 显式的将函数的 this 绑定到一个对象上

function foo(x, y) {
    console.log(this.a);
    return x + y
}
const obj = {
    a: 1,
}
foo.call(obj)  输出:1
foo.apply(obj)  输出:1

let bar = foo.bind(obj)
bar()  输出:1

那为什么这三个函数效果一样,可是开发者却要构造这三个函数呢?

方法参数传递执行时机返回值典型场景
call逐个参数立即执行函数执行结果明确参数数量时
apply数组形式参数立即执行函数执行结果动态参数或数组处理
bind可预设部分参数延迟执行新函数固定 this 或事件回调绑定

5. new 绑定

构造函数调用时,this 指向新创建的实例对象。

function Person(name) {
  this.name = name;
}
const p = new Person('John');
console.log(p.name); // 输出: John

三、new 操作符的底层原理

new 操作符的实际行为可通过以下步骤模拟:

  1. 创建空对象 obj
  2. 将构造函数 this 绑定到 obj
  3. 执行构造函数代码
  4. 设置 obj.__proto__ 指向构造函数原型
  5. 返回 obj(除非构造函数返回引用类型)
// 模拟 new(简化版)
function myNew(constructor) {
  const obj = {};
  obj.__proto__ = constructor.prototype;
  const result = constructor.apply(obj);
  return result instanceof Object ? result : obj;
}

四、箭头函数的特性

箭头函数没有自己的 this,其 this 继承自外层作用域,且不可作为构造函数。

const obj = {
  name: 'Alice',
  sayHello: () => {
    console.log(this.name); // 输出: undefined(this 指向外层 window)
  }
};
obj.sayHello();

五、手写 call 方法实现显式绑定

通过扩展 Function.prototype 实现自定义 call

// call.js
Function.prototype.myCall = function(context, ...args) {
  context = context || window;
  const fnKey = Symbol('fn');
  context[fnKey] = this;
  const result = context[fnKey](...args);
  delete context[fnKey];
  return result;
};

foo.myCall(obj, 2, 3); // 输出: 1 2 3

六、总结

  • this 的指向由调用方式动态决定,遵循五大绑定规则。
  • 箭头函数的 this 由词法作用域决定,适合需要固定上下文的场景。
  • 显式绑定是控制 this 的终极手段,new 操作符则与原型链密切相关。

理解这些概念后,可以尝试分析以下代码的输出结果:

const obj = { a: 1 };
function Foo() {
  this.a = 2;
  return { a: 3 };
}
const foo = new Foo();
console.log(foo.a); // ?

掌握 this 的奥秘,让你的 JavaScript 代码更加灵活高效!