前端必看:this 不是玄学!5 大绑定规则帮你永久告别 this 困惑

在 JavaScript 开发中,this 是一个既核心又容易让人困惑的概念。它像一个 "代词",在不同场景下指向不同的对象,为代码提供了优雅的隐式对象引用方式,让代码更简洁、更易于复用。

本文将带你系统梳理 this 的使用场景、绑定规则及核心原理,帮助你彻底掌握 this

一、为什么要有 this

this的存在,本质是为了更优雅地传递对象引用。

想象一个场景:我们有一个方法,需要操作某个对象的属性。如果没有this,我们可能需要将对象作为参数显式传入,代码会变得冗余。有了this以后,我们可以通过this隐式获取其执行时的上下文对象,让代码变得更简洁,更具有复用性。

代码示例:

javascript 复制代码
const user = {
  name: '张三',
  greet: function() {
    console.log(`Hello, I'm ${this.name}`); // this 隐式指向 user 对象
  }
};
user.greet(); // 输出: Hello, I'm 张三

在上述示例中,greet方法通过this.name访问对象属性,无需显式传递user对象,更加简洁直观。

二、this 在哪里生效?

this 并非在所有代码作用域中都有意义,它只在全局作用域和函数作用域中生效

  • 全局作用域 :在浏览器环境中,全局作用域的 this 指向 window 全局对象(严格模式下为 undefined)。
  • 函数作用域 :函数内部的 this 指向完全由调用方式 决定,这也是 this 最复杂、最核心的部分。

注意:在 ES6+ 的块级作用域(如 if/for 大括号内)中,this 继承自外层作用域,并不会产生新的绑定。

三、this 的五大绑定规则

this 的指向并非由定义位置决定,而是由函数的调用方式决定。

1. 默认绑定:独立调用时的 this

当函数独立调用(没有任何上下文对象)时,会触发默认绑定:

  • 非严格模式下:this 指向全局对象(浏览器中为 window)。
  • 严格模式下:thisundefined

代码示例:

scss 复制代码
function sayHi() {
  console.log(this);
}
sayHi(); // 非严格模式下输出 window,严格模式下输出 undefined

2. 隐式绑定:作为对象方法调用

当函数被某个对象调用 时,this 会隐式绑定到该对象上,即指向调用它的对象

代码示例:

javascript 复制代码
const obj = {
  name: '张三',
  getName: function() {
    return this.name;
  }
};
obj.getName(); // this 指向 obj,返回 '张三'
  • 隐式丢失 :当多层对象嵌套调用时,this 只绑定到最近一层的调用对象;若将对象方法赋值给变量再调用,会丢失绑定,触发默认绑定。

代码示例:

ini 复制代码
const obj2 = {
  level: 2,
  nested: {
    level: 1,
    getLevel: function() {
      return this.level;
    }
  }
};
obj2.nested.getLevel(); // this 指向 nested,返回 1

const getLevel = obj2.nested.getLevel;
getLevel(); // 独立调用,触发默认绑定,非严格模式下返回 undefined

3. 显式绑定:手动指定 this

通过 call()apply()bind() 三个方法,可以手动强制指定 函数执行时的 this 指向,这就是显式绑定。

  • fn.call(obj, arg1, arg2, ...) :将 this 绑定到 obj,call负责帮fn接受参数,参数逐个传入。
  • fn.apply(obj, [arg1, arg2, ...]) :将 this 绑定到 obj,apply负责帮fn以数组的形式接受参数。
  • fn.bind(obj, arg1, arg2, ...)不立即执行 ,返回一个新函数,新函数的 this 永久绑定到 obj,bind负责帮fn接受参数,返回一个新的函数,需要调用新的函数才能实现调用的效果。

代码示例:

javascript 复制代码
function greet(greeting) {
  console.log(`${greeting}, I'm ${this.name}`);
}
const user = { name: '张三' };

greet.call(user, 'Hi'); // 输出: Hi, I'm 张三
greet.apply(user, ['Hello']); // 输出: Hello, I'm 张三

