【JavaScript-Day 33】深入浅出 ES6 Class:从入门到精通面向对象新姿势

前端100道八股文+50道场景题 闭包是指一个函数能够访问它外部函数作用域中的变量,即使外部函数已经执行完毕。形成闭包需要三个条件:函数嵌套、内部函数引用外部变量、外部函数返回内部函数。闭包的主要作用有三个:第一,实现数据私有化。比如可以用闭包创建私有变量,外部无法直接访问,只能通过特定方法操作。第二,模块化。可以用闭包封装代码,只暴露需要的接口。第三,函数柯里化和防抖节流等高级技巧都会用到闭包。闭包的缺点是可能造成内存泄漏。因为闭包引用的外部变量不会被垃圾回收,如果大量使用闭包且不及时清理,会占用过多内存。 阅读详情

Langchain系列文章目录

01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南
02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖
03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南
04-玩转 LangChain:从文档加载到高效问答系统构建的全程实战
05-玩转 LangChain:深度评估问答系统的三种高效方法(示例生成、手动评估与LLM辅助评估)
06-从 0 到 1 掌握 LangChain Agents:自定义工具 + LLM 打造智能工作流!
07-【深度解析】从GPT-1到GPT-4:ChatGPT背后的核心原理全揭秘
08-【万字长文】MCP深度解析:打通AI与世界的“USB-C”,模型上下文协议原理、实践与未来

Python系列文章目录

PyTorch系列文章目录

机器学习系列文章目录

深度学习系列文章目录

Java系列文章目录

JavaScript系列文章目录

01-【JavaScript-Day 1】从零开始:全面了解 JavaScript 是什么、为什么学以及它与 Java 的区别
02-【JavaScript-Day 2】开启 JS 之旅:从浏览器控制台到 <script> 标签的 Hello World 实践
03-【JavaScript-Day 3】掌握JS语法规则:语句、分号、注释与大小写敏感详解
04-【JavaScript-Day 4】var 完全指南:掌握变量声明、作用域及提升
05-【JavaScript-Day 5】告别 var 陷阱:深入理解 let 和 const 的妙用
06-【JavaScript-Day 6】从零到精通:JavaScript 原始类型 String, Number, Boolean, Null, Undefined, Symbol, BigInt 详解
07-【JavaScript-Day 7】全面解析 Number 与 String:JS 数据核心操作指南
08-【JavaScript-Day 8】告别混淆:一文彻底搞懂 JavaScript 的 Boolean、null 和 undefined
09-【JavaScript-Day 9】从基础到进阶:掌握 JavaScript 核心运算符之算术与赋值篇
10-【JavaScript-Day 10】掌握代码决策核心:详解比较、逻辑与三元运算符
11-【JavaScript-Day 11】避坑指南!深入理解JavaScript隐式和显式类型转换
12-【JavaScript-Day 12】掌握程序流程:深入解析 if…else 条件语句
13-【JavaScript-Day 13】告别冗长if-else:精通switch语句,让代码清爽高效!
14-【JavaScript-Day 14】玩转 for 循环:从基础语法到遍历数组实战
15-【JavaScript-Day 15】深入解析 while 与 do…while 循环:满足条件的重复执行
16-【JavaScript-Day 16】函数探秘:代码复用的基石——声明、表达式与调用详解
17-【JavaScript-Day 17】函数的核心出口:深入解析 return 语句的奥秘
18-【JavaScript-Day 18】揭秘变量的“隐形边界”:深入理解全局与函数作用域
19-【JavaScript-Day 19】深入理解 JavaScript 作用域:块级、词法及 Hoisting 机制
20-【JavaScript-Day 20】揭秘函数的“记忆”:深入浅出理解闭包(Closure)
21-【JavaScript-Day 21】闭包实战:从模块化到内存管理,高级技巧全解析
22-【JavaScript-Day 22】告别 function 关键字?ES6 箭头函数 (=>) 深度解析
23-【JavaScript-Day 23】告别繁琐的参数处理:玩转 ES6 默认参数与剩余参数
24-【JavaScript-Day 24】从零到一,精通 JavaScript 对象:创建、访问与操作
25-【JavaScript-Day 25】深入探索:使用 for...in 循环遍历 JavaScript 对象属性
26-【JavaScript-Day 26】零基础掌握JavaScript数组:轻松理解创建、索引、长度和多维结构
27-【JavaScript-Day 27】玩转数组:push, pop, slice, splice 等方法详解与实战
28-【JavaScript-Day 28】告别繁琐循环:forEach, map, filter 数组遍历三剑客详解
29-【JavaScript-Day 29】数组迭代进阶:掌握 reduce、find、some 等高阶遍历方法
30-【JavaScript-Day 30】ES6新特性:Set与Map,让你的数据管理更高效!
31-【JavaScript-Day 31】对象的“蓝图”详解:构造函数、new 与 instanceof 完全指南
32-【JavaScript-Day 32】深入理解 prototype、__proto__ 与原型链的奥秘
33-【JavaScript-Day 33】深入浅出 ES6 Class:从入门到精通面向对象新姿势


文章目录


前言

