JavaScript 中 this 指向总结和箭头函数的作用域说明(附:call / apply / bind 对比总结)

JavaScript中的this指向规则:this的指向取决于函数调用方式而非定义位置。


主要规则包括:

1)全局作用域中指向window;

2)独立函数调用默认绑定全局对象(严格模式下为undefined);

3)对象方法调用指向调用对象;

4)构造函数指向新实例;

5)箭头函数继承定义时的外层this。


优先级顺序为:new绑定>显式绑定>隐式绑定>默认绑定。


箭头函数this固定不可修改,适合回调场景但不可用作构造函数。


严格模式会影响默认绑定结果,类方法需注意this丢失问题。


理解这些规则需把握"调用方式决定this指向"的核心原则。


callapplybind 是 JavaScript 中用于显式绑定 this 的三个核心方法。它们都能改变函数执行时的上下文,但在调用时机参数传递方式上有所不同。


关联阅读推荐

在 Vue 3 的 setup() 函数中,this 是 undefined

JavaScript 箭头函数 隐式/显式返回 和 this 指向


在 JavaScript 中,this 的指向并不是在编写代码时确定的,而是在函数调用时根据调用方式动态决定的。


理解 this 的关键在于:this 不是在编写时绑定的,而是在函数被调用时绑定的


以下是 this 在不同场景下的指向规则总结:


JavaScript 中 this 的指向规则

调用场景 调用方式/示例 this 指向对象 备注/说明
全局作用域 在浏览器控制台或脚本直接执行 console.log(this) window 对象 (浏览器) / global (Node.js) 在严格模式下("use strict"),全局作用域中 this 依然指向全局对象。
独立函数调用 直接执行 fn() window 对象 (浏览器) / global (Node.js) 这是非严格模式下的默认绑定。在严格模式下,thisundefined
对象方法调用 obj.fn() 调用该方法的对象 (即 obj) 谁调用指向谁。如果方法赋值给变量再调用(如 const fn = obj.fn; fn()),则变成独立函数调用。
构造函数调用 new Fn() 新创建的实例对象 new 操作符会强制将 this 绑定到正在构建的新对象上。
箭头函数 () => {} 定义时的外层作用域的 this 箭头函数没有自己的 this,它继承自包含它的函数/作用域,且一旦绑定无法更改(call/apply/bind 无效)。
DOM 事件绑定 element.onclick = fnaddEventListener 触发事件的 DOM 元素 标准事件监听中,this 指向绑定事件的元素。
显式绑定 fn.call(obj) / fn.apply(obj) / fn.bind(obj) call/apply/bind 的第一个参数指定的对象 手动强制指定 this 的指向。
计时器/异步回调 setTimeout(fn, 1000)setInterval window 对象 (浏览器) 回调函数通常作为独立函数执行。若想在回调中使用外部 this,可使用箭头函数或 bind

