【无标题】

JavaScript 中的 this 指向:四种绑定规则与优先级详解

this 是 JavaScript 中一个核心且容易混淆的概念。它的指向不是在定义时确定的,而是在调用时确定的 (即在执行上下文创建时绑定)。本文将系统梳理 this 的四种绑定规则、优先级以及箭头函数的特殊行为。

一、四种绑定规则

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

当函数以独立函数形式调用(不作为对象的方法,也不通过 call/apply/bindnew 调用)时,this 默认绑定到全局对象。

  • 浏览器环境:window
  • Node.js 环境:global
  • 严格模式('use strict'):undefined
javascript 复制代码
function foo() {
  console.log(this); // 非严格模式: window; 严格模式: undefined
}
foo();

2. 隐式绑定(方法调用)

当函数作为某个对象的方法被调用时,this 隐式绑定到该调用对象。如果有多层对象嵌套,this 指向最近一层的调用对象。

javascript 复制代码
const obj = {
  name: 'Alice',
  greet() {
    console.log(this.name);
  }
};
obj.greet(); // 'Alice'

const outer = {
  inner: {
    name: 'Bob',
    greet() {
      console.log(this.name);
    }
  }
};
outer.inner.greet(); // 'Bob'(this 指向 inner,而非 outer)

3. 显式绑定(主动指定)

通过 callapplybind 方法可以主动指定函数的 this 指向,这是最灵活的绑定方式。

call / apply / bind 的区别
方法 是否立即执行 参数传递方式
call 第一个参数是 this 指向,后续参数逐个列举
apply 第一个参数是 this 指向,第二个参数为数组
bind 否(返回新函数) 预设 this 和部分参数,返回的新函数永久绑定(硬绑定)
javascript 复制代码
function say(age) {
  console.log(`${this.name}, ${age}`);
}
const person = { name: 'Tom' };

say.call(person, 25);      // Tom, 25
say.apply(person, [25]);   // Tom, 25
const bound = say.bind(person, 25);
bound();                   // Tom, 25

4. new 绑定(构造函数调用)

当函数通过 new 关键字调用时,作为构造函数,this 绑定到新创建的实例对象。

new 操作符内部执行四个步骤:

  1. 创建一个空的新对象。
  2. 将该新对象的 __proto__ 指向构造函数的 prototype
  3. 将构造函数的 this 绑定到这个新对象。
  4. 如果构造函数没有返回对象,则返回这个新对象;如果返回了一个对象,则返回该对象(此时 this 绑定失败,返回的是手动返回的对象)。
javascript 复制代码
function Person(name) {
  this.name = name;
}
const p = new Person('John');
console.log(p.name); // 'John'

二、优先级:多个规则同时生效时,谁为准?

this 绑定的优先级从高到低为:

new 绑定 > 显式绑定(特别是 bind) > 隐式绑定 > 默认绑定

javascript 复制代码
function foo() { console.log(this.name); }
const obj1 = { name: 'obj1', foo };
const obj2 = { name: 'obj2', foo };

obj1.foo();                // 'obj1'(隐式绑定)
obj1.foo.call(obj2);       // 'obj2'(显式绑定 > 隐式绑定)

const bound = foo.bind(obj1);
bound.call(obj2);          // 'obj1'(bind 硬绑定后无法再被 call/apply 改变)

const instance = new bound(); // 如果 foo 不是构造函数则报错,但若可作为构造函数,new 优先级高于 bind(新对象会覆盖 bind 绑定的 this)

注意bind 生成的硬绑定函数,其 this 不会被 call/apply 覆盖,但 new 操作可以覆盖 bind 的绑定(因为 new 优先级最高)。

三、箭头函数:没有自己的 this 绑定规则

箭头函数不绑定自己的 this ,它的 this 继承自定义时外层词法作用域 (即箭头函数声明时所在上下文的 this),并且无法通过 callapplybind 修改。

javascript 复制代码
const obj = {
  name: 'Eve',
  greet: () => {
    console.log(this.name); // 此处的 this 是外层(全局或上层函数)的 this,不是 obj
  }
};
obj.greet(); // undefined(假设全局没有 name)

箭头函数与普通函数的核心区别

特性 普通函数 箭头函数
this 绑定 调用时动态绑定(四种规则) 定义时继承外层 this,不可变
构造函数能力 可以(new 调用) 不能(new 会报错)
prototype 属性
arguments 对象 有(类数组) 无(可用剩余参数 ...args
简写语法 function() {} () => {},单参数可省略括号

特别注意

定义对象时的大括号 {} 不是一个单独的执行环境 ,它依旧处于全局环境中。因此对象字面量内部的方法若使用箭头函数,其 this 仍指向外层作用域(通常是 window 或模块作用域),而不是该对象。

javascript 复制代码
const obj = {
  name: 'Test',
  arrow: () => console.log(this.name),
  normal() { console.log(this.name); }
};
obj.arrow();  // undefined(this 指向全局)
obj.normal(); // 'Test'

四、总结速查表

调用方式 this 指向
普通函数独立调用 非严格模式:全局对象;严格模式:undefined
对象方法调用 该对象(最近一层)
call/apply/bind 显式指定的对象(bind 返回的函数不可再改变)
new 构造函数调用 新创建的实例对象
箭头函数 定义时外层作用域的 this(不可改变)

理解 this 的关键在于记住:不看定义在哪,只看如何调用

相关推荐
Twsit丶1 小时前
ECMAScript 常用特性整理(ES6 — ES13)
javascript
叼烟扛炮2 小时前
C++ 知识点17 友元
开发语言·c++·算法·友员
计算机安禾2 小时前
【c++面向对象编程】第2篇:类与对象(一):定义第一个类——成员变量与成员函数
开发语言·c++
Dxy12393102162 小时前
Python Pillow库:`img.format`与`img.mode`的区别详解
开发语言·python·pillow
亿牛云爬虫专家2 小时前
深度解析:数据采集场景下的 Java 代理技术实战
java·开发语言·数据采集·动态ip·动态代理·代理配置·连接池复用
小小仙。2 小时前
IT自学第四十二天
java·开发语言
TA远方2 小时前
【JavaScript】Promise对象使用方式研究和理解
javascript·编程·脚本·web·js·promise·委托
兩尛2 小时前
c++知识点5
开发语言·c++
澈2072 小时前
C++内存管理:new/delete与内存泄漏实战
开发语言·c++·内存分区