在 JavaScript 的世界里,面向对象编程(OOP)一直是一个核心概念。在 ES6(ECMAScript 2015)出现之前,我们主要依赖构造函数和原型链来实现对象的创建和继承,这种方式虽然强大,但语法上对于习惯了类(Class)语法的开发者来说略显繁琐和不直观。ES6 class 关键字的引入,为 JavaScript 提供了一种更简洁、更清晰、更易于理解的面向对象编程方式。它本质上是基于原型继承的语法糖,但极大地提升了代码的可读性和易维护性。本文将带你深入探索 ES6 Class 的方方面面,从基础定义到高级特性,助你掌握现代 JavaScript 面向对象编程的利器。


一、ES6 Class 基础

在正式学习 ES6 Class 之前,我们先来理解一下它的基本概念和作用。

1.1 什么是 ES6 Class?

简单来说,ES6 class 提供了一种创建对象的蓝图。它封装了数据(属性)和操作数据的方法,使得我们可以更方便地创建和管理具有相似结构和行为的对象。

1.1.1 告别构造函数与原型模式的繁琐

回想一下 ES5 中创建自定义类型的方式:

// ES5 构造函数
function PersonES5(name, age) {
  this.name = name;
  this.age = age;
}

PersonES5.prototype.sayHello = function() {
  console.log("Hello, my name is " + this.name + " and I am " + this.age + " years old.");
};

var person1ES5 = new PersonES5("老王", 30);
person1ES5.sayHello();

虽然功能完善,但 prototype 的写法对于初学者或者有其他面向对象语言背景的开发者来说,可能不够直观。ES6 class 的出现正是为了简化这一过程。

1.1.2 类:对象的蓝图

class 就像一个模板或者设计图,它描述了所创建的对象共同的属性和方法。通过这个模板,我们可以创建出具体的对象实例。

1.2 定义一个简单的类

使用 ES6 class 定义一个类非常直观。

1.2.1 class 关键字

我们使用 class 关键字来声明一个类。类名通常采用首字母大写的驼峰命名法(PascalCase)。

class Person {
  // 类的代码写在这里
}

1.2.2 constructor 方法:对象的初始化器

constructor 方法是一个特殊的方法,用于创建和初始化由 class 创建的对象。每个类只能有一个 constructor 方法。

(1) constructor 的作用与特点
  • 作用:当通过 new 关键字创建类的实例时,constructor 方法会自动被调用。它的主要任务是初始化实例的属性。
  • 特点:
    • 方法名固定为 constructor。
    • 如果一个类没有显式定义 constructor 方法,JavaScript 会自动为其添加一个空的 constructor 方法:constructor() {}。
    • 在 constructor 内部,this 关键字指向新创建的实例对象。
(2) new 关键字与 constructor

当我们使用 new 操作符作用于一个类时,会发生以下事情:

  1. 创建一个新的空对象。
  2. 将这个新对象的原型 (__proto__) 指向类的 prototype 对象。
  3. 将这个新对象作为 this 的上下文调用 constructor 方法,并传入参数。
  4. 如果 constructor 方法没有显式返回一个对象,则返回这个新创建的 this 对象。
class Person {
  constructor(name, age) {
    // this 指向新创建的实例
    this.name = name;
    this.age = age;
    console.log('Person constructor called!');
  }
}

1.3 创建和使用类的实例

定义好类之后,我们就可以使用 new 关键字来创建它的实例(对象)。

1.3.1 实例化对象

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

  sayHello() {
    console.log(`你好,我叫 ${this.name},今年 ${this.age} 岁。`); // 使用模板字符串更简洁
  }
}

// 创建 Person 类的实例
const person1 = new Person('张三', 28);
const person2 = new Person('李四', 32);

1.3.2 访问属性和方法

创建实例后,我们可以通过点(.)操作符来访问实例的属性和调用实例的方法。

console.log(person1.name); // 输出: 张三
person2.sayHello();      // 输出: 你好,我叫 李四,今年 32 岁。

二、深入理解 Class 的特性

除了基本的定义和实例化,ES6 Class 还提供了许多强大的特性,如实例方法、静态方法、Getter 和 Setter。

2.1 实例方法

实例方法是定义在类的原型(prototype)上的方法,只有类的实例才能调用它们。

2.1.1 定义与调用

在 class 的主体中直接定义的方法,默认就是实例方法。

class Rectangle {
  constructor(width, height) {
    this.width = width;
    this.height = height;
  }

  // 实例方法
  getArea() {
    return this.width * this.height;
  }

  // 另一个实例方法
  printDescription() {
    console.log(`这是一个宽为 ${this.width},高为 ${this.height} 的矩形。`);
  }
}

const rect = new Rectangle(10, 5);
console.log(rect.getArea()); // 输出: 50
rect.printDescription();   // 输出: 这是一个宽为 10,高为 5 的矩形。

2.1.2 实例方法中的 this

在实例方法内部,this 关键字通常指向调用该方法的实例对象。

class Counter {
  constructor() {
    this.count = 0;
  }

  increment() {
    this.count++; // this 指向 Counter 的实例
    console.log(this.count);
  }
}

