首页/文章/八股文

JavaScript 之 this指向

2025-03-27
21118 分钟
...

每当提到 JavaScript 的 this 关键字,许多开发者都会感到头痛。它似乎总是以出乎意料的方式工作,让人捉摸不透。但事实上,一旦你理解了 this 的行为,一切都会变得清晰起来。

this的基本概念

this 在 JavaScript 中是一个特殊的关键字,它代表的是函数执行上下文中的“当前对象”。然而,this 的指向并不是固定的,它取决于函数是如何被调用的。这一点在 ECMAScript 规范中有详细的定义:tc39.es/ecma262/#se…

并且,如果你不了解它的运作机制,有时就会出现一些你认为的莫名其妙的现象。

那么,这个 this 到底说了什么内容呢?

说起来也简单,this 的指向会根据函数的调用方式不同而变化。 对于一些常见的函数调用模式,我们可以总结如下:

  1. 全局作用域中的 this

    • 当在全局作用域中调用一个函数时,this 通常指向全局对象(在浏览器中是 window,在 Node.js 中是 global)。
  2. 作为普通函数调用时的 this

    • 当一个函数被直接调用时(不是作为一个对象的方法),this 通常指向全局对象(非严格模式下)或者 undefined(严格模式下)。
  3. 作为对象方法调用时的 this

    • 当一个函数作为对象的一个方法被调用时,this 通常指向那个对象。
  4. 构造函数中的 this

    • 使用 new 关键字调用构造函数时,this 指向新创建的对象实例。
  5. 事件处理程序中的 this

    • 当一个函数作为事件处理程序被调用时,this 通常指向触发事件的元素。
  6. 箭头函数中的 this

    • 箭头函数不会绑定自己的 this,而是继承自外围函数的作用域中的 this
  7. 使用 .call, .apply, .bind 改变 this

    • 可以使用这些方法显式地设置函数执行时的 this 值。

为了描述this 的不同绑定方式,程序员们达成了共识,给this的绑定也分为了一下几类

社区里广泛使用的术语

默认绑定

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

示例:

function sayName() {
  console.log(this.name);
}

var name = '全局名称';
sayName(); 

注意:当这里是严格模式的时候,会访问undefined导致TypeError

隐式绑定

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

var person = {
  name: '张三',
  sayName: function() {
    console.log(this.name);
  }
};

person.sayName(); 

函数调用中的this

当一个函数作为普通函数调用时,this的行为取决于上下文:

  • 非严格模式下,this指向全局对象。
  • 严格模式下,thisundefined
function greet() {
    console.log(this);
}

greet(); 

箭头函数中的this

箭头函数不绑定自己的this。它们继承外部函数或全局作用域中的this

const person = {
    name: 'Alice',
    greet: () => {
        console.log('Hello, ' + this.name);
    }
};

person.greet(); 

new绑定this

在构造函数中,this指向新创建的对象实例。

function Person(name) {
    this.name = name;
}

const alice = new Person('Alice');
console.log(alice.name); 

显式绑定

通过call、apply和bind方法,可以显式指定函数的this指向。

示例:

function sayName(age) {
  console.log(this.name + ',年龄:' + age);
}

var person = {
  name: '李四'
};

sayName.call(person, 25); 

案例

来吧,让我们从最简单的案例开始看。

function sayHello() {
  console.log(this);
}

sayHello(); 

请仔细阅读上面的代码,然后你认为 sayHello 函数中的 this 是什么?

要回答这个问题,我们先要了解全局作用域的this

在全局作用域(非严格模式下),this指向全局对象。在浏览器环境中,这通常指的是window对象。

console.log(this); 

在严格模式下(use strict), 全局作用域中的this将被设置为undefined

'use strict';
console.log(this); 

很明显, 这里是普通函数的调用,且不在严格模式下,所以this指向全局。我们在浏览器环境中运行得到的答案就是window,在node环境指向的是globalThis


