2023-08-14
JavaScript
00
请注意,本文编写于 475 天前,最后修改于 469 天前,其中某些信息可能已经过时。

目录

变量的解构赋值
数组的解构赋值
对象的解构赋值
基本数据类型的解构赋值
圆括号问题
用途
运算符的扩展
指数运算符
?. 链判断运算符
Null 判断运算符
逻辑赋值运算符
!命令
扩展运算符
装饰器
旧版本语法
环境准备
基本使用
装饰器的执行顺序
新版本语法
API
环境准备
基本使用
addInitializer()
core-decorators.js
其他

本文介绍结构赋值、运算符的扩展、装饰器新旧版本的基本用法。

变量的解构赋值

数组的解构赋值

  • 模式匹配
  • 只要对象具有Iterator接口就可以结构 类遍历器对象也可以
  • 可以设置默认值
    • 如果默认值是表达式 与函数参数特性一样, 惰性求值
    • 只有undefined才触发默认值

对象的解构赋值

  • 先找同名属性,然后再赋值给对应的变量
  • 对象的解构赋值可以取到继承的属性
  • 支持设置默认值、支持别名

注意点:

  • 如果要将一个已经声明的变量用于解构赋值,必须非常小心
  • 允许等号左边的模式之中无变量
  • 数组是特殊的对象

注: 函数参数也是对象, 因此可以使用对象的方式进行解构

基本数据类型的解构赋值

boolean string number BigInt Symbol只要有包装对象(排除了 null undefined)就可以进行解构,获取原型链上的方法或属性。

圆括号问题

  1. 不使用圆括号的场景 2. 变量声明语句
    1. 函数参数
    2. 赋值语句的模式
  2. 可以使用圆括号的场景
    • 赋值语句的非模式部分

用途

  1. 交换变量的值
  2. 从函数返回多个值
  3. 函数参数的定义
  4. 提取 JSON 数据
  5. 函数参数的默认值
  6. 遍历 Map 结构
  7. 输入模块的指定方法

运算符的扩展

指数运算符

  • **
  • **=

?. 链判断运算符

  • ?.
  • ?.[]
  • ?.()

注意事项

  1. 它是短路机制
  2. 一般来说,使用?.运算符的场合,不应该使用圆括号。
  3. 报错场合
  4. 右侧不得为十进制数值

Null 判断运算符

  • ?? 他的行为类似 || 但只有nullundefined,才会返回右侧的值
  • 配合?.使用
  • 与其他逻辑运算在一起 必须表明优先级

逻辑赋值运算符

  • ||=
  • &&=
  • ??=

!命令

bash
#!/usr/bin/env node

扩展运算符

用途

  • 解构赋值
  • 函数rest参数
  • 遍历 可遍历对象(或类遍历器对象)
  • copy对象属性
    • 会自动执行取值函数

装饰器

其实装饰器从ES6诞生开始就有,一直到现在才进入第三阶段(候选),不过也就差最后一步了,由于很多项目都用到装饰器,这一块不得不认真学。

旧版本语法

环境准备

  1. package.json