const myCounter = new Counter();
myCounter.increment(); // 输出: 1, this.count 是 myCounter.count
myCounter.increment(); // 输出: 2

注意:如果实例方法被用作回调函数或者赋值给其他变量后调用,this 的指向可能会改变,这与 JavaScript 中 this 的一般规则一致。

2.2 静态方法 (static)

静态方法是直接定义在类本身上的方法,而不是在类的实例上。它们通常用于实现与类相关但不依赖于特定实例的功能,例如工具函数或工厂方法。

2.2.1 什么是静态方法?

静态方法通过 static 关键字定义。它们不能被类的实例调用,只能通过类本身来调用。

2.2.2 如何定义和调用静态方法

class MathHelper {
  static PI = 3.1415926535; // 静态属性 (ES2022+ 或需要Babel转译)

  static add(x, y) {
    return x + y;
  }

  static subtract(x, y) {
    return x - y;
  }
}

console.log(MathHelper.PI);       // 输出: 3.1415926535
console.log(MathHelper.add(5, 3)); // 输出: 8

// const helperInstance = new MathHelper();
// console.log(helperInstance.add(1, 1)); // 错误!helperInstance.add is not a function

在上面的例子中,PI 是一个静态属性,add 和 subtract 是静态方法。它们都直接通过 MathHelper 类名来访问。

2.2.3 静态方法与实例方法的区别及应用场景

  • 调用方式:静态方法通过类名调用,实例方法通过实例调用。
  • this 指向:在静态方法中,this 指向类本身,而不是实例。在实例方法中,this 指向调用该方法的实例。
  • 访问权限:静态方法不能直接访问实例的属性(因为它们不依赖于任何特定实例),但可以访问类的静态属性。实例方法可以访问实例的属性和类的静态属性。
  • 应用场景:
    • 静态方法:创建工具函数(如 Math.random())、配置信息、实例工厂等。
    • 实例方法:操作或表示特定实例状态的方法。

2.3 Getter 和 Setter

Getter 和 Setter 允许我们定义在获取或设置属性值时执行的特定操作。它们看起来像普通的属性访问,但实际上会调用相应的函数。

2.3.1 使用 get 定义 Getter

Getter 方法用于获取属性值。它使用 get 关键字定义,后面跟着一个函数名,该函数不接受参数,并应返回计算后的属性值。

class User {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
    this._age = 0; // 通常用下划线表示这是一个内部属性
  }

  // Getter
  get fullName() {
    return `${this.firstName} ${this.lastName}`;
  }

  get age() {
    return this._age;
  }
}

const user = new User('诸葛', '亮');
console.log(user.fullName); // 输出: 诸葛 亮 (像访问属性一样,但实际调用了 get fullName() 方法)

2.3.2 使用 set 定义 Setter

Setter 方法用于设置属性值。它使用 set 关键字定义,后面跟着一个函数名,该函数接受一个参数(即要设置的值)。

class User {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
    this._age = 0;
  }

  get fullName() {
    return `${this.firstName} ${this.lastName}`;
  }

  // Setter for age
  set age(value) {
    if (typeof value === 'number' && value > 0 && value < 150) {
      this._age = value;
    } else {
      console.error('无效的年龄值!');
    }
  }

  get age() {
    return this._age;
  }
}

const user = new User('司马', '懿');
user.age = 35; // 调用 set age(value) 方法
console.log(user.age); // 输出: 35 (调用 get age() 方法)

user.age = 200; // 输出: 无效的年龄值!
console.log(user.age); // 输出: 35 (因为上次设置失败,age 仍为 35)

2.3.3 Getter 和 Setter 的应用场景(数据校验、计算属性)

  • 数据校验:如上述 age 的 Setter,可以在设置属性值之前进行验证。
  • 计算属性:如 fullName Getter,它的值是根据其他属性动态计算出来的。
  • 副作用处理:当属性值改变时,可能需要执行一些额外的操作,例如更新 UI 或记录日志。

三、Class 的继承 (extends 与 super)

继承是面向对象编程的三大特性之一(封装、继承、多态),它允许我们创建一个类(子类),从另一个类(父类)继承属性和方法。ES6 class 通过 extends 和 super 关键字简化了继承的实现。

3.1 使用 extends 实现继承

extends 关键字用于创建一个类作为另一个类的子类。