接下来我们来看一个稍微复杂一点的例子:

const obj = {
  name: 'Alice',
  greet: function() {
    console.log(`Hello, my name is ${this.name}`);
  }
};

obj.greet(); 

请仔细阅读上面的代码,然后你认为 greet 函数中的 this 是什么?

相信你能够很自信的回答这个问题,greet 函数中的 this 指向 obj 对象。

这个答案是正确的,但如果我追问你是怎么得到这个答案的,我猜不了解 this 行为的你可能会说,因为它是作为一个对象的方法被调用的,所以 this 指向 obj

这里的情况是函数作为对象的方法被调用,因此遵循第3条规则。所以答案是'Hello, my name is Alice'


接下来我们继续增加复杂度:

const obj = {
  name: 'Alice',
  greet: function() {
    console.log(`Hello, my name is ${this.name}`);
  }
};

const greet = obj.greet;
greet();

这里我们将 greet 函数赋值给了一个新的变量,并直接调用它。你认为 greet 函数中的 this 是什么? 它依然是作为一个普通函数被调用了,遵循第二条,所以如果在浏览器环境下打印的仍是windows,但在node环境中会是globalThis


我们再来看看使用 new 关键字调用构造函数的情况:

function Person(name) {
  this.name = name;
  this.sayHello = function() {
    console.log(`Hello, my name is ${this.name}`);
  };
}

const alice = new Person('Alice');
alice.sayHello(); 

这里我们使用 new 关键字创建了一个新的 Person 实例。你认为 sayHello 函数中的 this 是什么? sayHello 函数中的 this 指向 alice 对象。

这里的情况是函数作为对象的方法被调用,因此遵循第3条规则。此外,alice 对象是在构造函数中创建的,因此遵循第4条规则。


接下来我们看看箭头函数中的 this 指向:

const obj = {
  name: 'Alice',
  greet: () => {
    console.log(`Hello, my name is ${this.name}`);
  }
};

obj.greet(); 

这里我们使用了箭头函数。你认为 greet 函数中的 this 是什么?

greet 函数中的 this 指向全局对象(非严格模式下)或者 undefined(严格模式下)。

这里的情况是箭头函数,因此遵循第6条规则,它继承了外围函数的作用域中的 this

我们再看一个例子

const user = {
    name: 'Alice',
    logName: function() {
        console.log('logName:', this.name); 
        
        const innerFunc1 = () => {
            console.log('innerFunc1:', this.name); 
        };
        
        const innerFunc2 = () => {
            console.log('innerFunc2:', this.name); 
            
            const innerFunc3 = () => {
                console.log('innerFunc3:', this.name); 
                
                setTimeout(() => {
                    console.log('setTimeout:', this.name); 
                }, 1000);
            };
            
            innerFunc3();
        };
        
        innerFunc1();
        innerFunc2();
    }
};


user.logName();


const globalArrowFunc = () => {
    console.log('globalArrowFunc:', this); 
};

globalArrowFunc();

先别看下面,你能独立分析出这里所有日志的内容吗?

  • 分析
  1. logName: 在对象方法 logName 内部,this 指向 user 对象,因此 this.name 输出 Alice

  2. innerFunc1: 箭头函数 innerFunc1 继承了 logNamethis,因此 this.name 输出 Alice

  3. innerFunc2: 同样,箭头函数 innerFunc2 继承了 logNamethis,因此 this.name 输出 Alice

  4. innerFunc3: 箭头函数 innerFunc3 也继承了 logNamethis,因此 this.name 输出 Alice

  5. setTimeout: 即使在 setTimeout 中定义了一个箭头函数,它依然继承了 logNamethis,因此 this.name 输出 Alice

  6. globalArrowFunc: 在全局作用域中定义的箭头函数,this 指向全局对象,因此 this 输出全局对象。

你都分析对了吗?


