ES6进阶知识一

目录

一、ES6构建工具与模块化

1.1.构建工具

1.1.1.Webpack

[安装 Webpack](#安装 Webpack)

[配置 Webpack](#配置 Webpack)

[使用 Webpack](#使用 Webpack)

1.1.2.Babel

[安装 Babel](#安装 Babel)

[配置 Babel](#配置 Babel)

1.2.ES6模块化

1.命名导出导入

导出模块

导入模块

[2. 默认导出与导入](#2. 默认导出与导入)

导出模块

导入模块

1.3.完整案例展示

[1. 项目结构](#1. 项目结构)

[2. 代码实现](#2. 代码实现)

[3. 构建与运行](#3. 构建与运行)

二、高阶异步编程模式

2.1.任务队列与微任务

[1.任务队列(Task Queue):](#1.任务队列(Task Queue):)

[2.微任务队列(Microtask Queue):](#2.微任务队列(Microtask Queue):)

3.宏任务与微任务的执行顺序

2.2.自定义事件与事件总线

1.自定义事件:

2.事件总线模式:

3.使用事件总线进行模块间通信

2.3.异步迭代与生成器的高级用法

使用异步迭代器处理网络请求流

三、迭代器(Iterator)与生成器(Generator)

3.1.迭代器(Iterator)

3.2.生成器(Generator)

3.3.迭代器与生成器的关系

3.4.总结

四、代理(Proxy)与反射(Reflect)

4.1.代理(Proxy)

4.2.反射(Reflect)

4.3.代理与反射的关系

五、元编程与装饰器

5.1.元编程

5.2.装饰器


一、ES6构建工具与模块化

1.1.构建工具

ES6的构建工具包括Gulp、Babel、Webpack等。

这些工具可以帮助开发者将ES6代码编译为ES5代码,以便在旧版浏览器上运行。

它们还支持代码监听、打包、压缩等功能,提高开发效率和代码质量。

1.1.1.Webpack

Webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。它会根据模块的依赖关系进行静态分析,然后将这些模块打包成一个或多个 bundle。

安装 Webpack

在项目根目录下运行以下命令来安装 Webpack 及其 CLI 工具:

npm install --save-dev webpack webpack-cli
配置 Webpack

在项目根目录下创建一个 webpack.config.js 文件,并添加以下内容:

javascript 复制代码
const path = require('path');

module.exports = {
  entry: './src/index.js', // 入口文件
  output: {
    filename: 'bundle.js', // 输出文件名
    path: path.resolve(__dirname, 'dist') // 输出路径
  },
  module: {
    rules: [
      {
        test: /\.js$/, // 匹配所有 .js 文件
        exclude: /node_modules/, // 排除 node_modules 目录
        use: {
          loader: 'babel-loader', // 使用 Babel 加载器
          options: {
            presets: ['@babel/preset-env'] // 配置 Babel 预设
          }
        }
      }
    ]
  }
};
使用 Webpack

package.json 文件的 scripts 部分添加一个构建脚本

javascript 复制代码
"scripts": {
  "build": "webpack --config webpack.config.js"
}

然后运行以下命令来构建项目:

javascript 复制代码
npm run build

1.1.2.Babel

Babel 是一个广泛使用的 JavaScript 编译器,可以将 ES6+ 代码转换为向后兼容的 JavaScript 代码,以便在旧版浏览器或环境中运行。

安装 Babel

在项目根目录下运行以下命令来安装 Babel 及其预设:

javascript 复制代码
npm install --save-dev @babel/core @babel/cli @babel/preset-env
配置 Babel

在项目根目录下创建一个 .babelrc 文件,并添加以下内容:

javascript 复制代码
{
  "presets": ["@babel/preset-env"]
}

或者使用 babel.config.json 文件(两者选其一):添加内容一样

javascript 复制代码
{
  "presets": ["@babel/preset-env"]
}

1.2.ES6模块化

ES6 引入了模块化编程的概念,允许我们将代码拆分成多个模块,每个模块只关注自己的功能,并通过 importexport 关键字来实现模块之间的通信。

1.命名导出导入

导出模块

src/math.js 文件中定义一些数学函数,并使用命名导出:

javascript 复制代码
// src/math.js
export function add(a, b) {
  return a + b;
}

export function subtract(a, b) {
  return a - b;
}
导入模块

src/index.js 文件中导入 math.js 模块中的函数

javascript 复制代码
// src/index.js
import { add, subtract } from './math.js';

console.log(add(2, 3)); // 输出: 5
console.log(subtract(5, 3)); // 输出: 2

2. 默认导出与导入

导出模块

src/utils.js 文件中定义一个工具函数,并使用默认导出:

javascript 复制代码
// src/utils.js
export default function logMessage(message) {
  console.log(message);
}
导入模块

src/index.js 文件中导入 utils.js 模块中的默认函数:

javascript 复制代码
// src/index.js
import logMessage from './utils.js';

logMessage('Hello, ES6 Modules!'); // 输出: Hello, ES6 Modules!

1.3.完整案例展示

1. 项目结构

javascript 复制代码
my-es6-project/
├── dist/
│   └── bundle.js (构建后生成的文件)
├── node_modules/ (安装的依赖包)
├── src/
│   ├── index.js
│   ├── math.js
│   └── utils.js
├── .babelrc (Babel 配置文件)
├── package.json (项目配置文件)
└── webpack.config.js (Webpack 配置文件)

2. 代码实现

src/math.js

javascript 复制代码
export function add(a, b) {
  return a + b;
}

export function subtract(a, b) {
  return a - b;
}

src/utils.js

javascript 复制代码
export default function logMessage(message) {
  console.log(message);
}

src/index.js

javascript 复制代码
import { add, subtract } from './math.js';
import logMessage from './utils.js';

console.log(add(2, 3)); // 输出: 5
console.log(subtract(5, 3)); // 输出: 2
logMessage('Hello, ES6 Modules!'); // 输出: Hello, ES6 Modules!

.babelrc

javascript 复制代码
{
  "presets": ["@babel/preset-env"]
}

webpack.config.js

javascript 复制代码
const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      }
    ]
  }
};

package.json

javascript 复制代码
{
  "name": "my-es6-project",
  "version": "1.0.0",
  "description": "A demo project for ES6 modules and build tools.",
  "main": "index.js",
  "scripts": {
    "build": "webpack --config webpack.config.js"
  },
  "devDependencies": {
    "@babel/core": "^7.x.x",
    "@babel/cli": "^7.x.x",
    "@babel/preset-env": "^7.x.x",
    "webpack": "^5.x.x",
    "webpack-cli": "^4.x.x"
  }
}

3. 构建与运行

在项目根目录下运行以下命令来构建项目

javascript 复制代码
npm run build

构建成功后,将在 dist 目录下生成一个 bundle.js 文件。你可以通过创建一个 HTML 文件来引用这个构建后的文件,并在浏览器中查看输出结果

二、高阶异步编程模式

2.1.任务队列与微任务

1.任务队列(Task Queue):

通常包含宏任务(MacroTask),如setTimeoutsetInterval、I/O操作等。

宏任务之间的执行间隔可能会受到浏览器渲染和其他因素的影响。

2.微任务队列(Microtask Queue):

包含微任务(MicroTask),如Promise的回调、MutationObserver等。

微任务的优先级高于宏任务,会在当前宏任务执行完毕后立即执行,直到微任务队列为空。

3.宏任务与微任务的执行顺序

javascript 复制代码
console.log('宏任务1');

setTimeout(() => {
  console.log('宏任务2');
}, 0);

Promise.resolve().then(() => {
  console.log('微任务1');
});

console.log('宏任务3');

执行结果:

宏任务1

宏任务3

微任务1

宏任务2

解释:

首先执行同步代码(宏任务1和宏任务3)。

然后执行微任务(微任务1),因为微任务的优先级高于宏任务。

最后执行宏任务队列中的setTimeout回调(宏任务2)。

2.2.自定义事件与事件总线

自定义事件和事件总线是JavaScript中用于组件或对象间通信的重要机制。

1.自定义事件

允许在对象或应用程序的不同部分之间传递消息和数据。

可以通过CustomEvent构造函数或Event构造函数(在某些情况下)来创建自定义事件。

2.事件总线模式

是一种松耦合的通信方式,允许不同的模块或组件通过事件进行通信,而无需直接相互引用。

使用事件总线可以减少模块之间的依赖,提高代码的可维护性和可扩展性。

在Vue等前端框架中,自定义事件和事件总线得到了广泛的应用。例如,在Vue中,子组件可以通过$emit触发自定义事件,父组件则可以通过v-on@语法来监听这些事件,从而实现组件间的通信。

3.使用事件总线进行模块间通信

假设我们有两个模块moduleAmoduleB,它们需要通过事件总线进行通信。

javascript 复制代码
// 事件总线类
class EventBus {
  constructor() {
    this.events = {};
  }

  subscribe(event, callback) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(callback);
  }

  publish(event, ...args) {
    if (this.events[event]) {
      this.events[event].forEach(callback => callback(...args));
    }
  }
}

// 创建事件总线实例
const eventBus = new EventBus();

// moduleA
function moduleA() {
  eventBus.subscribe('dataReceived', (data) => {
    console.log('Module A received data:', data);
  });
}

// moduleB
function moduleB() {
  setTimeout(() => {
    eventBus.publish('dataReceived', { key: 'value' });
  }, 1000);
}

// 初始化模块
moduleA();
moduleB();

执行结果:

在1秒后,控制台会输出"Module A received data: { key: 'value' }"。

解释:

moduleA通过eventBus.subscribe订阅了dataReceived事件。

moduleB在1秒后通过eventBus.publish发布了dataReceived事件,并传递了一个数据对象。

moduleA的回调函数被调用,并接收到了传递的数据

2.3.异步迭代与生成器的高级用法

异步迭代器和生成器是ES6中引入的高级异步编程特性,它们允许开发者以更直观、更优雅的方式处理异步操作。

  1. 异步迭代器

    • 允许在异步操作中逐个处理数据项,而不会阻塞程序的执行。
    • 它们通过Symbol.asyncIterator方法实现,可以被for await...of循环使用。
  2. 异步生成器函数

    • 可以暂停和恢复执行,以异步方式生成数据。
    • 它们使用async function*声明,并通过yield关键字返回数据。

异步迭代器和生成器在处理大量数据或复杂异步操作时非常有用。例如,它们可以用于读取大型文件、处理网络请求流等场景。通过异步迭代器和生成器,开发者可以更加灵活地控制异步操作的执行流程,从而提高代码的可读性和可维护性。

3.示例

使用异步迭代器处理网络请求流

假设我们需要从一个API端点获取分页数据,并逐个处理这些数据项

javascript 复制代码
async function* fetchData(url, pageSize) {
  let page = 1;
  while (true) {
    const response = await fetch(`${url}?page=${page}&pageSize=${pageSize}`);
    const data = await response.json();
    if (data.length === 0) break; // 假设返回的数据为空数组时表示没有更多数据
    for (const item of data) {
      yield item;
    }
    page++;
  }
}

(async () => {
  const dataStream = fetchData('https://api.example.com/items', 10);
  for await (const item of dataStream) {
    console.log('Received item:', item);
    // 在这里处理每个数据项,例如保存到数据库或更新UI
  }
  console.log('All data processed.');
})();

执行结果:

控制台会逐个输出从API获取的数据项,并在处理完所有数据后输出"All data processed."。

解释:

fetchData是一个异步生成器函数,它使用fetch函数从API获取分页数据。

while循环中,它不断请求下一页的数据,直到返回的数据为空数组。

对于每一页的数据,它使用for...of循环逐个yield出数据项。

在异步函数块中,我们使用for await...of循环来消费这个异步生成器,并逐个处理从API获取的数据项。

三、迭代器(Iterator)与生成器(Generator)

3.1.迭代器(Iterator)

1.定义

迭代器是一种对象,它提供了一种顺序访问集合中每个元素的方式,而不暴露集合内部的表示。

迭代器的主要方法是next(),每次调用该方法都会返回一个包含valuedone属性的对象。value表示当前元素的值,done表示是否已经遍历完所有元素。

2.实现原理

在需要进行遍历操作时,通过调用集合对象上的Symbol.iterator方法获取到该集合对象对应的默认迭代器。

在每次调用next()方法时,迭代器会执行相应的操作,并返回一个包含valuedone属性的对象。

如果donefalse,则表示还有更多的元素需要遍历,此时value属性表示当前遍历到的值。

如果donetrue,则表示已经遍历完所有元素,此时value属性为undefined

3.使用场景

迭代器提供了一种统一的遍历机制,使得我们可以使用相同的方式来访问不同类型的数据结构。

无论是数组、字符串、Set、Map还是自定义对象,只要实现了迭代器接口,就可以使用for...of循环或者手动调用next()方法来进行遍历。

4.示例:

javascript 复制代码
let arr = [1, 2, 3];
let iterator = arr[Symbol.iterator]();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }

3.2.生成器(Generator)

1.定义:

生成器是一种特殊的函数,它可以通过yield关键字来暂停函数的执行,并返回一个包含valuedone属性的对象。

生成器函数使用function*语法进行定义。

2.实现原理:

当调用生成器函数时,实际上并不会立即执行函数体内部的代码,而是返回一个迭代器对象,该迭代器对象实现了next()方法。

每次调用next()方法时,生成器会从上一次暂停的位置继续执行代码,直到遇到下一个yield关键字或者函数结束。

3.使用场景:

生成器提供了一种更灵活、更可控的方式来处理异步编程。

通过使用yield关键字,我们可以在函数执行过程中暂停和恢复,并且可以将异步操作以同步方式编写和理解。

4.示例:

javascript 复制代码
function* generatorFunc() {
    yield 'Hello';
    yield 'World';
}
let generator = generatorFunc();
console.log(generator.next()); // { value: 'Hello', done: false }
console.log(generator.next()); // { value: 'World', done: false }
console.log(generator.next()); // { value: undefined, done: true }

3.3.迭代器与生成器的关系

生成器本身就是一个迭代器,它返回的迭代器对象实现了迭代器接口,因此可以使用next()方法进行遍历。

生成器通过yield关键字实现了函数的暂停和恢复,这使得它在处理异步编程时具有更大的灵活性。

3.4.总结

迭代器为JavaScript提供了一种统一的遍历机制,使得我们可以使用相同的方式来访问不同类型的数据结构。

生成器则通过yield关键字实现了函数的暂停和恢复,为异步编程提供了更自然和易于理解的方式。

在实际开发中,迭代器与生成器经常结合使用,以实现更复杂的迭代和异步逻辑。

四、代理(Proxy)与反射(Reflect)

4.1.代理(Proxy)

  1. 定义与功能
    • 代理是ES6引入的一种元编程机制,允许开发者拦截并自定义目标对象的操作。
    • 通过使用Proxy构造函数,开发者可以创建一个代理对象,该对象将拦截对目标对象的读取、写入、函数调用等操作。
  2. 创建与使用
    • 要创建一个代理对象,需要使用Proxy构造函数,并传入两个参数:目标对象(target)和处理程序(handler)。
    • 处理程序是一个对象,其方法被称为陷阱(trap),用于指定拦截后的行为。
  3. 常用陷阱方法
    • get(target, property, receiver):拦截对目标对象属性的读取操作。
    • set(target, property, value, receiver):拦截对目标对象属性的写入操作。
    • apply(target, thisArg, argumentsList):拦截对目标对象的函数调用操作。
    • construct(target, argumentsList, newTarget):拦截对目标对象的构造函数调用操作。
    • 其他陷阱方法还包括hasdeletePropertygetOwnPropertyDescriptor等。
  4. 示例
javascript 复制代码
const target = { name: 'John', age: 30 };
const handler = {
  get(target, property) {
    console.log(`Getting property: ${property}`);
    return target[property];
  },
  set(target, property, value) {
    console.log(`Setting property: ${property} = ${value}`);
    target[property] = value;
  }
};
const proxy = new Proxy(target, handler);
console.log(proxy.name); // 输出: Getting property: name John
proxy.age = 35; // 输出: Setting property: age = 35

4.2.反射(Reflect)

  1. 定义与功能
    • 反射是ES6引入的一个内置对象,提供了一套与JavaScript内置操作直接对应的静态方法。
    • 这些方法的设计目的是为了使操作对象的行为变得更为清晰、更易于使用,并在某些情况下提供更好的错误处理机制。
  2. 常用方法
    • Reflect.get(target, propertyKey[, receiver]):获取目标对象上指定属性的值。
    • Reflect.set(target, propertyKey, value[, receiver]):设置目标对象上指定属性的值。
    • Reflect.defineProperty(target, propertyKey, attributes):在目标对象上定义一个属性。
    • Reflect.deleteProperty(target, propertyKey):尝试删除目标对象上的指定属性。
    • Reflect.apply(target, thisArgument, argumentsList):调用目标函数。
    • Reflect.construct(target, argumentsList[, newTarget]):使用给定的参数列表调用构造函数并创建一个新实例。
    • 其他方法还包括Reflect.hasReflect.ownKeysReflect.isExtensible等。
  3. 特点
    • Reflect对象的方法与Object对象上的同名方法相对应,但Reflect方法通常返回一个布尔值以表示操作是否成功,而不是静默失败。
    • Reflect方法在遇到非法操作时会抛出适当的异常,如TypeError或RangeError,而不是默默地失败。
  4. 示例
javascript 复制代码
const obj = { a: 1, b: { c: 2 } };
console.log(Reflect.get(obj, 'a')); // 输出: 1
Reflect.set(obj, 'a', 2);
console.log(obj.a); // 输出: 2

4.3.代理与反射的关系

代理和反射是协调合作的关系。代理用于拦截对象的内置操作,并注入自定义的逻辑;而反射则向外界暴露了一些底层操作的默认行为,使得开发者可以更方便地访问和修改这些行为。

在代理的陷阱方法中,通常会使用Reflect对象的方法来执行原始的、未被拦截的操作。这样,开发者就可以在自定义逻辑前后添加额外的行为,而不会破坏原始的操作。

五、元编程与装饰器

5.1.元编程

  1. Symbol

    • 引入了一种新的原始数据类型,用于创建唯一的标识符。
    • Symbols 可以被用作对象的键,且保证不会和现有的字符串键冲突。
    • Symbols 提供了一种隐藏层,使得对象可以拥有不可迭代且不能被现有反射工具获取的属性。
  2. Reflect

    • 是一个新的全局对象,提供了大量有用的内省(introspection)方法。
    • Reflect 囊括了所有JavaScript引擎内部专有的"内部方法",现在被暴露为了一个单一、方便的对象。
    • 通过Reflect对象,开发者可以更容易地访问和操作对象的底层信息。
  3. Proxy

    • 允许开发者创建对象的代理,并拦截和自定义对目标对象的操作。
    • 通过Proxy,开发者可以实现对对象行为的动态修改和扩展。

5.2.装饰器

装饰器是ES7(虽然目前在标准中尚未正式定稿,但已被广泛使用)提出来的一种语法特性,它允许你在类、类方法、类属性等声明前面添加特殊的修饰符,以此来修改他们的行为。装饰器本质上是一个高阶函数,用于对类的定义、方法、属性进行修饰。

  1. 装饰器的语法

    • 使用@符号作为前缀,后面紧跟装饰器函数的名称。
    • 装饰器函数接受特定的参数,如目标对象、属性名、属性描述符等,并返回一个新的属性描述符或进行其他操作。
  2. 装饰器的应用

    • 类装饰器:应用于整个类,可以修改类的构造函数或添加新的静态方法。
    • 方法装饰器:应用于类的方法,可以修改方法的行为或添加额外的逻辑。
    • 属性装饰器:应用于类的属性,可以修改属性的特性或添加元数据。
  3. 装饰器的用途

    • 日志记录:为类或方法自动添加日志输出,便于调试。
    • 权限控制:控制方法的访问权限,检查调用者的身份。
    • 缓存功能:对函数调用结果进行缓存,避免重复计算。
    • 性能监控:通过装饰器监控函数执行的性能数据。
    • 自动绑定 :为类方法自动绑定this,避免在不同上下文中丢失this指向。
    • 代码复用:通过装饰器复用代码,不必重复编写逻辑。
    • 提升可读性:通过分离业务逻辑与装饰器逻辑,提升代码可读性和可维护性。
  4. 装饰器的使用示例

javascript 复制代码
function log(target, name, descriptor) {
  const original = descriptor.value;
  descriptor.value = function(...args) {
    console.log(`Calling ${name} with`, args);
    return original.apply(this, args);
  };
  return descriptor;
}

class Example {
  @log
  sayHello(name) {
    return `Hello, ${name}`;
  }
}

const example = new Example();
example.sayHello('World'); // 输出: Calling sayHello with [ 'World' ] Hello, World

在这个例子中,log装饰器为sayHello方法添加了日志输出功能,每次调用sayHello时都会记录参数信息。

六、Generator 函数与class

6.1.Generator 函数

Generator 函数允许你暂停和恢复函数的执行

1. 定义 Generator 函数

Generator 函数使用 function* 语法来定义,而不是普通的 function 关键字。

javascript 复制代码
function* myGenerator() {
  yield 1;
  yield 2;
  yield 3;
}

2. 调用 Generator 函数

调用 Generator 函数不会立即执行其代码,而是返回一个迭代器对象。

javascript 复制代码
const gen = myGenerator();

3. 使用 next() 方法

通过调用迭代器对象的 next() 方法,可以逐步执行 Generator 函数中的代码。每次调用 next() 方法时,Generator 函数会从上次暂停的地方继续执行,直到遇到下一个 yield 表达式或函数结束

javascript 复制代码
console.log(gen.next().value); // 输出: 1
console.log(gen.next().value); // 输出: 2
console.log(gen.next().value); // 输出: 3
console.log(gen.next().done);  // 输出: true

每次调用 next() 方法时,该方法返回一个对象,该对象有两个属性:

valueyield 表达式的值(如果函数已结束,则为 undefined)。

done:一个布尔值,表示 Generator 函数是否已经执行完毕。

4. 示例:使用 Generator 函数处理异步操作

Generator 函数特别适合用于处理异步操作,因为它允许你暂停和恢复函数的执行,而不需要嵌套回调函数或 Promise 链。以下是一个简单的示例,展示了如何使用 Generator 函数和 Promise 处理异步操作:

javascript 复制代码
function fetchData(url) {
  return new Promise((resolve, reject) => {
    // 模拟异步数据获取
    setTimeout(() => {
      resolve(`Data from ${url}`);
    }, 1000);
  });
}

function* asyncFlow() {
  const data1 = yield fetchData('https://api.example.com/data1');
  console.log(data1);

  const data2 = yield fetchData('https://api.example.com/data2');
  console.log(data2);
}

function runGenerator(generator) {
  const it = generator();

  function handleResult(result) {
    if (result.done) {
      return;
    }

    result.value.then(
      (value) => {
        const nextResult = it.next(value);
        handleResult(nextResult);
      },
      (error) => {
        console.error(error);
      }
    );
  }

  handleResult(it.next());
}

runGenerator(asyncFlow);

在这个示例中,asyncFlow 是一个 Generator 函数,它使用 yield 来暂停执行并等待异步操作的结果。runGenerator 函数负责运行这个 Generator 函数,并在每次 yield 表达式的结果解决后继续执行。

5. 注意事项

Generator 函数返回的是一个迭代器对象,而不是普通的函数返回值。

Generator 函数中的 yield 表达式可以返回任何值,包括对象、数组、Promise 等。

Generator 函数允许你使用 yield* 表达式来委托另一个 Generator 函数或可迭代对象。

6.2.class

class 语法被引入,为JavaScript提供了更接近传统面向对象编程的语法糖。尽管JavaScript本质上仍然是基于原型的继承,但class语法使得定义和继承类变得更加直观和易于理解。

1. 定义类

使用class关键字来定义一个类。类体中包含构造器(constructor)和方法。

javascript 复制代码
class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  greet() {
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
  }
}

2. 创建类的实例

使用new关键字来创建类的实例。

javascript 复制代码
const person1 = new Person('Alice', 30);
person1.greet(); // 输出: Hello, my name is Alice and I am 30 years old.

3. 继承

使用extends关键字来实现类的继承。子类可以重写父类的方法,或者添加新的方法。

javascript 复制代码
class Employee extends Person {
  constructor(name, age, jobTitle) {
    super(name, age); // 调用父类的构造器
    this.jobTitle = jobTitle;
  }

  greet() {
    super.greet(); // 调用父类的方法
    console.log(`I am an ${this.jobTitle}.`);
  }
}

const employee1 = new Employee('Bob', 40, 'Engineer');
employee1.greet();
// 输出:
// Hello, my name is Bob and I am 40 years old.
// I am an Engineer.

4. 静态方法

使用static关键字来定义静态方法。静态方法属于类本身,而不是类的实例。

javascript 复制代码
class MathUtils {
  static add(a, b) {
    return a + b;
  }
}

console.log(MathUtils.add(2, 3)); // 输出: 5

5. Getter 和 Setter

在类中可以使用getset关键字来定义属性的getter和setter方法。

javascript 复制代码
class Rectangle {
  constructor(width, height) {
    this._width = width;
    this._height = height;
  }

  get area() {
    return this._width * this._height;
  }

  set area(value) {
    // 注意:这里通常不会直接设置面积,因为面积是由宽度和高度决定的。
    // 但为了演示,我们可以抛出一个错误。
    throw new Error('Area is a read-only property.');
  }

  // 可以设置宽度和高度
  set width(value) {
    this._width = value;
  }

  get width() {
    return this._width;
  }

  // 同理,可以设置和获取高度
  set height(value) {
    this._height = value;
  }

  get height() {
    return this._height;
  }
}

const rect = new Rectangle(10, 5);
console.log(rect.area); // 输出: 50
// rect.area = 100; // 这将抛出错误
rect.width = 20;
console.log(rect.area); // 输出: 100

注意事项

尽管ES6引入了class语法,但JavaScript的类并不完全等同于其他面向对象语言中的类。例如,JavaScript的类没有传统的"类型"或"接口"的概念,也没有访问修饰符(如privateprotected等,尽管在后续版本中引入了私有字段)。

类的继承是基于原型的,这意味着子类会继承父类的原型链上的属性和方法。

在类中,this的绑定是自动的,与在函数或方法中使用this时需要小心的情况不同。

class语法为JavaScript提供了更清晰、更易于理解的面向对象编程方式,但它仍然是基于JavaScript原型继承机制的语法糖。

亲爱的友友们~~码字不易,给孩子点点赞呗

相关推荐
AlgorithmAce1 小时前
Live2D嵌入前端页面
前端
nameofworld1 小时前
前端面试笔试(六)
前端·javascript·面试·学习方法·递归回溯
前端fighter1 小时前
js基本数据新增的Symbol到底是啥呢?
前端·javascript·面试
GISer_Jing1 小时前
从0开始分享一个React项目:React-ant-admin
前端·react.js·前端框架
川石教育2 小时前
Vue前端开发子组件向父组件传参
前端·vue.js·前端开发·vue前端开发·vue组件传参
Embrace9242 小时前
为什么 Vue2会出现数据更新视图不更新 Vue3不会出现
javascript·vue.js·ecmascript
GISer_Jing2 小时前
Vue前端进阶面试题目(二)
前端·vue.js·面试
乐闻x3 小时前
Pinia 实战教程:构建高效的 Vue 3 状态管理系统
前端·javascript·vue.js
weixin_431449683 小时前
web组态软件
前端·物联网·低代码·编辑器·组态
橘子味小白菜3 小时前
el-table的树形结构后端返回的id没有唯一键怎么办
前端·vue.js