JS 中的this

JavaScript 中 this 的绑定主要分为 5 种情况。

核心原则:普通函数的 this 通常由调用方式决定,而不是定义位置。箭头函数除外。

一、默认绑定

普通函数独立调用时,使用默认绑定。

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

foo();
  • 非严格模式:this 指向 globalThis(浏览器中通常是 window)。
  • 严格模式:this 为 undefined。

二、隐式绑定

函数作为对象的方法调用时,this 指向调用该方法的对象。

javascript 复制代码
const obj = {
  name: "Tom",
  sayHello() {
    console.log(this.name);
  }
};

obj.sayHello(); // Tom

注意,隐式绑定可能丢失:

ini 复制代码
const fn = obj.sayHello;

fn(); // this 不再指向 obj

因为 fn() 是独立调用,不再通过 obj 调用。


① 隐式绑定不仅适用于普通对象,也适用于数组。

② 把对象的方法赋值给变量,会丢失原来的隐式绑定。

js 复制代码
 var length = 1;

    function fun() {
      console.log(this.length);
    }

    let arr = [fun, "a", "b"];

    arr[0](); // 输出什么?3

    let fun2 = arr[0];

    fun2(); // 输出什么?1

补充:函数作为参数传递时的 this 绑定

函数作为参数传递时,只传递函数本身,不会保留原来的隐式绑定。

scss 复制代码
const obj = {
  name: "Tom",
  sayName() {
    console.log(this.name);
  }
};

function execute(fn) {
  fn();
}

execute(obj.sayName); // 默认绑定,不再指向 obj

原因:execute 内部通过 fn() 独立调用函数,而不是通过 obj 调用。

可以通过 bind 保留原来的 this:

scss 复制代码
execute(obj.sayName.bind(obj)); // Tom

核心记忆:判断普通函数的 this,看函数实际如何调用,而不是函数从哪里传过来。

三、显式绑定

通过 call、apply、bind 显式指定 this。

scss 复制代码
function foo() {
  console.log(this.name);
}

const obj = { name: "Tom" };

foo.call(obj);  // Tom
foo.apply(obj); // Tom

const fn = foo.bind(obj);
fn(); // Tom

区别:

方法 是否立即执行 参数传递
call 是 逐个传参
apply 是 数组传参
bind 否 逐个传参,返回新函数

四、new 绑定

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

ini 复制代码
function Person(name) {
  this.name = name;
}

const p = new Person("Tom");

console.log(p.name); // Tom

结合之前学习的 new 执行过程:

  1. 创建新对象。
  2. 将新对象的原型关联到构造函数的 prototype。
  3. 以新对象作为 this 执行构造函数。
  4. 根据构造函数返回值决定最终返回的对象。

五、箭头函数绑定

箭头函数没有自己的 this,而是继承外层词法环境中的 this。

ini 复制代码
const obj = {
  name: "Tom",

  sayHello() {
    const fn = () => {
      console.log(this.name);
    };

    fn();
  }
};

obj.sayHello(); // Tom

这里:

  • sayHello() 是普通方法,this 指向 obj。
  • fn 是箭头函数,捕获外层 sayHello 的 this。
  • 因此输出 Tom。

箭头函数的 this 无法通过 call、apply、bind 修改。


六、this 绑定的优先级

面试中经常考察多个绑定规则同时存在的情况。

对于普通函数,可以按照下面的优先级判断:

new 绑定 > 显式绑定 > 隐式绑定 > 默认绑定

例如:

ini 复制代码
function Person(name) {
  this.name = name;
}

const obj = { name: "Jack" };

const BoundPerson = Person.bind(obj);

const p = new BoundPerson("Tom");

console.log(p.name);   // Tom
console.log(obj.name); // Jack

即使通过 bind 绑定了 obj,使用 new 时依然会创建新实例,并将 this 指向新实例。

注意:箭头函数不参与上述优先级比较。


七、this 与词法作用域的区别

你刚刚学过词法作用域,这两个概念可以对比理解。

javascript 复制代码
const name = "Global";

const obj = {
  name: "Tom",

  foo() {
    console.log(name);
    console.log(this.name);
  }
};

obj.foo();
// Global
// Tom

这里:

  • name:按照作用域链 查找,找到外层的 name。
  • this.name:先确定 this 指向 obj,再读取 obj.name。

一句话总结:

  • 作用域链:变量在哪里定义,决定去哪里查找。
  • this 绑定 :普通函数怎么调用,决定 this 指向谁。

这是理解 JavaScript 函数执行机制的两个重要维度。

相关推荐
Nayana1 小时前
《Web 到 HarmonyOS》-- 进程、线程与通信:谁能通、怎么通
前端
狂潮lister1 小时前
狂潮·元理论 | 第5篇:想为你的AI培养个性吗!哈哈哈……(6个测试,教你幻觉的正确玩法)
前端·人工智能
高晶1 小时前
2A3 单端甲类功放从零制作全攻略:电路设计、焊接调试到煲机维护一站式指南
前端·设计模式
deli0071 小时前
塔防游戏里怪物为什么绕远路?Canvas 实测 A* 寻路 20 波
前端
小万7421 小时前
WHO 心理自助指南深度解析:Step-by-Step 与 Doing What Matters in Times of Stress,用循证协议重构心理韧性
前端
代码猎人1 小时前
前端怎么优化用户体验
前端
deli0071 小时前
批量加水印还在用在线工具?30 张图 Canvas 本地铺满实测
前端
鸿蒙开发1 小时前
扫描 PDF 看得见却搜不到?用 Tesseract.js + PDF.js 生成可搜索 PDF
前端·react.js
小万7421 小时前
ECC(Everything Claude Code)深度解析:把编程 Agent 从“聊天写代码”拉进工程现场的 Harness 系统
前端