JavaScript: JavaScript Class 与模块

随着项目变大,你需要的不再是零散的函数和变量,而是有组织的代码结构。Class 提供了面向对象的写法,Module 提供了文件级的代码隔离——两者结合,就是现代 JS 项目的基石。


1. Class 语法

Class 是创建对象的模板,是构造函数的语法糖。

HTML
<script>
class 类名 {
  constructor(参数) {
    this.属性 = 参数;
  }
  方法() {
    // ...
  }
}
</script>

▶ 示例

JAVASCRIPT
// 本节代码示例
const message = "你好,世界!";
console.log(message);
▶ 试一试

这是最简单的示例,后续章节会详细解释。### ▶ 示例:创建 Student 类

HTML
<div id="output" style="padding: 10px; border: 1px solid #ccc;"></div>

<script>
const output = document.getElementById('output');

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

  introduce() {
    return '我叫' + this.name + ',今年' + this.age + '岁,' + this.grade + '年级。';
  }

  study(subject) {
    return this.name + '正在学习' + subject + '。';
  }
}

const s1 = new Student('小明', 12, '六');
const s2 = new Student('小红', 11, '五');

output.textContent = s1.introduce() + '\n' + s2.introduce() + '\n' + s1.study('数学');
</script>
💡 提示: class 本质上就是构造函数 + 原型的语法糖。new Student() 的效果和以前 new function Student() 一样,但写起来更清晰,读起来更直观。



2. 属性和方法

(1) 实例属性和方法

constructor 中用 this.xxx 定义的属性和方法属于实例——每个对象各有一份。

(2) 静态方法

static 关键字定义的方法属于类本身,不属于实例。通过 类名.方法名() 调用。

(3) getter 和 setter

getset 关键字定义"虚拟属性"——读写时自动执行函数。

HTML
<div id="output" style="white-space: pre; font-family: monospace; padding: 10px; border: 1px solid #ccc;"></div>
<script>
const output = document.getElementById('output');

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  get fullName() {
    return this.firstName + ' ' + this.lastName;
  }

  set fullName(value) {
    const parts = value.split(' ');
    this.firstName = parts[0];
    this.lastName = parts[1];
  }
}

const p = new Person('张', '三');
output.textContent = 'fullName: ' + p.fullName + '\n';
p.fullName = '李 四';
output.textContent += '修改后: ' + p.fullName;
</script>

▶ 示例:静态方法与 getter/setter

HTML
<div id="output" style="white-space: pre; font-family: monospace; padding: 10px; border: 1px solid #ccc;"></div>

<script>
const output = document.getElementById('output');

class Circle {
  static count = 0;

  constructor(radius) {
    this.radius = radius;
    Circle.count++;
  }

  get area() {
    return Math.PI * this.radius * this.radius;
  }

  get diameter() {
    return this.radius * 2;
  }

  set diameter(value) {
    this.radius = value / 2;
  }

  static createUnit() {
    return new Circle(1);
  }
}

const c1 = new Circle(5);
const c2 = new Circle(10);
const c3 = Circle.createUnit();

output.textContent = '半径5的圆:\n';
output.textContent += '  面积: ' + c1.area.toFixed(2) + '\n';
output.textContent += '  直径: ' + c1.diameter + '\n';
output.textContent += '  修改直径为20:\n';
c1.diameter = 20;
output.textContent += '  新半径: ' + c1.radius + '\n\n';
output.textContent += '创建的圆总数: ' + Circle.count + '\n';
output.textContent += '单位圆半径: ' + c3.radius;
</script>
▶ 试一试

3. 继承

extends 让子类继承父类的属性和方法,super 调用父类的构造函数或方法。

▶ 示例:继承演示

HTML
<div id="output" style="white-space: pre; font-family: monospace; padding: 10px; border: 1px solid #ccc;"></div>

<script>
const output = document.getElementById('output');

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

  speak() {
    return this.name + '说: ' + this.sound;
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name, '汪汪');
    this.breed = breed;
  }

  fetch(item) {
    return this.name + '捡回了' + item;
  }
}

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

  purr() {
    return this.name + '在打呼噜...';
  }
}

