news 2026/7/19 3:52:20

JavaScript 函数 this 全解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
JavaScript 函数 this 全解

文章目录

  • 一、this 是什么?
  • 二、四条铁律:this 绑定规则
    • 1. 默认绑定 —— 独立函数调用
    • 2. 隐式绑定 —— 作为对象方法调用
    • 3. 显式绑定 —— call、apply、bind
    • 4. new 绑定 —— 构造函数调用
  • 三、箭头函数的 this
  • 四、this 绑定优先级
  • 五、常见场景实战
    • 1. 事件处理函数中的 this
    • 2. 类(Class)中的 this
    • 3. 回调函数中的 this
  • 六、经典面试题分析
  • 七、最佳实践总结

一、this 是什么?

简单说,this 是函数执行时的上下文对象。在绝大多数情况下,函数的调用方式决定了 this 的值。它不能在函数定义时确定,只有在函数被调用时才会绑定。

理解这一点至关重要:同一个函数,不同方式调用,this 截然不同。

二、四条铁律:this 绑定规则

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

当函数直接调用(非严格模式)时,this 指向全局对象(浏览器中是 window,Node.js 中是 global)。

functionshowThis(){console.log(this);}showThis();// window(或 global)

如果是严格模式(‘use strict’),this 为 undefined。

functionstrictShow(){'use strict';console.log(this);}strictShow();// undefined

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

函数作为对象的属性被调用时,this 指向调用该方法的对象(即点号前面的对象)。

const obj={name:'Alice',sayName(){console.log(this.name);}};obj.sayName();// Alice,this 指向 obj

常见陷阱:隐式丢失
将方法赋值给变量或作为回调传递时,会丢失原对象,回退为默认绑定。

const fn=obj.sayName;fn();// undefined(非严格模式下 window.name 通常为''或 undefined)

在 setTimeout 中也一样:

setTimeout(obj.sayName,100);// this 指向 window

3. 显式绑定 —— call、apply、bind

这三大方法可以强制指定 this。

call(thisArg, arg1, arg2, …):立即执行函数,参数逐个传递。
apply(thisArg, [args]):立即执行函数,参数以数组形式传递。
bind(thisArg, …):返回一个绑定了 this 的新函数,不立即执行。

functiongreet(greeting){console.log(`${greeting}, ${this.name}`);}const user={name:'Bob'};greet.call(user,'Hi');// Hi, Bob greet.apply(user,['Hello']);// Hello, Bob const boundGreet=greet.bind(user,'Hey');boundGreet();// Hey, Bob

注意:一旦使用 bind 绑定,后续再使用 call/apply 也无法改变 this(箭头函数同样无法改变,见下文)。

4. new 绑定 —— 构造函数调用

使用 new 调用函数时,会创建一个全新的对象,并将 this 绑定到这个新对象上。

functionPerson(name){this.name=name;}const p=new Person('Carol');console.log(p.name);// Carol

new 做了四件事:
1、创建一个空对象。
2、该对象的proto指向构造函数的 prototype。
3、将 this 绑定到该对象上,执行构造函数。
4、如果构造函数没有返回对象,则返回 this。

三、箭头函数的 this

箭头函数没有自己的 this。它会捕获定义时外层作用域的 this 作为自己的 this,且之后无法被改变(call、apply、bind 均无效)。

