当前位置: 首页 > news >正文

Javascript:类中的成员变量及其this的理解

先看下经典代码,如下所示:

最标准原生 JS 类写法

// 定义一个类
class Demo {// 类中的普通成员变量name = "张三";// 类中的普通成员函数showInfo() {// 函数内部使用当前类的成员变量console.log(this.name);}
}// 实例化类
let obj = new Demo();// 调用方法
obj.showInfo();

ES5 老式写法(与上面等价的)

function Demo() {// 普通变量this.name = "李四";
}// 原型普通函数
Demo.prototype.show = function(){console.log(this.name);
};let obj = new Demo();
obj.show();

再列举一个例子进行说明:

class Person {// 实例自身的普通成员变量msg = "底层原理";// 普通成员方法print() {console.log(this.msg);}
}const p = new Person();
p.print();

1、this 核心本质

在类的普通成员函数中:this 永远指向当前被 new 出来的实例对象

2、内存结构图直白解释

  1. new Person() 执行时,会在堆内存开辟一块空间,这就是实例对象
  2. msg 这个变量,直接挂载在这个实例对象自身上。
  3. print 函数不在实例上,存在类的原型 (prototype) 上。
  4. 当你执行 p.print()
    • 调用者是 p 实例
    • 函数内部的 this 就自动绑定为调用者 p
    • 所以 this.msg 等价于 p.msg

致命易错点(必考)

class Person {msg = "底层原理";print() {console.log(this.msg);}
}const p = new Person();// 单独提取函数出来
const fn = p.print;
fn();

此时打印:undefined

原因

  • p.print() 调用:前面有对象,this 绑定 p
  • fn() 直接独立调用:前面无调用者,浏览器模式下 this 指向 window,window 上没有 msg

箭头函数完美解决(类里面写法)

class Person {msg = "底层原理";// 箭头函数写法print = () => {console.log(this.msg);}
}const p = new Person();
const fn = p.print;
fn(); // 正常打印

原理:

箭头函数本身没有自己的 this,它会继承定义时外层的 this,永远绑定当前实例,永不改变。
 

总结一句话

  1. 普通成员函数:this 看调用者
  2. 箭头成员函数:this 看定义位置,永久绑定实例

----------------------------------------------------------------------------------------------------------------------

----------------------------------------------------------------------------------------------------------------------

继续看实例,从作用域链的角度进行分析,如下:

class Person {// 实例自身的普通成员变量msg = "底层原理";// 普通成员方法print() {console.log(msg);}
}const p = new Person();
p.print();

以上代码中的没有使用this,我们探讨一下JavaScript 类内部的作用域规则、变量查找机制,以及普通成员变量 / 方法的本质。

先直接说结论:
 
代码报错 msg is not defined,根本原因是:print 方法内部找不到变量 msg —— 类成员变量不是 “局部变量”,不会自动进入方法的作用域链。

1. 先拆解代码的执行与作用域结构

我们把代码翻译成底层实际运行的逻辑,你会瞬间明白:
class Person {// 这是【实例成员变量】// 等价于:constructor 里写 this.msg = "底层原理"msg = "底层原理";// 这是【原型方法】print() {// 关键:这里的代码执行时,作用域里根本没有 msgconsole.log(msg); }
}
 
类内部不是一个统一的作用域
  • msg = ...实例属性,挂载在 this(注意:这是实例属性,不是变量,只有变量才会有作用域这一说法)
  • print() {} 内部是一个独立的函数作用域

2. 核心:JS 变量查找规则 = 作用域链查找(和 this 无关)

当你在函数里写:
console.log(msg);
 
JS 引擎会按照作用域链一层层找变量 msg
  1. 先找 print 函数自身作用域 → 没有
  2. 再找 类的私有 / 块级作用域 → 类没有块级作用域,找不到
  3. 再找 全局作用域 → 还是没有
  4. 最终:抛出错误 msg is not defined

3. 关键知识点:类成员变量 ≠ 局部变量

很多新手最容易踩坑的点:

❌ 错误认知

“我在类里写了 msg,所以类里所有方法都能直接用 msg。”

✅ 正确认知

  • msg 不是变量
  • msg 是实例对象的属性,存在于 this
  • 属性不会自动加入作用域
  • 作用域只查找变量,不查找对象属性

4. 对比:为什么必须写 this.msg 才正确?

print() {console.log(this.msg); // 正确
}
 