this指向的优先级

  • 当多种绑定规则同时存在时,它们的优先级顺序为:new绑定 > 显式绑定 > 隐式绑定 > 默认绑定。

如果您觉得这篇文章有帮助,请点个赞吧~

分享文章

相关文章

更多文章 →
八股文2025-10-13
cookie跨域介绍
Cookie 跨域问题详解 在 Web 开发中, Cookie 是最常见的客户端存储机制之一,用于记录用户登录状态、偏好设置、会话信息等。然而,当涉及到 跨域请求 时,Cookie 的行为往往变得复杂,尤其在现代浏览器的安全策略下,跨域 Cookie 的传递和写入都有严格的限制。 本文将从基础概念开始,逐步讲解 Cookie 的跨域机制、SameSite 属性、CORS 配置及常见问题解决方案。 一、Cookie 的基础概念 Cooki...
学习面试
八股文2025-10-09
JavaScript 闭包详解
JavaScript 闭包(Closure)详解 一、什么是闭包 闭包(Closure) 是 JavaScript 中一个非常核心且常被问到的概念。简单来说: 闭包是一个函数,它可以“记住”并访问其定义时所在的词法作用域,即使这个函数在其作用域之外被调用。 换句话说,当一个函数“嵌套”在另一个函数中,并且 内部函数引用了外部函数的变量 时,就形成了闭包。 示例: 虽然 已经执行完毕,但 依然可以访问 中的 。这就是闭包。 二、闭包的形成...
学习面试
八股文2025-10-07
开始性能优化之旅
事件循环机制 一、JavaScript 引擎的本质 核心职责 : 解析 JavaScript 语法 管理变量和内存 执行代码逻辑 不涉及 : 线程管理(Worker除外) I/O 操作 定时器控制 网络请求 常见引擎:V8(Chrome)、SpiderMonkey(Firefox)、JavaScriptCore(Safari) 二、宿主环境的扩展能力 宿主提供的多线程能力 : | 线程类型 | 功能 | 对应 API | | | | |...
学习面试
八股文2025-09-30
前端首屏优化
话说,我在面试的时候,80%的情况下,都会被问到首屏优化问题,烦,恨 TCP Slow Start(慢启动)概念 咱先不说标题的数字哪里来的,先说一个概念,就是TCP的慢启动。 你们想哈,在浏览器和服务器开始建立连接的时候,服务器是并不知道浏览端网络的带宽、拥塞状况。假设你本地的带宽是1M,如果一开始服务器就发送2M的文件,那浏览器压根就不是人类,忍忍或者挤挤就能接收到的,这样就可能会引发丢包和重传。 想想,这个该有什么办法解决这个问题...
学习面试
八股文2025-09-28
Function.prototype和Object.prototype
一道面试题引发的原型链血案,彻底搞懂 Function.prototype 和 Object.prototype 前言 有这样一道题: 目录 问题分析与答案揭晓 让我们先公布正确答案,然后深入分析原因: 为什么 是 而不是 ? 这是因为函数和实例的原型链路径完全不同! 原型链的本质机制 JavaScript中的两条原型链 在JavaScript中,存在两条不同的原型链:       关键理解:F.proto...
学习面试
八股文2025-09-10
让你彻底明白什么是闭包
今天我们来聊一个听起来很高大上,但实际上你可能天天在用(只是不知道它名字)的概念—— 闭包 。 一、一个你肯定写过的闭包 先别管定义,来看这段代码,你是不是再熟悉不过了? 恭喜你!这就是一个经典的闭包!是不是很简单? 二、为什么会有闭包?—— 背包的故事 想象一下,JavaScript 中的函数就像一个小机器人,当它被创建时,会背着一个神奇的 背包 。 这个背包里装着什么呢?装着它 出生时 所在环境的所有变量! 当我们调用 时,返回的那...
学习面试

评论

请登录后发表评论

去登录
加载评论中...

目录