const dog = new Dog('旺财', '柴犬');
const cat = new Cat('咪咪', true);

output.textContent = dog.speak() + '\n';
output.textContent += dog.fetch('飞盘') + '\n';
output.textContent += cat.speak() + '\n';
output.textContent += cat.purr();
</script>
▶ 试一试
💡 提示: 子类 constructor 中必须先调用 super() 才能使用 this——因为父类还没初始化,子类的 this 就不存在。这是初学继承最容易忘的规则。



4. ES 模块

模块是 JS 的代码组织单位。一个模块就是一个文件,模块内的变量默认是"私有的",只有通过 export 导出的才能被外部使用。

(1) 命名导出 / 导入

HTML
<script>
// math.js
export const PI = 3.14;
export function add(a, b) { return a + b; }

// app.js
import { PI, add } from './math.js';
</script>

(2) 默认导出 / 导入

HTML
<script>
// logger.js
export default function log(msg) { console.log(msg); }

// app.js
import log from './logger.js';
</script>

(3) 在 HTML 中使用模块

script 标签加 type="module" 属性即可。

HTML
<script type="module">
  import { add } from './math.js';
  console.log(add(1, 2));
</script>
⚠️ 注意: 模块文件会被浏览器缓存,且受同源策略限制。本地文件直接打开 HTML 可能无法加载模块,需要通过 HTTP 服务器访问。

▶ 示例:单文件模拟模块(inline module)

实际项目中模块应该拆分成独立文件。这里为了在单个 HTML 文件中演示,使用 type="module"script 标签模拟。

HTML
<div id="output" style="padding: 10px; border: 1px solid #ccc;"></div>

<script type="module">
const output = document.getElementById('output');

const calculator = {
  add(a, b) { return a + b; },
  subtract(a, b) { return a - b; },
  multiply(a, b) { return a * b; },
  divide(a, b) { return b !== 0 ? a / b : '不能除以0'; }
};

const formatter = {
  currency(value) { return '¥' + value.toFixed(2); },
  percent(value) { return (value * 100).toFixed(1) + '%'; }
};

const r1 = calculator.add(10, 20);
const r2 = calculator.multiply(5, 4);
const r3 = calculator.divide(10, 3);

output.textContent = '10 + 20 = ' + r1 + '\n';
output.textContent += '5 × 4 = ' + r2 + '\n';
output.textContent += '10 ÷ 3 = ' + formatter.currency(r3) + '\n';
output.textContent += '0.85 → ' + formatter.percent(0.85);

// 实际项目中应该这样拆分:
// calculator.js → export { calculator }
// formatter.js → export { formatter }
// main.js → import { calculator } from './calculator.js'
//           import { formatter } from './formatter.js'
</script>
▶ 试一试

❓ 常见问题

Q Class 和构造函数有什么本质区别?
A 没有本质区别,Class 是语法糖。但 Class 有几个特点:必须用 new 调用(不能当普通函数执行)、方法不可枚举、默认严格模式。写法上 Class 更清晰,推荐使用。
Q importrequire 有什么区别?
A import 是 ES 模块语法,编译时静态分析;require 是 CommonJS 语法(Node.js),运行时动态加载。浏览器原生只支持 ES 模块。import 必须在顶层,require 可以在任何位置。
Q 默认导出和命名导出能混用吗?
A 可以。一个模块可以有一个默认导出和多个命名导出:export default App; export const utils = {};。导入时:import App, { utils } from './module.js'

📖 小节

📝 作业

  1. 基础:创建一个 Rectangle 类,有 widthheight 属性,getArea()getPerimeter() 方法。
  2. 进阶:让 Rectangle 类增加 get area() getter 和 set area(value) setter(设置面积时按比例调整宽高)。再创建 Square 子类继承 Rectangle
  3. 挑战:设计一个 EventManager 类,实现 on(event, callback)off(event, callback)emit(event, data) 三个方法,模拟简易事件系统(提示:用对象存储事件到回调数组的映射)。
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