这里发生了两件完全不同的事:
  1. this 是通过执行上下文确定的对象(p 对象)
  2. this.msg 是访问对象的属性,不是作用域变量查找
一个是对象属性访问,一个是作用域变量解析完全是两套机制

5. 用最通俗的话总结作用域问题

你可以把作用域想象成房间
  • 类的 {} 不是一个房间
  • 每个方法 {} 才是一个独立房间
  • msg 根本没放在房间里,而是贴在实例对象身上
  • 你在房间里大喊 msg,当然找不到

6. 最终结论(深度版)

作用域角度看:
  1. ES6 class 没有类作用域,只有方法级函数作用域(注意:这个也是理解的重点)
  2. 类字段 msg实例属性,不是作用域变量
  3. print 内部执行 console.log(msg) 时,JS 只会沿着作用域链查找变量
  4. 作用域链中不存在 msg 变量,因此抛出 not defined
  5. 必须用 this.msg 访问属性,这属于对象属性访问,与作用域无关

7. 修正后的正确代码

class Person {msg = "底层原理";print() {// 必须通过 this 访问实例属性console.log(this.msg); }
}const p = new Person();
p.print(); // 输出:底层原理
 

总结

  1. 作用域查找变量,对象访问属性
  2. 类成员变量是属性,不是变量,不会进入作用域
  3. 方法内部直接写 msg → 作用域找不到 → 报错
  4. 方法内部写 this.msg → 访问实例属性 → 正确
这就是 JS 类最经典、最核心的作用域与 this 面试题之一。

注:下一篇中我们会讲到类中,也就是class中,为什么不能使用avr a = 123;或let a =123;的具体原因。与本篇还是有着紧密的联系的。

 

http://www.jsqmd.com/news/833755/

相关文章:

  • 终极指南:让老Mac重获新生,OpenCore Legacy Patcher完整解决方案
  • 终极指南:3步快速完成Mac微信聊天记录备份与导出
  • Windows右键菜单管理神器ContextMenuManager:告别杂乱,打造高效工作台的终极指南
  • 轻量级爬虫框架slacrawl:基于规则驱动的模块化数据采集实践
  • 镜像空间全域透视,赋能多维场景一体化透明数智治理技术白皮书
  • 如何快速突破平台限制:跨平台Steam创意工坊模组下载终极指南
  • 一种用于并网光伏系统的创新型多层逆变器,以降低总谐波失真(THD)研究(Matlab代码实现)
  • 哈尔滨家庭教育指导师报名避坑指南:正规入口、靠谱机构与口碑推荐 - 优选机构推荐
  • 10分钟打造个人游戏云:Sunshine开源串流服务器全攻略
  • 【限时公开】后印象派专属--ar 16:9 --style raw --stylize 800参数组合包(含塞尚构图/修拉点彩/劳特累克动态线共12套已验证prompt模板)
  • 抖音批量下载器终极指南:5分钟学会无水印视频下载技巧
  • 专业视频资源捕获指南:猫抓扩展的完整高效解决方案
  • 终极FGO自动化助手:告别枯燥刷本,每天节省3小时游戏时间
  • 镜像空间全域透视,赋能多维场景一体化透明数智治理
  • JVM调优实战:让你的服务性能提升50%
  • 如何在10分钟内搭建个人游戏流媒体服务器:Sunshine跨平台游戏串流完全指南
  • ncmdumpGUI:3分钟解锁网易云音乐ncm格式,让你的音乐无处不在
  • 【优化交叉口的绿灯时间】基于遗传算法的交通灯管理研究(Matlab代码实现)
  • 如何快速掌握yfinance:从零到实战的Python金融数据获取终极指南
  • 3DS游戏格式转换实战指南:5步完成CCI到CIA的高效转换
  • 7 个 React 性能优化技巧,让你的应用快如闪电
  • 通达信数据解析终极指南:mootdx让金融数据获取变得如此简单
  • 使用Taotoken快速为Hermes Agent配置自定义模型供应商
  • Java 大厂面试 200 题完整版含答案解析
  • 终极Python通达信数据解析方案:mootdx完整使用指南与金融量化实践
  • OpenPilot:开源自动驾驶系统的全面解析与实践指南
  • 完整指南:如何在Windows系统中使用ViGEmBus实现游戏控制器虚拟化
  • 产品模式:从代码仓库到持续交付的现代软件工程实践
  • 基于LLM的长文本摘要工具SumGPT:从原理到本地化部署实战
  • 抖音批量下载神器:5分钟学会免费高效下载视频、音乐和直播