3.1.1 基本语法

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} 发出声音。`);
  }
}

// Dog 类继承自 Animal 类
class Dog extends Animal {
  constructor(name, breed) {
    // ...
    this.breed = breed;
  }

  wagTail() {
    console.log(`${this.name} 摇尾巴。`);
  }
}

const myDog = new Dog('旺财', '中华田园犬');
console.log(myDog.name); // 输出: 旺财 (继承自 Animal)
myDog.speak();          // 输出: 旺财 发出声音。 (继承自 Animal)
myDog.wagTail();        // 输出: 旺财 摇尾巴。 (Dog 自己的方法)

3.1.2 子类与父类

  • 父类 (Superclass/Base Class):被继承的类,如 Animal。
  • 子类 (Subclass/Derived Class):继承父类的类,如 Dog。子类会自动拥有父类的所有非私有属性和方法。

3.2 super 关键字

super 关键字用于访问和调用一个对象的父对象上的函数,通常用在子类的 constructor 和方法中。

3.2.1 在 constructor 中调用父类构造函数 (super())

当子类继承父类时,子类的 constructor 必须调用 super() 方法,以便执行父类的构造函数,并初始化父类中定义的属性。

(1)为何以及何时调用 super()
  • 为何:子类实例也是父类实例,因此父类的初始化逻辑也需要被执行。super() 就是用来调用父类的 constructor。
  • 何时:在子类的 constructor 中,必须在使用 this 关键字之前调用 super()。这是因为 this 的初始化是由父类的构造函数完成的。如果不调用 super() 或者在 super() 之前使用 this,JavaScript 会抛出错误。
(2)super() 的注意事项
  • super() 只能在子类的 constructor 方法中使用。
  • 它必须在 this 关键字被访问之前调用。
  • 如果父类构造函数需要参数,super() 也需要传递相应的参数。
class Animal {
  constructor(name) {
    this.name = name;
    console.log('Animal constructor called');
  }
  // ...
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name); // 调用父类的 constructor,并传递 name 参数
                 // 必须在 this.breed 之前
    this.breed = breed;
    console.log('Dog constructor called');
  }
  // ...
}

const myDog = new Dog('小黑', '拉布拉多');
// 输出:
// Animal constructor called
// Dog constructor called

3.2.2 调用父类的普通方法 (super.methodName())

在子类的方法中,可以使用 super.methodName() 来调用父类中同名的方法。这在方法重写时非常有用。

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} 发出声音。`);
  }
}

class Cat extends Animal {
  constructor(name, color) {
    super(name);
    this.color = color;
  }

  speak() {
    super.speak(); // 调用父类的 speak 方法
    console.log(`${this.name} 喵喵叫,它是 ${this.color} 的。`);
  }
}

const myCat = new Cat('咪咪', '橘色');
myCat.speak();
// 输出:
// 咪咪 发出声音。
// 咪咪 喵喵叫,它是 橘色 的。

3.3 方法重写 (Overriding)

子类可以定义一个与父类同名的方法,从而覆盖(重写)父类的该方法。

3.3.1 子类覆盖父类方法

如上例中 Cat 类的 speak 方法就重写了 Animal 类的 speak 方法。当调用 myCat.speak() 时,执行的是 Cat 类中定义的版本。

3.3.2 通过 super 调用被覆盖的父类方法

即使子类重写了父类的方法,我们仍然可以通过 super.methodName() 在子类方法内部调用父类被覆盖的方法。这允许我们在扩展父类功能的同时,保留其原有行为。


四、ES6 Class 与传统构造函数的区别与联系

虽然 ES6 class 的语法看起来很像传统的面向对象语言中的类,但理解其与 JavaScript 原有原型继承机制的关系非常重要。

4.1 语法糖的本质

ES6 class 本质上是 JavaScript 现有原型继承机制的语法糖 (Syntactic Sugar)。这意味着它并没有引入新的对象继承模型,而是提供了一种更简洁、更符合传统面向对象思维的语法来操作原型。

4.1.1 底层仍然是原型继承

我们定义的类、类的方法,最终都会转换成基于原型的实现。

class MyClass {
  constructor() {}
  myMethod() {}
}

// 上述代码在概念上(非完全等价)类似于:
function MyClassES5() {}
MyClassES5.prototype.myMethod = function() {};

console.log(typeof MyClass); // 输出 "function"
console.log(MyClass === MyClass.prototype.constructor); // true

这表明 MyClass 本身是一个函数,它的 prototype 属性的 constructor 指回 MyClass 自身。

4.1.2 typeof MyClass 的结果

如上所示,对一个类使用 typeof 操作符,结果是 "function"。这进一步证明了类在 JavaScript 底层仍然是基于函数的。

4.2 Class 的一些不同之处

尽管是语法糖,但 class 语法与传统的构造函数写法还是有一些关键区别:

4.2.1 类声明不会被提升

与函数声明不同,类声明不会被提升 (Hoisting)。这意味着你必须先声明类,然后才能使用它,否则会引发 ReferenceError。

// new MyGadget(); // ReferenceError: Cannot access 'MyGadget' before initialization

class MyGadget {
  constructor(name) {
    this.name = name;
  }
}
const gadget = new MyGadget('Phone'); // 正确

4.2.2 类内部默认启用严格模式 ('use strict')

类声明和类表达式的主体都默认在严格模式下执行。这意味着你不需要在类定义的顶部显式写 'use strict';。

4.2.3 类的所有方法都是不可枚举的(除了 constructor)

在 ES6 class 中定义的方法(包括实例方法、静态方法、getter 和 setter),默认情况下其 enumerable 属性为 false。这与 ES5 中通过 prototype 添加的方法(默认为可枚举)不同。

class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    toString() { /* ... */ }
}

console.log(Object.keys(Point.prototype)); // 输出: []
console.log(Object.getOwnPropertyNames(Point.prototype)); // 输出: ["constructor", "toString"]

function PointES5(x, y) {
    this.x = x;
    this.y = y;
}
PointES5.prototype.toString = function() { /* ... */ };
console.log(Object.keys(PointES5.prototype)); // 输出: ["toString"]