const boundGreet = greet.bind(user, 'Hey');
boundGreet(); // 输出: Hey, I'm 张三

4.new 绑定:构造函数中的 this

当使用 new 关键字调用函数(构造函数)时,会触发 new 绑定:

  1. 创建一个全新的空对象。
  2. 将构造函数的 this 绑定到这个新对象。
  3. 执行构造函数内部代码。
  4. 将这个新对象的 __proto__ 指向构造函数的 prototype
  5. 返回这个新对象。

代码示例:

javascript 复制代码
Person.prototype.sayName = 'hello'
function Person() {
    // var obj={} --1
    //Person.call(obj) --2
    this.name = '张三' //--3
    //obj.__proto__=Person.prototype //--4
    //return obj --5
}
let p = new Person()
console.log(p.name) //张三

注:如果构造函数中 return 了一个引用类型 (对象 / 数组 / 函数),则 new 表达式会返回这个 return 的值,而非新创建的对象。

5.箭头函数:继承外层作用域的 this

ES6 引入的箭头函数是一个特殊存在:它没有自己的 this

箭头函数中的 this 完全继承自外层作用域(定义时的作用域)的 this,且在定义时就确定了,不会随调用方式改变。

代码示例:

javascript 复制代码
const obj = {
  name: '张三',
  greet: function() {
    // 箭头函数的 this 继承自外层 greet 方法的 this(即 obj)
    setTimeout(() => {
      console.log(`Hello, ${this.name}`); // 输出: Hello, 张三
    }, 100);
  }
};
obj.greet();

对比普通函数:如果 setTimeout 中是普通函数,独立调用时 this 会指向全局对象,导致 this.nameundefined

四、绑定优先级与常见陷阱

绑定优先级

当多种绑定规则同时存在时,优先级如下:箭头函数 > new 绑定 > 显式绑定(call/apply/bind) > 隐式绑定 > 默认绑定

  • 箭头函数的 this 一旦确定,无法被 call/apply/bind/new 改变。
  • new 绑定无法与 call/apply 同时使用,但优先级高于显式绑定。

常见陷阱

  1. 隐式丢失 :将对象方法赋值给变量后调用,this 丢失绑定,指向全局。

    ini 复制代码
    const getName = obj.getName;
    getName(); // this 不再指向 obj

解决:使用 bind() 永久绑定 this,或使用箭头函数。

  1. 回调函数中的 this :事件监听、setTimeout 等回调函数若为普通函数,this 会指向全局或事件目标。解决:使用箭头函数继承外层 this,或用 bind() 预先绑定。

  2. 严格模式的影响 :严格模式下默认绑定的 thisundefined,容易引发错误。

五、总结

this 是 JavaScript 中实现面向对象编程的核心机制,理解它的关键在于:this 的指向由函数的调用方式决定

  • 记住五大绑定规则:默认绑定、隐式绑定、显式绑定、new 绑定、箭头函数。
  • 关注函数的调用位置 而非定义位置,这是判断 this 指向的核心。
  • 利用箭头函数或 bind() 可以稳定 this 指向,避免常见陷阱。

理解 this 的本质,能让你写出更健壮、更易维护的 JavaScript 代码,也能在排查 this 相关问题时游刃有余。

相关推荐
做前端的娜娜子6 分钟前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅13 分钟前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
HeiSenBerg29 分钟前
Android 网络协议全解析:从 TCP 三次握手到 HTTPS 加密,一篇就够了
面试
jarvisuni39 分钟前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学2 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端
赵庆明老师2 小时前
Vben精讲:21-详解web-antd:tsconfig.json
前端·json·vim
wc882 小时前
微软EDGE浏览器功能学习
前端·学习·edge
破z晓2 小时前
javascript 导出excel表
开发语言·javascript·excel
Csvn3 小时前
🎯 原生 `<dialog>` 元素:终于可以扔掉一半的自定义弹窗组件了?
前端
Csvn3 小时前
🎨 CSS @layer:用「层叠优先级」终结样式打架的世纪难题
前端