JavaScript 中的 Class 类

news2024/10/6 6:54:17

在这里插入图片描述

🔥 引言

在ECMAScript 2015(ES6)中,class 关键字被引入,为JavaScript带来了一种更接近传统面向对象语言的语法糖。类是创建对象的模板,它们封装了数据(属性)和行为(方法),是实现面向对象编程的基础单元。🎈


🎯 基础知识

在JavaScript这门灵活多变的语言中,Class是ES6引入的一个重要特性,它为面向对象编程提供了一种更简洁、更清晰的语法糖。通过Class,我们可以更容易地创建对象和管理继承关系。本文将从基础到进阶,带你深入探索Class的世界,让代码更加优雅且易于理解。👩‍🏫

基本声明

Class关键字用于定义一个类。类是一种蓝图,用于创建具有特定属性和方法的对象。

class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  sayHello() {
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
  }
}
  • constructor 是类的构造函数,用于初始化新创建的实例。
  • sayHello 是类的一个方法。

创建实例

创建类的实例就像调用构造函数一样简单:

const alice = new Person('Alice', 30);
alice.sayHello(); // 输出: Hello, my name is Alice and I am 30 years old.

🏗️ 构造函数 (Constructor)

构造函数是一个特殊的方法,用于初始化新创建的对象。每当你使用 new 关键字创建类的实例时,构造函数会被自动调用。

// 定义一个Person类,包含姓名(name)和年龄(age)属性
class Person {
  /**
   * 构造函数
   * @param {string} name - 人的名字
   * @param {number} age - 人的年龄
   */
  constructor(name, age) {
    // 使用'this'关键字给实例绑定属性
    // 'this'在此处指的是新创建的Person实例
    this.name = name; // 绑定name属性
    this.age = age;   // 绑定age属性
  }
}

// 使用'new'关键字创建Person类的实例
const alice = new Person('Alice', 30);

// 访问alice实例的name属性,输出:Alice
console.log(alice.name); // 输出: Alice
  • 构造函数签名:构造函数的名称必须是constructor,且没有返回值类型声明。
  • 参数:构造函数可以接收任意数量的参数,这些参数用于初始化实例的属性。
  • this的使用:在构造函数内部,this是一个特殊的关键字,它指向新创建的实例对象。
  • 默认值与验证:虽然本例未展示,但在构造函数中可以加入逻辑来为参数设置默认值或进行有效性验证。
  • 自动调用:实例化时,构造函数自动执行,无需手动调用

🔐 私有字段 (Private Fields)

ES2022 引入了私有字段的概念,通过在字段名前加 # 符号来声明,确保了字段的私密性和安全性。

class User {
  // 声明一个私有字段 #password
  #password;

  /**
   * 构造函数,用于初始化User实例
   * @param {string} password - 用户的密码
   */
  constructor(password) {
    // 使用私有字段 #password 存储密码
    this.#password = password;
  }

  /**
   * 公共方法,安全地获取密码(实际应用中应谨慎处理密码)
   * @returns {string} 返回存储的密码
   */
  getPassword() {
    return this.#password;
  }
}

// 创建User类的实例
const user = new User('mySecret');

// 通过公共方法访问私有字段
console.log(user.getPassword()); // 输出: mySecret

// 尝试直接访问私有字段会导致语法错误
// console.log(user.#password); // 这里会引发错误,因为#password是私有的
  • 封装性增强:私有字段有效限制了对类内部状态的直接访问,促进了良好的封装实践。
  • 安全性提升:防止外部代码无意或恶意修改类的内部数据,减少潜在的bug。
  • 设计原则:鼓励使用公共方法(getter/setter)来控制对私有字段的访问,便于未来修改内部实现而不影响外部接口。
  • 语法提示:编辑器和IDE通常会根据井号标记来提示哪些字段是私有的,帮助开发者遵循最佳实践。

虽然私有字段提高了封装性,但在设计API时,应谨慎考虑哪些数据应该设为私有,以平衡封装与灵活性的需求。


🔐 私有方法 (Private Methods)

私有方法是ES2020引入的另一项关键特性,与私有字段类似,它们通过在方法名前添加#符号来定义,以此增强类的封装性并保护类的内部实现细节不被外部访问或篡改。