可以看到,Point.prototype.toString 方法没有出现在 Object.keys 的结果中,因为它不可枚举。


五、实际应用场景与最佳实践

理解了 ES6 Class 的核心概念后,我们来看看它在实际开发中的应用和一些需要注意的地方。

5.1 什么时候使用 Class?

5.1.1 构建具有明确结构和行为的对象

当你需要创建一组具有相似属性(状态)和方法(行为)的对象时,使用 class 是一个非常好的选择。例如:

  • UI 组件(按钮、表单、对话框等)
  • 数据模型(用户、产品、订单等)
  • 游戏中的角色、物品等
  • 各种工具类或服务类

5.1.2 在框架和库中的应用 (如 React 组件)

许多现代 JavaScript 框架和库广泛使用 class 语法。例如,在 React 中,类组件就是通过继承 React.Component 来创建的:

// 这是一个非常简化的 React 类组件示例
// import React from 'react'; // 实际使用时需要引入

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  handleClick = () => { // 使用箭头函数绑定 this
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.handleClick}>Increment</button>
      </div>
    );
  }
}

5.2 常见问题与排查建议

5.2.1 this 指向问题 (尤其在事件处理或回调中)

在类的方法中,this 的指向是一个常见的问题来源,特别是在将方法作为事件处理器或回调函数传递时。

class Button {
  constructor(label) {
    this.label = label;
    // this.onClick = this.onClick.bind(this); // 解决方案1: 在构造函数中绑定 this
  }

  onClick() {
    console.log(`${this.label} button clicked! this is:`, this);
  }

  // 解决方案2: 使用箭头函数定义方法 (推荐)
  // onClick = () => {
  //   console.log(`${this.label} button clicked! this is:`, this);
  // }

  simulateClick() {
    // 模拟将 onClick 方法传递给事件监听器
    const triggerEvent = this.onClick;
    triggerEvent(); // 此时 triggerEvent 内部的 this 将是 undefined (严格模式) 或全局对象 (非严格模式)
  }
}

const myButton = new Button('Submit');
myButton.simulateClick(); // 输出: Submit button clicked! this is: undefined (在严格模式下)

// 如果使用解决方案1或2,则 this 会正确指向 Button 实例

解决方案:

  1. 在构造函数中使用 bind:this.methodName = this.methodName.bind(this);
  2. 使用箭头函数定义方法:箭头函数会捕获其定义时所在上下文的 this 值。这是目前比较推荐的做法。
  3. 在调用时使用箭头函数包裹:例如在事件监听中 element.addEventListener('click', () => this.methodName());

5.2.2 super() 必须在 this 之前调用

在子类的 constructor 中,如果你需要访问 this 或者从构造函数返回任何东西,都必须先调用 super()。

class Parent {
  constructor() {
    this.parentProp = "Hello from Parent";
  }
}

class Child extends Parent {
  constructor() {
    // this.childProp = "Hello from Child"; // 错误!Cannot access 'this' before 'super()' call
    super();
    this.childProp = "Hello from Child"; // 正确
    console.log(this.parentProp);
  }
}
new Child();

5.2.3 忘记使用 new 关键字

类必须通过 new 关键字来实例化。如果直接像调用普通函数那样调用类构造函数,会抛出 TypeError。

class MyData {
  constructor() {
    this.value = 42;
  }
}

// const data = MyData(); // TypeError: Class constructor MyData cannot be invoked without 'new'
const data = new MyData(); // 正确
console.log(data.value);

六、总结

ES6 class 为 JavaScript 的面向对象编程带来了革命性的便利。通过本文的学习,我们掌握了:

  • 基本概念:class 关键字用于定义类,constructor 方法用于初始化实例。
  • 核心特性:
    • 实例方法:定义在类原型上,由实例调用,this 指向实例。
    • 静态方法 (static):定义在类本身上,由类名调用,this 指向类。常用于工具函数。
    • Getter (get) 和 Setter (set):允许在读写属性时执行自定义逻辑,实现计算属性或数据校验。
  • 继承机制:
    • extends:用于实现类之间的继承关系。
    • super():在子类构造函数中调用父类构造函数,必须在 this 之前。
    • super.methodName():在子类中调用父类的同名方法。
  • 与传统方式的联系与区别:
    • ES6 class 是原型继承的语法糖,底层机制不变。
    • 类声明不提升,内部默认严格模式,类方法不可枚举。
  • 实际应用与常见问题:
    • 适用于构建结构化对象和在现代框架中使用。
    • 需注意 this 指向问题,并正确使用 super() 和 new。

掌握 ES6 class 是现代 JavaScript 开发者的必备技能。它不仅让代码更易读、更易维护,也让我们能够更好地运用面向对象的思想来构建复杂应用。希望本文能为你打下坚实的基础,在未来的 JavaScript 编程之路上更加得心应手!