特殊情况与注意事项

  1. 严格模式("use strict"

    • 在独立函数调用中,this 不再指向 window,而是 undefined。这可以防止无意中修改全局对象。
  2. 优先级

    • new 调用 > 显式绑定 (call/apply/bind) > 对象方法调用 > 默认绑定 (独立函数)。

    • 箭头函数的 this 具有最高优先级 ,因为它不会被任何方式(包括 new)修改。

  3. 事件处理中的区别

    • addEventListener 中,this 指向元素。

    • 内联事件(onclick="fn()")中,this 指向 window,除非在 HTML 属性中显式传入 this(如 onclick="fn(this)")。


理解 this 的关键在于记住函数被调用时的具体形式,而不是它在哪里定义(箭头函数除外)。


关键点解析与易错点

1. "谁调用,指向谁"原则

这是判断 this 最通用的法则(箭头函数除外)。

  • obj.fn() -> thisobj
  • fn() -> thiswindow (非严格) 或 undefined (严格)。

2. 箭头函数的特殊性

箭头函数没有 自己的 this。它像是一个"透视镜",直接穿透看到定义它那一层代码的 this 是什么,它就是什么。

  • 无法修改 :对箭头函数使用 call, apply, bind 传入第一个参数是无效的,会被忽略。
  • 常见用途 :在 setTimeout、数组方法 (map, filter) 或事件监听器中,为了保留外层对象(如 Vue/React 组件实例)的 this

3. 严格模式的影响

在现代开发(模块化、构建工具、React/Vue 等)中,默认通常都是严格模式。

  • 这意味着:独立的函数调用 func() 中,thisundefined ,而不是 window。如果你尝试访问 this.property,会直接报错 Cannot read properties of undefined

4. 类方法中的 this 丢失

在 ES6 Class 中,方法默认是不绑定 this 的。

javascript 复制代码
class Person {
  constructor(name) {
    this.name = name;
  }
  greet() {
    console.log(`Hello, ${this.name}`);
  }
}

const p = new Person('Alice');
const sayHi = p.greet; 

sayHi(); 
// 报错 (严格模式): Cannot read properties of undefined (reading 'name')
// 因为 sayHi 作为普通函数调用,this 为 undefined
解决方案
  1. 在构造函数中绑定:this.greet = this.greet.bind(this);
  2. 使用箭头函数定义类字段(推荐):greet = () => { ... }

5. 优先级

如果多种规则冲突,优先级如下:

  1. new 绑定 (构造函数) >
  2. 显式绑定 (call/apply/bind) >
  3. 隐式绑定 (对象方法 obj.fn()) >
  4. 默认绑定 (独立函数调用)
    (注:箭头函数不参与此优先级排序,它在定义时就确定了)

箭头函数的作用域


箭头函数(Arrow Functions)是 ES6 引入的一种简洁的函数语法,它在作用域 (Scope)和 this 指向上与传统函数(普通函数)有显著区别。


核心结论:箭头函数没有自己的 thisargumentssupernew.target 绑定。它的 this 值是由定义时所在的上下文(外层作用域)决定的,也就是所谓的"词法作用域"(Lexical Scoping)。


以下是详细解析:

1. this 的词法作用域(最核心的区别)

  • 普通函数this 的值取决于调用方式
    • 作为对象方法调用:this 指向该对象。
    • 作为普通函数调用:this 指向全局对象(浏览器中是 window,严格模式下是 undefined)。
    • 使用 call/apply/bindthis 可以被显式修改。
  • 箭头函数this 的值取决于定义位置
    • 它不会创建自己的 this,而是捕获其所在上下文的 this 值。
    • 一旦定义,this 就固定了,无法通过 callapplybind 改变。

代码示例

javascript 复制代码
const obj = {
  name: 'Alice',
  
  // 普通函数
  regularFunc: function() {
    console.log('Regular:', this.name); 
    // 这里的 this 指向 obj
  },

  // 箭头函数
  arrowFunc: () => {
    console.log('Arrow:', this.name); 
    // 这里的 this 不指向 obj,而是指向定义时的外层作用域(通常是 window 或 undefined)
  }
};

obj.regularFunc(); // 输出: Regular: Alice
obj.arrowFunc();   // 输出: Arrow: undefined (在浏览器非严格模式下可能是 window)

2. 典型应用场景:回调函数中的 this

箭头函数最大的用处是在回调函数中保持外层 this 的上下文,避免了 var that = this.bind(this) 的繁琐写法。


场景:定时器或数组遍历

javascript 复制代码
function Counter() {
  this.count = 0;
  
  // 错误示范:普通函数在 setInterval 中,this 指向 window
  setInterval(function() {
    this.count++; 
    // 报错或无效,因为这里的 this 不是 Counter 实例
  }, 1000);

  // 正确示范:箭头函数继承外层的 this (即 Counter 实例)
  setInterval(() => {
    this.count++; 
    // 这里的 this 就是 Counter 实例,可以正常访问 count
    console.log(this.count);
  }, 1000);
}

const c = new Counter();

3. 其他作用域特性

除了 this,箭头函数在以下方面也没有自己的绑定,同样遵循词法作用域:


  • arguments 对象

    • 箭头函数内部没有 arguments 对象。
    • 如果访问 arguments,它会去外层作用域查找。
    • 替代方案 :使用剩余参数(Rest Parameters)...args
javascript 复制代码
const func = () => {
  console.log(arguments); // ReferenceError: arguments is not defined (除非外层有)
};

// 正确用法
const funcWithRest = (...args) => {
  console.log(args); 
};
  • 不能用作构造函数

    • 箭头函数没有 [[Construct]] 内部方法,不能使用 new 关键字调用。
    • 尝试 new 会抛出 TypeError
  • 没有 supernew.target

    • 同样继承自外层作用域。

4. 总结对比表

特性 普通函数 (Function Declaration/Expression) 箭头函数 (Arrow Function)
this 绑定 动态绑定 (取决于调用方式) 词法绑定 (取决于定义位置)
arguments 有自己的 arguments 对象 ,需使用剩余参数 ...args
可用作构造函数 是 (new Func()) (抛出错误)
可更改 this 是 (call, apply, bind) (调用这些方法无效)
拥有 super/new.target 否 (继承自外层)
适用场景 对象方法、构造函数、需要动态 this 的场景 回调函数、闭包、保持外层 this 的场景

什么时候应该使用箭头函数?

  1. 定义对象方法时 :如果你需要方法内部的 this 指向该对象本身,不要用箭头函数。
  2. 定义构造函数时 :需要用 new 调用的函数。
  3. 需要访问 arguments 对象时
  4. 事件监听器中需要动态 this (虽然现代开发中常用箭头函数配合显式传参,但如果依赖 this 指向触发事件的 DOM 元素,普通函数更方便)。

理解箭头函数的"词法作用域"特性,关键在于记住:它只是外层作用域的一个快捷引用,而不是一个新的作用域边界(针对 this 而言)。


call / apply / bind 对比总结


callapplybind 是 JavaScript 中用于显式绑定 this 的三个核心方法。它们都能改变函数执行时的上下文,但在调用时机参数传递方式上有所不同。


特性 call apply bind
核心功能 调用函数,并指定 this 指向 调用函数,并指定 this 指向 不立即调用,返回一个新函数(该函数的 this 被永久绑定)
执行时机 立即执行 立即执行 延迟执行 (返回的函数需要在后续调用)
返回值 原函数的执行结果 原函数的执行结果 一个新函数 (闭包)
参数传递方式 参数列表 (逐个传递) func.call(thisArg, arg1, arg2) 数组/类数组对象 func.apply(thisArg, [arg1, arg2]) 参数列表 (逐个传递,也可预置部分参数) func.bind(thisArg, arg1)
主要用途 1. 借用其他对象的方法 2. 构造函数继承 1. 借用方法 2. 处理数组参数 (如 Math.max) 3. 将类数组转为数组 (旧写法) 1. 事件回调中保持 this 2. 柯里化 (Currying) 3. 固定函数的上下文供后续使用
能否修改 this 能 (仅对当次调用有效) 能 (仅对当次调用有效) 能 (永久绑定,后续无法再通过 call/apply 修改)
示例代码 fn.call(obj, 1, 2) fn.apply(obj, [1, 2]) const newFn = fn.bind(obj, 1);<br>newFn(2);

详细场景解析与代码示例

1. 参数传递的区别 (call vs apply)

这是两者最直观的区别。


如果你知道参数的具体个数,用 call 更直观;如果参数是一个数组,用 apply 更方便。

javascript 复制代码
function sum(a, b, c) {
  return a + b + c;
}

const args = [1, 2, 3];

// call: 逐个传参
console.log(sum.call(null, 1, 2, 3)); // 6

// apply: 传入数组
console.log(sum.apply(null, args));   // 6

// 经典案例:求数组最大值
// Math.max 不接受数组,只接受参数列表
console.log(Math.max.apply(null, [1, 5, 9])); // 9 
// (ES6 后通常用展开运算符: Math.max(...[1, 5, 9]))

2. 执行时机的区别 (call/apply vs bind)

callapply 是"即插即用",而 bind 是"制造工具"。

javascript 复制代码
const obj = { name: 'Alice' };

function greet(greeting, punctuation) {
  console.log(`${greeting}, ${this.name}${punctuation}`);
}

// call/apply: 立即执行
greet.call(obj, 'Hello', '!');      // 输出: Hello, Alice!
greet.apply(obj, ['Hi', '.']);      // 输出: Hi, Alice.

// bind: 返回新函数,不立即执行
const boundGreet = greet.bind(obj, 'Hey'); 
// 此时什么也没发生

// 稍后在需要的地方调用
boundGreet('?');                    // 输出: Hey, Alice?

3. bind 的永久绑定特性

一旦使用 bind 绑定了 this,后续再尝试用 callapply 修改这个新函数的 this无效的。

javascript 复制代码
function show() {
  console.log(this.name);
}

const objA = { name: 'A' };
const objB = { name: 'B' };

const funcA = show.bind(objA);

funcA();            // 输出: A
funcA.call(objB);   // 输出: A (依然指向 objA,call 失效)

4. 常见应用场景

场景 推荐方法 原因
构造函数继承 call 在子类构造函数中调用父类构造函数,传递参数方便。 Parent.call(this, name)
数组操作借用 call / apply 让非数组对象使用数组方法。 Array.prototype.slice.call(arguments)
定时器/事件回调 bind 需要保存 this 上下文供未来回调使用。 setTimeout(this.handler.bind(this), 1000)
函数柯里化 bind 固定部分参数,生成新函数。 const add5 = add.bind(null, 5)
求最大/最小值 apply 将数组展开为参数列表传给 Math.max/minMath.max.apply(null, arr)

记忆口诀

  • CallC omma (逗号) -> 参数用逗号 分隔,立即调用。
  • ApplyA rray (数组) -> 参数用数组 包裹,立即调用。
  • BindB inding (绑定) -> 绑定 后返回新函数,以后再调。

注意事项

  1. 箭头函数 :这三个方法对箭头函数内部的 this 无效 。箭头函数的 this 在定义时已确定,无法被 call/apply/bind 改变(虽然可以调用它们,但第一个参数会被忽略)。
  2. nullundefined :在非严格模式下,如果传入 nullundefinedthis 会指向全局对象(浏览器为 window);在严格模式下,this 就是 nullundefined
相关推荐
2501_921930832 小时前
ReactNative项目OpenHarmony三方库集成实战:react-native-appearance(更推荐自带的Appearance)
javascript·react native·react.js
还是大剑师兰特2 小时前
Vue3 中 computed(计算属性)完整使用指南
前端·javascript·vue.js
csdn_aspnet2 小时前
查看 vite 与 vue 版本
javascript·vue.js
兆子龙2 小时前
前端工程师转型 AI Agent 工程师:后端能力补全指南
前端·javascript
前端大波3 小时前
Web Vitals 与前端性能监控实战
前端·javascript
毕设源码-赖学姐3 小时前
【开题答辩全过程】以 基于VUE的环保网站设计为例,包含答辩的问题和答案
前端·javascript·vue.js
小J听不清4 小时前
CSS 字体样式全解析:字体类型 / 大小 / 粗细 / 样式
前端·javascript·css·html·css3
进击的尘埃4 小时前
LangGraph.js 核心机制拆解:从状态管理到完整数据分析 Agent 实战
javascript
进击的尘埃4 小时前
Cursor Rules 配置指南:提示词工程与多模型切换
javascript