class Calculator {
  // 声明一个私有方法 #multiply
  #multiply(a, b) {
    // 实现乘法逻辑
    return a * b;
  }

  /**
   * 公共方法,计算两个数的乘积
   * @param {number} a - 第一个乘数
   * @param {number} b - 第二个乘数
   * @returns {number} 两数的乘积
   */
  calculateProduct(a, b) {
    // 内部可以无障碍访问私有方法 #multiply
    return this.#multiply(a, b);
  }
}

// 创建Calculator类的实例
const calc = new Calculator();

// 通过公共方法间接调用私有方法,得到正确结果
console.log(calc.calculateProduct(2, 3)); // 输出: 6

// 尝试直接访问或调用私有方法会导致语法错误
// console.log(calc.#multiply(2, 3)); // 这是不允许的,会导致编译或运行时错误
  • 封装性:私有方法隐藏了类的内部计算逻辑,只暴露必要的公共接口,符合面向对象设计的封装原则。
  • 安全性:防止外部代码直接调用或修改私有方法,减少潜在错误和安全风险。
  • 设计模式:鼓励使用面向接口而非实现编程,增强代码的灵活性和可测试性。
  • 代码提示:现代IDE能够识别出私有方法,给予开发者明确的视觉提示,减少误操作。

私有方法是JavaScript类中一种强大的工具,它帮助开发者构建更加健壮、安全和易于维护的代码库。正确使用私有方法,是提升代码质量和团队协作效率的关键之一。


🧬 继承 (Inheritance)

JavaScript中的类支持继承,使用 extends 关键字来实现。子类可以继承父类的属性和方法,并可覆盖或扩展它们。

class Animal { // 父类或基类
  constructor(name) {
    this.name = name; // 初始化属性
  }

  // 定义一个方法
  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

class Dog extends Animal { // 子类或派生类,使用extends继承Animal
  constructor(name) {
    super(name); // 调用父类构造函数,传递参数
  }