JavaWeb实战:图书管理系统全链路开发与架构设计详解 在JavaWeb开发领域,掌握从基础技术到完整项目落地的能力至关重要。其核心在于理解如何将Servlet、JSP、JDBC等基础组件,通过经典的三层架构(表现层、业务逻辑层、数据访问层)进行有效组织,从而构建出可维护、可扩展的应用程序。这种架构模式的价值在于清晰分离关注点,是构建复杂业务系统的基石。在实际应用场景中,例如开发一个图书管理系统,开发者需要综合运用数据库设计、事务控制、连接池管理以及前端交互等技术。本文将深入探讨如何利用**MVC模式**与**连接池**(如HikariCP)等关键技术,实现用户 阅读详情

相关推荐

DigitalOcean Functions日志外置实战:Papertrail集成与Java避坑指南

云函数日志管理是可观测性的基础能力,其核心在于解决短生命周期、分布式执行与高并发场景下的日志丢失、检索低效和告警缺失问题。基于Syslog协议的外部日志系统,通过标准化采集、结构化索引与实时告警,将原始日志升级为可查、可溯、可预警的运维数据源。在DigitalOcean Functions环境中,Papertrail凭借零配置集成、UDP轻量推送和原生元数据解析能力,成为兼顾开箱即用性与生产可靠性的首选方案;而Java应用需绕过JDK精简版导致的java.util.logging反射限制,采用SLF4J+L

weixin_30889885的博客 428

ES5 和 ES6新增

目录 1.ES6 - 定义变量 2.ES6 - 模板字符串 3.ES6 - 箭头函数 4.ES6 - 函数参数默认值 5.ES6 - 解构赋值 面试案例 : 6.ES6 - 扩展运算符 7.ES6 - 对象简写语法 8.ES6 - 模块化开发 9.ES6 - 类语法 10. 图例 :

Mr.小灰狼_随笔 694

【信息科学与工程学】【通信工程】计算机科学与自动化——第二百三十三篇 云NaaS服务 系列二01

国家骨干网(含核心网、IP骨干网、OTN骨干网)/城域网【含3G/4G/5G接入网/PON接入网/OTN接入网+OTN/IP城域网+其他】/中心云(多/双/单Region+多AZ/双AZ/单AZ)+边缘DC+边缘计算节点+端计算节点包括IoT传感器、移动终端、CPE设备。通过5G NR(n78频段,带宽100MHz,子载波间隔30kHz)或GPON(下行2.5Gbps,上行1.25Gbps)接入。本地缓存小于10GB,主要用于临时数据暂存。位于地市级机房,部署MEC服务器(Intel Xeon Platin

weixin_49199313的博客 1315

Python开发者进阶前端:JavaScript核心与框架实战

JavaScript作为现代Web开发的核心语言,其异步编程模型和原型继承机制构成了前端开发的基石。理解事件循环、Promise等核心概念,能帮助开发者构建高性能的交互式应用。随着Vue、React等框架的普及,前端工程化已成为提升开发效率的关键,其中模块化打包工具如Webpack/Vite解决了代码组织问题。对于Python开发者而言,掌握这些前端技术不仅能实现全栈开发,更能优化API设计,提升前后端协作效率。本教程特别针对Python背景的学习者,通过对比Python与JavaScript的编程范式差异

weixin_30567225的博客 412

原生JavaScript构建图书馆在线书城:从HTML5/CSS3到SPA核心功能实战

在Web开发领域,理解原生HTML、CSS和JavaScript(前端三件套)是掌握现代前端框架的基石。其核心原理在于通过DOM操作、事件驱动和异步编程直接控制浏览器行为,实现高效、轻量的页面交互。这种技术方案的价值在于能构建出无框架运行时开销、兼容性更广且性能优异的单页应用(SPA),尤其适合对性能有极致要求、需要快速原型验证或框架受限的场景。具体到工程实践,例如构建一个具备商品展示、购物车和状态管理的在线书城,开发者需要综合运用Flexbox/Grid进行响应式布局,通过事件委托优化交互性能,并利用lo

weixin_30368981的博客 196

Java+HTML5全栈网站性能优化与绿色开发实践

Web全栈开发中,'绿色环保'已从概念演变为可量化的技术指标,涵盖首屏加载速度、Lighthouse评分、资源体积压缩等核心维度。其底层原理依赖于现代Java生态(Spring Boot 3.x + JDK 17 ZGC)的内存管理与安全治理能力,以及HTML5语义化标签、ARIA属性与响应式构建工具链(Bootstrap 5.3 Sass)的深度协同。该技术路径不仅提升用户体验与无障碍访问水平,更支撑碳足迹计算、生态地图可视化等环保业务场景的高精度落地。本文聚焦Java+HTML5组合在真实项目中的工程化

weixin_33912246的博客 474

CGB2day7

什么是Vue Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。 渐进式: 构建项目可以由简单到复杂 Vue优点 1).体积小 压缩后的文件只有33k 2).运行效率更高 采用虚拟机DOM,一种可以预先通过javaScript

m0_52298926的博客 118

DOA估计算法从原理到仿真——CBF、Capon、MUSIC算法

从原理出发理解并实现经典的DOA估计算法

m0_55845774的博客 1万+

TypeScript 学习笔记(Day3)