const obj={name:'Dave',regularFunc(){setTimeout(function(){console.log('regular:', this.name);// undefined(或 window.name)},100);},arrowFunc(){setTimeout(()=>{console.log('arrow:', this.name);// Dave,捕获到 arrowFunc 的 this 即 obj},100);}};obj.regularFunc();obj.arrowFunc();

这解决了回调函数中 this 丢失的经典难题。但也要注意:箭头函数不能用作构造函数(new 会报错)。

四、this 绑定优先级

当多个规则同时出现时,优先级为:

new 绑定 > 显式绑定(call/apply/bind) > 隐式绑定 > 默认绑定

验证一下:

functionfoo(){console.log(this.a);}const obj1={a:1, foo};const obj2={a:2};obj1.foo.call(obj2);//2(显式 vs 隐式 → 显式赢)const bound=foo.bind(obj1);new bound();// undefined,new 会覆盖 bind,this 指向新对象

五、常见场景实战

1. 事件处理函数中的 this

DOM 事件中,this 默认指向绑定事件的元素(除 IE attachEvent 外)。

button.addEventListener('click',function(){console.log(this);// 指向 button 元素});

若在回调中使用箭头函数:

button.addEventListener('click',()=>{console.log(this);// 捕获外层 this,可能是 window});

2. 类(Class)中的 this

ES6 类的方法默认启用严格模式,且 this 容易在赋值传递时丢失。

class Counter{constructor(){this.count=0;}increment(){this.count++;}}const c=new Counter();const inc=c.increment;inc();// TypeError: Cannotreadproperty'count'of undefined

解决方案:

在构造函数中使用 bind:this.increment = this.increment.bind(this);
使用类字段(箭头函数语法):increment = () => { this.count++; }

3. 回调函数中的 this

在 map、filter、forEach 等数组方法中可传入 thisArg 作为第二个参数,或直接使用箭头函数。

const obj={multiplier:2};[1,2,3].map(function(x){returnx * this.multiplier;}, obj);//[2,4,6]// 或者[1,2,3].map(x=>x * obj.multiplier);

六、经典面试题分析

var length=10;functionfn(){console.log(this.length);}var obj={length:5, method: function(fn){fn();// ① arguments[0]();// ②}};obj.method(fn,1);

答案:① 10(或 undefined严格模式),② 2。

解析:

①:fn() 独立调用,this 指向 window,window.length = 10(或严格模式 undefined)。

②:arguments0 相当于 arguments.0(),arguments 对象调用了 fn,隐式绑定 this 为 arguments,而 arguments.length 是实参个数(2 个:fn 和 1),所以输出 2。

这道题极好地体现了隐式绑定与独立调用的差异。

七、最佳实践总结

1、优先使用箭头函数 来处理回调,避免 this 丢失问题,但需清楚它没有自己的 this。
2、在类中,若方法需作为回调传递,在构造函数内 bind 或使用箭头函数类字段。
3、普通函数用 call/apply/bind 显式绑定,尤其在工具函数中。
4、避免混用 var 和 this 在对象方法内,建议统一用 this 访问实例属性。
5、开启严格模式 是个好习惯,能让默认绑定的 undefined 尽早暴露错误。
6、理解优先级,遇到复杂 this 问题时按 new → 显式 → 隐式 → 默认 的顺序推理。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/19 3:51:26

Airflow云原生ELT实战:MWAA+Redshift+dbt架构落地指南

1. 项目概述:当Airflow离开本地服务器,真正扎根云原生环境“Airflow is on the Cloud”不是一句口号,而是过去三年我亲手推动的五个核心数据平台迁移项目中,最常被客户在立项会上反复确认的一句话。它背后藏着一个现实困境&#x…

作者头像 李华
网站建设 2026/7/19 3:49:49

Python hashlib模块:安全哈希与消息摘要详解

1. Python hashlib模块概述 hashlib是Python标准库中用于安全哈希和消息摘要的模块。它提供了多种加密哈希算法的通用接口,包括SHA系列、MD5以及更现代的BLAKE2算法。这个模块在数据完整性验证、密码存储、数字签名等场景中扮演着关键角色。 我第一次接触hashlib是…

作者头像 李华
网站建设 2026/7/19 3:49:44

Flask用户登录系统:密码安全与会话管理实践

1. 用户登录系统概述在Web应用开发中,用户登录系统是最基础也是最重要的功能模块之一。一个完整的用户登录系统需要处理用户认证、会话管理、密码安全等多个关键环节。使用Flask框架构建用户登录系统时,通常会结合Flask-Login、Werkzeug等扩展来实现这些…

作者头像 李华
网站建设 2026/7/19 3:45:30

技术类Newsletter设计方法论:从内容分发到认知操作系统

1. 项目概述:一份AI社区 Newsletter的底层逻辑与实操价值“Learn AI Together — Towards AI Community Newsletter #10”不是一封普通邮件,它是一份经过精密设计的知识流动协议,是AI领域里少有的、把“人”真正放在技术中心的社区运营样本。…

作者头像 李华
网站建设 2026/7/19 3:44:29

ARM Cortex-A9裸机双核通信:共享内存与中断同步实战

这次我们来看一个嵌入式开发中的实用技术——裸机双核通信。在ARM Cortex-A9这类多核嵌入式系统中,两个核心如何高效地交换数据是一个常见需求。通过共享内存结合中断同步的方式,可以实现低延迟、高效率的双核数据交互。这个方案的核心价值在于&#xff…

作者头像 李华