json
{ // ... "dependencies": { "@babel/cli": "^7.22.10", "@babel/core": "^7.22.10", "@babel/plugin-proposal-decorators": "^7.22.10", "@babel/preset-env": "^7.22.10", "core-js": "3" }, "browserslist": [ "chrome >= 100" ] }
  1. babel.config.js
js
module.exports = { presets: [ [ "@babel/preset-env", { useBuiltIns: "usage", corejs: { version: 3, proposals: true, // 默认值是false, 对stage阶段的全局对象上的方法打补丁 }, }, ], ], plugins: [ [ "@babel/plugin-proposal-decorators", { legacy: true, }, ], ], };
  1. build.sh
sh
#!/bin/bash run() { local name=$1 eval "npx babel $1 --out-file dist/$1" eval "node dist/$1" } run $1

执行命令 sh build.sh 1.js

基本使用

js
@decorator class A {} // 等价于 A = decorator(A) || A class MyReactComponent extends React.Component {} connect(mapStateToProps, mapDispatchToMap)(MyReactComponent); // 使用装饰器优化React-Reduce代码 @connect(mapStateToProps, mapDispatchToMap);
  • 类装饰器就一个参数 target 值为被修饰的类
  • 方法(原型方法)装饰器有三个参数
    • 参数1是类的原型对象
    • 参数2是所要装饰的属性名
    • 参数3是该属性的描述对象 descriptor
  • 没有属性(实例属性)装饰器 因为装饰器是在编译时执行的
  • 静态(方法or属性)装饰器
    • 参数1是类
    • 参数2与3同方法装饰器

注意: 装饰器不能修饰函数, 因为函数声明语句存在提升。应采用高阶函数方式替代

装饰器的执行顺序

  1. 先执行方法装饰器(后定义的先执行性),再执行类装饰器
  2. 从下到上执行
js
@decorator1 @decorator2 class A { @decoratorMethod1 @decoratorMethod2 say(text) { console.log('A.prototype.say()', text); } }
  1. decoratorMethod2
  2. decoratorMethod1
  3. decorator2
  4. decorator1

装饰器对类的行为的改变,是代码编译时发生的,而不是在运行时。 这意味着什么呢?

  • 调用方法装饰不管是静态方法还是原型方法,this指向undefined
  • 存值器、取值器、access装饰器 也是如此

新版本语法

API

image.png

kind的值是啥含义

kind枚举值含义对value的影响
class类装饰器对应的类
method方法装饰器
包括原型方法和静态方法
对应的方法`
field属性装饰器valueundefined
getter取值器装饰器value为 取值器
setter存值器装饰器value为 存值器
accesser它是存取器的简写value为 一个对象,内含取值器和存值器
  • 类装饰器API
js
type ClassDecorator = (value: Function, context: { kind: "class"; name: string | undefined; // 如果被装饰的类是一个匿名类时其值为undefined addInitializer(initializer: () => void): void; }) => Function | void;
  • 方法装饰器API
ts
type ClassMethodDecorator = (value: Function, context: { kind: "method"; name: string | symbol; access: { get(): unknown }; static: boolean; private: boolean; addInitializer(initializer: () => void): void; }) => Function | void;
  • 存取器装饰器 accessor装饰器 与方法装饰器API类似
  • 属性装饰器 与方法装饰器项目没有初始化逻辑addInitializer()

环境准备

  1. package.json
json
{ "dependencies": { "@babel/cli": "^7.22.10", "@babel/core": "^7.22.10", "@babel/plugin-proposal-decorators": "^7.22.10", "@babel/preset-env": "^7.22.10" }, "browserslist": [ "chrome >= 100" ] }
  1. babel.config.js 与前面 babel的配置基本相同,只要改一下@babel/plugin-proposal-decorators的配置即可。

image.png

  1. build.sh同上, 执行命令 sh build.sh 1.js

基本使用

示例

js
function logged(value, { kind, name }) { if (kind === "class") { return class extends value { constructor(...args) { super(...args); console.log(`constructing an instance of ${name} with arguments ${args.join(", ")}`); } } } } @logged class C {} new C(1); // constructing an instance of C with arguments 1

类装饰器实际上执行的是下面的语法。

js
class C {} C = logged(C) ?? C function trace(value, context) { return function(...args) { console.log('trace hello ', this.name); value.call(this, ...args); } } class Person { constructor(name) { this.name = name; } @trace hello() { console.log(`Hi ${this.name}!`) } } const robin = new Person('Robin'); console.log(robin.hello()); // trace hello Robin // Hi Robin!

方法装饰器

  • 必须返回一个函数 或undefined, 否则报错
  • 如果返回函数取代原方法,如果返回undefined则使用原方法

addInitializer()

示例1 webComponents

js
function customElement(name) { return function(value, context) { context.addInitializer(() => { customElements.define(name, this); }) } } @customElement('my-element'); class MyElement extends HTMLElement { }

示例2: bound

js
class C { message = 'hello'; @bound m() { console.log(this.message) } } function bound(value, {addInitializer, name}) { addInitializer(function() { this[name] = this[name].bind(this) }) } var c = new C(); const {m} = c; console.log(m());

core-decorators.js

目前core-decorators.js采用的是老的装饰器语法实现的

  • @autobind
  • @readonly
  • @override
  • @deprecate

其他

以下内容挪到了其他文章中讲解

本文作者:郭敬文

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!