因为 TS 是结构化类型系统,只检查 Point 和 Point2D 的结构是否相同(相同,都具有 x 和 y 两个属性,属性类型也相同)。类型变量 Key 受 Type 约束,可以理解为:Key 只能是 Type 所有键中的任意一个,或者说只能访问对象中存在的属性。比如,将类型修改为 Type[](Type 类型的数组),因为只要是数组就一定存在 length 属性,因此就可以访问了。注意:属性 age 后面的类型注解(比如,此处的 number)如果不加,则 age 的类型为 18 (字面量类型)。

🧑‍🎓 博士研究生在读 🔬 分享生物信息学、神经生物学方面的知识 💬 合作请在后台留言 1100

差异分析三巨头 —— DESeq2、edgeR 和 limma 包 | 附完整代码 + 注释

前面我们介绍了[看完还不会来揍/找我 | TCGA 与 GTEx 数据库联合分析 | 附完整代码 + 注释](https://mp.weixin.qq.com/s/VUDl2PUtMVz4kO0CBPSTIg),很多粉丝朋友私信想要我分享一下如何利用得到的混合矩阵进行差异表达分析,那今天我们就一鼓作气!把最最最经典的三个差异分析工具 —— DESeq2, edgeR 和 limma 包,以及如何通过得到的差异基因绘制火山图、热图等等,给大家进行一个详细的介绍!

weixin_43843918的博客 1万+

保姆级教程:用NVIDIA Jetson AGX Xavier和GMSL2相机搭建8路车载视觉原型系统

本文提供了一份详细的教程,指导如何使用NVIDIA Jetson AGX Xavier开发板和GMSL2相机搭建8路车载视觉原型系统。内容涵盖硬件选型、系统架构设计、软件环境配置、多路视频采集实现以及性能优化与问题排查,帮助开发者快速掌握GMSL2技术在车载视觉系统中的应用。

weixin_42525005的博客 288

JavaScript 中级(三)

ES6 一. 模板字符串 (1). 问题:旧js中,拼接字符串,只能用+,但是+极容易和算术计算的+冲突! (2). 解决:只要动态拼接字符串,都可用模板字符串 (3). 什么是:支持动态拼接内容,支持换行的特殊的字符串。 (4). 如何:3句话: a. 整个字符串要用一对儿反引号``(键盘左上角esc键正下方) b.在``内支持换行,支持单引号,支持双引号 c....

qq_46027468的博客 247

k3s快速搭建k8s集群环境

k8s 搭建方式有多种,如果你是内网环境,希望占用服务器资源少或是边缘系统(如ARM系统),强烈推荐您使用 k3s进行安装 目录k3s 简介k3s架构中文官方文档三台服务器快速搭建k8s集群安装说明步骤1 修改主机名,保持唯一步骤2 安装docker步骤3 安装master节点步骤4 安装worker节点加入集群步骤5 查看集群步骤6 部署 k8s仪表盘 dashboard k3s 简介 轻量级 Kubernetes。安装简单,内存只有一半,所有的二进制都不到 100MB K3s 是一个完全符合 Ku.

gzt19881123的专栏 2438

TypeScript学习笔记(一)

TS学习笔记 文章目录TS学习笔记一. 环境搭建二. 基本类型1. 类型声明2. 基本类型三. 编译选项四. Webpack + TS(一)基础配置1. 配置依赖2. webpack.config.js3. tsconfig.json4. 构建指令配置(二)清除旧的打包产物五. class0. 基础使用1. 构造函数&this2. 继承3. 抽象4. 接口5. 属性封装6. 泛型 一. 环境搭建 安装Node.js npm i -g typescript 创建ts文件test.ts,编译:tsc

HYY的博客 5984

机械臂电机力矩计算

设计 6 轴机械臂时,电机力矩计算是确保机械臂负载能力、运动精度和安全性的核心环节,需围绕和两大核心展开,结合机械臂结构参数(如关节长度、质量分布)、负载需求(如末端负载重量、惯性)和运动参数(如加速度、角速度)逐步推导。

ZPC8210的博客 1844

ORB-SLAM3代码详解 - 第 18 篇 · Atlas 多地图系统、地图保存与全系统调优

会跑:理解三线程架构与数据流(01/08/10/13),能编译、配参、复现各数据集结果。会改:掌握相机模型抽象(05)、g2o 顶点与边(14)、预积分(15),能接入新传感器、新相机、新约束。会调:理解每个参数背后的物理含义与耦合(03/15/16/18),能针对自己的平台与场景做精度/实时/鲁棒的权衡。ORB-SLAM3 的价值不止于一个可用的 SLAM 系统,更在于它是一套工程化的几何状态估计模板:清晰的模块边界、可替换的相机/传感器抽象、紧耦合优化的标准范式。

PX4-SLAM-PNC 169

自用书签管理页面

通过页面对浏览器的页签进行统一管理

chickenDishss的博客 558

核心组件大换血:Backbone与Neck魔改篇:YOLO26魔改Neck:引入BiFPN(双向特征金字塔),多尺度融合能力飙升

本文深入解析了YOLO26目标检测模型中Neck部分的优化策略,重点探讨了BiFPN(双向特征金字塔网络)的原理与实现。文章首先指出YOLO26原生FPN+PAN结构在多尺度特征融合上的局限性,特别是在小目标检测和边缘部署场景中的性能瓶颈。随后系统梳理了Neck网络的演进历程,从FPN、PANet到BiFPN和ASFF,详细对比了各架构的特点。核心部分展示了如何将BiFPN引入YOLO26,包括可学习权重融合、双向跨尺度连接等关键技术实现,并提供了PyTorch代码示例。本文为需要提升小目标检测性能或优化边

ZDQ58818的博客 239

知识库效用评估与优化实战:四层漏斗模型与七种手术刀

知识库不是静态文档仓库,而是支撑业务决策的动态认知系统。其核心价值在于能否被真实用户在关键任务节点快速找到、理解并用于解决问题。本文基于17个企业级落地项目经验,提炼出以‘人的行为路径-任务完成节点-业务结果变化’为锚点的效用评估逻辑,构建可采集、可归因、可行动的四层漏斗模型(曝光→触达→应用→反哺),并结合搜索日志分析、结构化内容治理、智能推送策略等关键技术手段,实现从模糊感知到精准优化的闭环。特别聚焦知识条目更新频率、搜索失败日志挖掘等高频痛点,提供开箱即用的阈值标准、脚本片段与避坑清单。

weixin_30595035的博客 357

audio语音相关的基础知识-VAD,ASR,AEC,AGC,BF等

有音素之间的间隙,也有静音和语音本身的间隙,为了对数据从时间上进行相对的校准,语音端点检测技术就应运而生了,因此端点检测技术可以决定这种校准的相对精度,使得同一内容的特征更趋于相同,当然,一般情况下是不可能完全相同的。盲源分离的目的是求得源信号的最佳估计,盲辨识的目的是求得传输通道的。这个技术的主要任务是从带有噪声的语音中准确的定位出语音的开始和结束点,因为语音中含有很长的静音,也就是把静音和实际语音分离开来,因为是语音数据的原始处理,所以VAD是语音信号处理过程的关键技术之一。呈现出与会者清晰的声音。

u013250861的博客 1029

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

PHP面试题集之基础题

1、用PHP打印出前一天的时间格式是 2006-5-10 22:21:21 date_default_timezone_set('PRC'); //默认时区 echo "今天:",date("Y-m-d",time()); echo "今天:",date("Y-m-d",strtotime("18 june 2008")); echo "昨天:",date(...

weixin_30443895的博客 92

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

并行分布式框架:Celery、rq、funboost

Celery是一个异步任务的调度工具,也可以叫做 "分布式任务队列(Distributed Task Queue)"。

freeking101的博客 5万+

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

ML模型生产化落地:从Notebook到高稳服务的12个关键实践

机器学习模型部署(ML Deployment)不仅是将代码打包上线,更是构建可监控、可回滚、可演进的生产级服务。其核心在于解决数据漂移、推理延迟、服务熔断等现实稳定性问题,技术原理涵盖数据契约校验、Triton推理优化、分层可观测性设计与Kubernetes治理策略。通过Pydantic输入验证、GPU感知扩缩容、特征分布量化监控等工程手段,显著提升模型服务SLA与MTTR。本文聚焦真实交付中反复验证的硬核细节,覆盖从本地Dev Container开发、CI/CD四阶段门禁、灰度发布到故障根因溯源的全链路,

weixin_33724659的博客 405

正则表达式完整知识体系

正则表达式(Regex)是一种文本处理工具,通过特殊字符组合定义匹配模式,用于字符串的查找、校验、提取和替换。核心特性包括: 基础语法 元字符:.(任意字符)、\d(数字)、\w(单词字符)等 量词:+(1次以上)、*(0次以上)、{n,m}(次数范围) 分组:()捕获内容,(?:)非捕获分组 边界:^(起始)、$(结尾)、\b(单词边界) 高级功能 零宽断言:(?=)(正向先行)、(?<=)(正向后行)实现精准匹配 贪婪/非贪婪:默认贪婪匹配,?切换非贪婪 修饰符:g(全局)、i(忽略大小写)、s(

专注 Java 后端开发,分享基础语法、JVM、并发、Spring 全家桶、微服务、分布式、性能优化等实战干货。坚持输出高质量技术笔记,用简单语言讲清复杂原理,助力每一位 Java 开发者稳步进阶。 593

工作中使用到的单词(软件开发)

1.エイリアス   alias 别名 powershell使用 get-help 命令时,会得到与此命令 相关(别名)的命令 2.プロキシ proxy 代 理 3.プロモート   promote 促进,提升   开发的系统新功能上线发布,有时会使用到这个词,比如 STプロモート 还有使用リリース这个词的。比如STリリース。个人认为两者含义相同。 4.見逃し み................................................................

sun0322 6万+
上一篇: 【深度学习-Day 23】框架实战:模型训练与评估核心环节详解 (MNIST实战)
下一篇: 【Python-Day 23】Python 模块化编程实战:创建、导入及 sys.path 深度解析
吴师兄大模型
吴师兄大模型 优质创作者: 人工智能技术领域 优质创作者: 人工智能技术领域
博客等级 码龄7年 5121粉丝 574原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吴师兄大模型

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值