  // 重写父类的speak方法以适应Dog类的特性
  speak() {
    console.log(`${this.name} barks.`);
  }
}

const myDog = new Dog('Rex'); // 创建Dog类的实例
myDog.speak(); // 输出: Rex barks.
  • extends关键字:用于创建一个类作为另一个类的子类,继承其所有非私有属性和方法。
  • super关键字:在子类构造函数中调用,用于访问父类的构造函数。这对于正确初始化继承自父类的属性至关重要。
  • 方法重写:子类可以定义与父类同名的方法,从而实现方法的覆盖(重写),根据子类的特点定制功能。
  • 访问控制与继承:父类的公有和受保护成员(没有使用#声明的)可以被子类继承和访问,私有成员则不能直接访问。
  • 多级继承:JavaScript支持多级继承,即一个子类可以再被其他类继承,形成继承链。

高级议题

  • Mixins与多重继承替代方案:由于JavaScript不直接支持多重继承,开发者可以利用组合、 Mixins模式或使用类的静态方法来模拟多重继承的效果。
  • 抽象类与接口概念:虽然JavaScript原生不支持抽象类和接口,但可以通过设计模式和约定来模仿这些概念,指导类的设计与实现。

通过继承机制,JavaScript的类体系能够构建层次清晰、复用性强的代码结构,是面向对象编程中不可或缺的一部分。


📦 静态方法与属性 (Static Methods and Properties)

静态方法和属性是类的一部分,它们不依赖于类的实例,可以直接通过类名访问。这使得静态成员成为执行与类的实例无关操作的理想选择,比如工具函数或常量值。在JavaScript中,使用static关键字来定义静态成员。

静态方法

  • 定义:静态方法是与类关联而非与类的实例关联的方法。它们通常用于执行与类的实例无关的操作,比如实用函数。
  • 访问:通过类名直接调用,无须创建类的实例。
  • 示例:在MathUtil类中,add是一个静态方法,用于执行简单的加法运算,不依赖于任何特定实例的状态。

静态属性

  • 定义:静态属性是类级别的变量,所有类的实例共享同一份数据。它们通常用于存储类的常量值或配置信息。
  • 初始化:静态属性可以在类定义中直接赋值,或者使用类的静态初始化块(ES2022引入)进行更复杂的初始化逻辑。
  • 示例PI是一个静态属性,存储圆周率的近似值,对所有MathUtil的调用者都是一致的。

示例代码

class MathUtil {
  // 静态属性定义,存储π的值
  static PI = 3.14159;

  // 静态方法定义,实现两个数的相加
  static add(a, b) {
    return a + b;
  }
}

// 直接通过类名调用静态方法和属性
console.log(MathUtil.add(2, 3)); // 输出: 5
console.log(MathUtil.PI); // 输出: 3.14159

// 注意:静态成员不能通过类的实例访问
// const util = new MathUtil();
// util.add(2, 3); // 这是不推荐的,尽管技术上可行,但违反了静态方法的使用原则
  • 共享性:静态方法和属性对于类的所有实例都是共享的,修改它们会影响所有实例。
  • 内存占用:每个静态成员只占用一份内存空间,不同于实例成员每个实例都会有一份。
  • 设计原则:静态成员适合于那些与类的状态无关,仅需通过类本身访问的功能或数据。
  • 访问限制:静态成员不能通过实例访问,这是保持设计清晰和减少误用的一种方式。

综上所述,静态方法和属性为JavaScript类的设计提供了额外的灵活性和功能,特别适用于那些全局工具函数或类级常量的场景。正确使用静态成员,可以使代码更加整洁高效。


🔄 Getter 和 Setter 解析

Getter和Setter是JavaScript类中用于定义访问和修改对象属性的特殊方法,它们为属性访问提供了额外的控制层,允许在获取或设置属性值时执行自定义逻辑,如数据验证、格式化或触发副作用。这种机制被称为“访问器属性”。

基本概念

  • Getter:用于读取属性值。定义时使用get关键字,无参数,返回属性值。
  • Setter:用于设置属性值。定义时使用set关键字,有一个参数(通常是新值),用于更新属性。

示例代码分析

class Product {
  constructor(price) {
    // 使用下划线前缀是一种约定,表示这个属性应该是“受保护”的或内部使用的
    this._price = price;
  }

  // Getter方法,获取价格
  get price() {
    return this._price;
  }

  // Setter方法,设置价格,包含验证逻辑
  set price(newValue) {
    // 验证价格是否为正数
    if (newValue < 0) {
      throw new Error('Price cannot be negative');
    }
    // 通过验证后,更新价格
    this._price = newValue;
  }
}

// 创建Product实例
const product = new Product(10);

// 使用getter读取价格
console.log(product.price); // 输出: 10

// 尝试使用setter设置负价格,这将触发错误
try {
  product.price = -5;
} catch (error) {
  console.error(error.message); // 输出: Price cannot be negative
}

重要用途

  1. 数据验证:在setter中添加条件检查,确保数据的合法性,如上述示例中确保价格非负。
  2. 属性转换:在getter或setter中转换数据格式,例如,将内部存储的日期转换为字符串输出。
  3. 触发事件:当属性值改变时,可以在setter内部触发自定义事件或执行其他操作。
  4. 封装实现:隐藏属性的实际存储方式或逻辑,提供简洁的公共接口。

注意事项

  • Getter和Setter应成对出现,以便提供一致的访问控制机制。
  • 使用下划线前缀(如_price)是一种常见的做法,用于区分私有或受保护的属性,但JavaScript本身并不强制私有性。
  • 从ES6开始,可以使用私有字段(如#price)来实现更严格的封装,但请注意,私有字段不能直接定义getter和setter。

通过getter和setter,开发者可以灵活地管理对象状态,增强代码的健壮性和可维护性,是实现面向对象设计中封装原则的有效手段。


📚 总结

在JavaScript中,class语法作为一种面向对象编程的结构化方式,为创建可复用、易于维护的代码提供了强大支持。

  1. 构造函数 (Constructor):

    • 负责初始化新创建的类实例,自动在使用new关键字实例化时调用。
    • 可以接收参数并使用this关键字为实例分配属性。
    • 是类定义中的核心入口点,奠定了实例的基础状态。
  2. 私有字段与方法 (Private Fields & Methods):

    • ES2022 引入了私有字段与方法,通过在名称前加#来声明,增强了类的封装性。
    • 私有字段和方法仅能在类内部访问,外部代码无法直接触及,确保了数据安全和实现细节的隐藏。
    • 促进良好的封装实践,限制了对内部状态的不当修改。
  3. 继承 (Inheritance):

    • 使用extends关键字实现类的继承,子类能继承父类的属性和方法。
    • super关键字允许子类调用父类的构造函数和方法,支持方法覆盖以实现具体化或优化。
    • 构建类的层次结构,促进代码复用和模块化设计。
  4. 静态方法与属性 (Static Methods and Properties):

    • 通过static关键字定义,直接通过类名访问,不依赖于类的实例。
    • 适用于类级别操作和常量数据,如工具函数和配置项。
    • 提高代码组织性和效率,避免了每次实例化时的资源消耗。
  5. Getter和Setter:

    • 提供了一种控制属性访问和修改的机制,增加逻辑验证和灵活性。
    • Getter用于获取属性值,Setter用于设置属性值,并可嵌入验证逻辑。
    • 有助于实现数据验证、格式化和封装,保持数据一致性与安全。

综上所述,JavaScript的class语法通过构造函数、私有特性、继承机制、静态成员以及getter和setter,为开发者提供了一套完整的面向对象编程工具集,支持构建复杂而结构化的应用程序。这些特性不仅提升了代码的可读性和可维护性,也促进了面向对象设计原则在JavaScript中的应用。


🔐 相关链接

  • JavaScript中call、apply与bind详解: JavaScript中call、apply与bind的区别以及使用
  • JavaScript数组常用方法: JavaScript数组全方位探索指南

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1646348.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

25.哀家要长脑子了---哈希表

1.525. 连续数组 - 力扣&#xff08;LeetCode&#xff09; 在我对通义千问的一番折磨下&#xff0c;终于弄清楚一点点了。哈希表存储前缀和数组值 用一个counter来记录nums中0、1数量差值的变化。 哈希表map存储某个特定的counter值首次出现的位置。counter的计算&#xff1a;…

2023年乡镇街道边界数据、行政村边界、省市县区划边界、建筑轮廓边界数据、流域边界数据、降雨量分布、气温分布、道路网分布

数据范围&#xff1a;全国行政区划-行政乡镇街道边界 数据类型&#xff1a;面状数据&#xff0c;全国各省市县【乡镇-边界】乡村界、乡村范围 数据属性&#xff1a;标准12位行政区划编码、乡镇名称、所属地区 分辨率&#xff1a;1:2万--1:5万 数据格式&#xff1a;SHP数据&…

力扣100284. 有效单词(C++)

【题解】 (实际在力扣中运行的代码只需要把下方的check函数放到力扣作答区给的模板中就可以) #include <bits/stdc.h> #include <iostream> #include <vector> #include <string> #include <cctype> #include <cstring> #include <st…

【简单介绍下Debian常用命令】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

tomcat-以服务的方式重启tomcat

背景 双击tomcat的bin目录下面的startup.bat&#xff0c;会留下一个cmd的窗口&#xff0c;很不优雅 使用service服务的方式启动&#xff0c;并且设置为自动启动 找到tomcat的bin目录输入cmd&#xff0c;按Enter&#xff0c;进入命令行界面。执行“service.bat install” 。&…

最近惊爆谷歌裁员

Python团队还没解散完&#xff0c;谷歌又对Flutter、Dart动手了。 什么原因呢&#xff0c;猜测啊。 谷歌裁员Python的具体原因可能是因为公司在进行技术栈的调整和优化。Python作为一种脚本语言&#xff0c;在某些情况下可能无法提供足够的性能或者扩展性&#xff0c;尤其是在…

【小菜鸟之---Ansible基础详解】

文章目录 1 【Ansible简介】1.1简介1.2 Ansible 特点1.3 Ansible的工作机制1.4Ansible任务工作模式 2【安装部署】2.1安装命令2.2 Ansible配置文件2.3主机清单配置2.4 基于ssh免密登录2.5常用命令 3【Ansible常用模块】3.1 ping模块3.2 shell模块3.3 command模块3.4 copy模块3.…

【notes2】并发,IO,内存

文章目录 1.线程/协程/异步&#xff1a;并发对应硬件资源是cpu&#xff0c;线程是操作系统如何利用cpu资源的一种抽象2.并发&#xff1a;cpu&#xff0c;线程2.1 可见性&#xff1a;volatile2.2 原子性&#xff08;读写原子&#xff09;&#xff1a;AtomicInteger/synchronized…

【从零开始学架构 前言】整体的学习路线

本文是《从零开始学架构》的第一篇学习笔记&#xff0c;在工作6年左右的这个时间点需要有一些先行的理论来指导即将面临的复杂实践&#xff0c;以便在真正面临复杂实践的时候能有所参照。 主要从以下几个方面和顺序来进行学习 架构基础&#xff1a;从架构设计的本质、历史背景…

# 怎么关闭 win10 系统中自带的【文件预览】功能?关闭WIN10【文件预览】功能的方法

怎么关闭 win10 系统中自带的【文件预览】功能&#xff1f;关闭WIN10【文件预览】功能的方法 win10 系统中自带的【文件预览】功能&#xff0c;默认是开启状态的&#xff0c;如果需要关闭它&#xff0c;一步搞定。 1、打开电脑文件浏览器&#xff0c;随便进入有文件的一个文件…

【Linux】操作系统

上一篇博客我们从硬件的角度谈了计算机&#xff0c;我们说到了计算机的效率跟操作系统写的好不好有着直接的关系&#xff0c;那么这篇博客我们从软件的角度&#xff0c;就来谈一谈究竟什么是操作系统&#xff0c;为什么要有操作系统&#xff1f; 首先我们来大体的认识一下操作…

php基础知识快速入门

一、PHP基本知识 1、php介绍&#xff1a; php是一种创建动态交互性的强有力的服务器脚本语言&#xff0c;PHP是开源免费的&#xff0c;并且使用广泛。PHP是解释性语言&#xff0c;按顺序从上往下执行&#xff0c;无需编译&#xff0c;直接运行。PHP脚本在服务器上运行。 2、ph…

解决 java: 非法字符: ‘\ufeff‘

【报错解释】&#xff1a; 该错误通常发生在尝试编译Java源代码文件时&#xff0c;文件开头的字符是一个字节顺序标记&#xff08;Byte Order Mark&#xff0c;BOM&#xff09;&#xff0c;即\ufeff。在Java中&#xff0c;\ufeff不是一个合法的字符&#xff0c;因此编译器会报…

【码银送书第十九期】《图算法:行业应用与实践》

作者&#xff1a;嬴图团队 01 前言 在当今工业领域&#xff0c;图思维方式与图数据技术的应用日益广泛&#xff0c;成为图数据探索、挖掘与应用的坚实基础。本文旨在分享嬴图团队在算法实践应用中的宝贵经验与深刻思考&#xff0c;不仅促进业界爱好者之间的交流&#xff0c;…

图卷积神经网络GCN

计算图 通过消息传递的框架构建一个局部邻域的计算图 每个节点分别构建自己的计算图 理论上任意深度&#xff0c;根据六度空间理论&#xff0c;层数一般不会超过六层 基本过程 数学形式 矩阵表示 代码表示 图计算改进 能够听见自己内心的声音 最终形式 如何训…

Flink时间语义 | 大数据技术

⭐简单说两句⭐ ✨ 正在努力的小叮当~ &#x1f496; 超级爱分享&#xff0c;分享各种有趣干货&#xff01; &#x1f469;‍&#x1f4bb; 提供&#xff1a;模拟面试 | 简历诊断 | 独家简历模板 &#x1f308; 感谢关注&#xff0c;关注了你就是我的超级粉丝啦&#xff01; &a…

抖捧Ai实景直播案例-助力景区商家假期销量爆棚!

出门游玩你会提前做攻略吗&#xff1f;是不是早早就规划好了路线、定好了门票车票。这就是属于消费前置的动作。所以在消费前置的情况&#xff0c;各个景区的商家都铆足劲发展线上流量&#xff0c;短视频直播也成为众多商家必做的宣传渠道&#xff0c;特别是五一假期更让不少商…

C++面向对象程序设计 - 继承与派生进一步讨论

C中所谓“继承”就是在一个已存在的类的基础上建立一个新类&#xff0c;从已有的类那里获得已有特性&#xff0c;叫做类的继承。从另一角度说&#xff0c;从已有的类&#xff08;父类&#xff09;产生一个新的子类&#xff0c;称为类的派生。一个派生类只从一个基类派生&#x…

分布式与一致性协议之ZAB协议(四)

ZAB协议 ZooKeeper是如何选举领导者的。 首先我们来看看ZooKeeper是如何实现成员身份的&#xff1f; 在ZooKeeper中&#xff0c;成员状态是在QuorumPeer.java中实现的&#xff0c;为枚举型变量 public enum ServerState { LOOKING, FOLLOWING, LEADING, OBSERVING }其实&…

权益商城系统源码 现支持多种支付方式

简介&#xff1a; 权益商城系统源码&#xff0c;支持多种支付方式&#xff0c;后台商品管理&#xff0c;订单管理&#xff0c;串货管理&#xff0c;分站管理&#xff0c;会员列表&#xff0c;分销日志&#xff0c;应用配置。 上传到服务器&#xff0c;修改数据库信息&#xff…