JS的类以及对象🤔

5 阅读4分钟

面向对象编程(OOP)的核心思想,以及在 JavaScript(特别是 ES6)中的具体实现和特性。

一、面向对象的核心思想

1. 基本概念

  • 类与对象:类是对现实世界中具有相同特征事物的抽象描述(模板);对象是根据类创建出来的具体实例(具体事物)。一个类可以衍生出无穷无尽的对象。
  • 核心目的:面向对象是对具象世界的抽象表达。通过屏蔽不关心的细节和差异,提取事物的共性进行分类,研究其共同的属性和行为,从而形成固定的、丰富的处理方案(即设计模式)。
  • 设计 vs 编程
    • 面向对象设计:从现实世界抽象出“类”的思维过程。
    • 面向对象编程:将设计转换成具体代码(如 JS 中的类)的实现过程。
    • 封装:在 JS 中,把属性和方法写在类里面来描述一系列相同事务的共同特征,这个过程就叫封装。

2. 编程范式对比

  • 面向过程:以过程(函数)为中心,关注“怎么做”,将问题拆解为一步步的执行流程。
  • 面向对象:以对象(数据和行为)为中心,关注“谁来做”,系统由哪些东西组成,它们有什么状态(属性)以及彼此间的关系。
  • 函数式编程:强调一切都是计算,以及计算与计算之间的关系。

二、JavaScript 中的类与成员

1. 类的组成与成员

  • 构造函数 (Constructor):描述对象产生时的特征。在创建对象时自动调用,用于初始化。
  • 实例成员:属于对象本身。每个对象都有自己独立的副本,互不影响(如:每个学生的姓名)。
  • 静态成员 (Static):属于类本身。不需要创建对象就能访问,所有对象共享同一份数据(如:全局配置、计数器)。

2. 访问器(Getter & Setter)

  • 作用:让一个方法看起来像属性,避免数据冗余。例如 总价 = 数量 * 单价,总价不需要作为属性存储,而是通过计算得出。
  • 实现方式
    • ES5:使用 Object.defineProperty(object, 'total', { get: function () { return this.price * this.count; } })
    • ES6:直接使用语法糖 get 方法名() {}。一旦读取这个属性,就相当于调用了这个方法。

三、ES6 类的特性与底层机制

1. 可枚举性 (Enumerable) ES6 的类在设计上对属性的可枚举性做了严格限制,以保持代码的整洁:

  • 类的方法:在 ES6 类中定义的方法,默认是不可枚举的(enumerable: false)。
  • 访问器属性 (Getter/Setter):在类中定义的访问器属性也是不可枚举的。
    • 表现:在控制台查看对象时,这些属性通常会显示为淡淡的颜色;使用 Object.keys() 遍历时会忽略它们。
    • ES5 转换注意:如果在 ES5 中手动模拟类的访问器,需要显式加上 enumerable: false 来保持与 ES6 一致的行为。

2. 暂时性死区 (TDZ)

  • 定义:在 ES6 中,使用 letconstclass 声明变量/类之前,该变量处于不可访问的状态。
  • 类的表现:ES6 的类不存在变量提升,且必须通过 new 来调用
  • 检测是否使用 New:可以通过判断 this 的原型来检测类是否被正确实例化。例如:if (new.target === Product) 或检查 this 的原型链是否指向 Product.prototype,以此判断有无使用 new

3. ES6 转 ES5 (Babel 原理简述)

  • 核心转换:ES6 的类本质上是 ES5 构造函数的“语法糖”。
  • 转换逻辑
    • 类的 constructor 转换为 ES5 的构造函数。
    • 类的方法转换为添加到 构造函数.prototype 上的方法。
    • 静态方法转换为直接添加到构造函数上的方法。
    • 继承机制通过原型链(Object.create 等)来实现。

总结

面向对象不仅仅是写代码的方式,更是一种对现实世界的抽象思维。在 JavaScript 中,我们通过来封装共同的特征(属性)和行为(方法),利用实例与静态成员区分个体与整体,并通过访问器优雅地处理计算属性。理解 ES6 类底层的不可枚举性暂时性死区,能帮助我们写出更健壮、更符合语言规范的代码。