ES6不常见的新特性

ES6不常见的新特性

DevUI是面向企业中后台产品的开源前端解决方案,其设计价值观基于高效、开放、可信、乐趣四种自然与人文相结合的理念,旨在为设计师、前端开发者提供标准的设计体系,并满足各类落地场景,是一款企业级开箱即用的产品。

感谢我们的DevUI社区贡献者TongxinXie提供的优质好文!

ES6背景介绍

什么是ECMAScript

ECMA International :ECMA国际(前身为欧洲计算机制造商协会),是一个专门为技术制定标准的组织。 ECMAScript是一种由ECMA在标准ECMA-262中定义的脚本语言规范。 ECMAScript是 JavaScript的标准规范,JavaScript是 ECMAScript的具体实现。

ECMAScript就是 JavaScript中的语法规范:

  • 语法 -- 解析规则,关键字,语句,声明,操作等;
  • 类型 -- Boolean, Number, String, Undefined, Null, Symbol, BigInt等;
  • 原型和继承;
  • 内置对象和函数的标准库--JSON,Math,数组方法,对象内省的方法等等;

为什么不能叫JavaScript呢?

  • 商标版权问题:Java 是 Sun 公司的商标。根据授权,只有Netscape 可以合法使用JavaScript这个名字。且JavaScript已经被 Netscape公司注册为商标。
  • 保持标准的开放和中立性:ECMA想告诉大家,这门语言的制定者是ECMA,而不是 Netscape公司,从而有利于保证这门语言的开放性和中立性。

ECMA发展历程


ES6是什么

ES5 其实泛指 ECMAScript 6.0 大版本以前的 JavaScript 标准的统称,概念范围基本等同于ECMAScript5.1版。 ES6 其实泛指ECMAScript 6.0 版以后的 JavaScript 的下一代标准的统称,概念范围涵盖了 ES2015~ES2023。

ES6特性总览


ES6不常见特性

1. 从后往前查找数组(ES2023)

findLast() 和 findLastIndex():用法跟 find() 和 findIndex() 完全一致,区别是从数组的最后一个元素开始向前查找。

ts 复制代码
const list= [1,2,3,4,5];

// 从前往后遍历
const resultFoward = list.find(element => element > 2); // 3
const indexFoward = list.findIndex(element => element > 2); // 2

//从后往前遍历
const resultBackward = list.findLast(element => element > 2); // 5
const indexBackward = list.findLastIndex(element => element > 2); // 4

2. 不改变原数组的新方法(ES2023)

数组新增无副作用方法:toReversed(), toSorted(), toSpliced()

ts 复制代码
let original = [1, 3, 2, 4];

// 不改变原数组,返回处理后的新数组
const toReversed = original.toReversed();
console.log(original); // [ 1, 3, 2, 4 ]
console.log(toReversed); // [ 4, 2, 3, 1 ]

const toSorted = original.toSorted();
console.log(original); // [ 1, 3, 2, 4 ]
console.log(toSorted); // [ 1, 2, 3, 4 ]

const toSpliced = original.toSpliced(0,2,'a');
console.log(original); // [ 1, 3, 2, 4 ]
console.log(toSpliced); // [ 'a', 2, 4 ]

修改指定索引值的复制方法版本:with(index,value)

ts 复制代码
//在数组元素上使用
const origin = [1, 3, 2, 4];
const withed = original.with(1,'a');
console.log(original); // [ 1, 3, 2, 4 ]
console.log(withed); // [ 1, 'a', 2, 4 ]

//在非数组元素上使用, with() 方法会读取 this 上的 length 属性,之后读取 this 上的每个整数键并写入到新数组中,同时 value 会被写入指定的 index
const arrayLike = {
    length: 4,
    unrelated: "foo",
    0: 5,
    2: 4,
    3: 6
};
console.log(Array.prototype.with.call(arrayLike, 0, 1));// [ 1, undefined, 4, 6 ]

回顾数组原有方法

  • 不会改变原数组的方法:concat(), join(), slice(), filter(), map(), toString()
ts 复制代码
let original = [1, 3, 2, 4];
const sliced = original.slice(1);
console.log(original); // [ 1, 3, 2, 4 ]
console.log(sliced); // [ 3, 2, 4 ]
  • 会改变原数组的方法:push()/unshift(), pop()/shift(), splice(), reversed(), sort()
ts 复制代码
let original = [1, 3, 2, 4];
const sorted = original.sort();
console.log(original); // [ 1, 2, 3, 4 ]
console.log(sorted); // [ 1, 2, 3, 4 ]

3. 类的私有实例字段(ES2022)

ES2022以前,类的实例中的属性都是可以被访问的,为了防止实例被污染,通过#来声明私有属性

ts 复制代码
class Child{
    #name = 'Tom';
    name = 'Jerry';
    print(){
        console.log(this.#name);
    }
}

const child = new Child();
child.print(); // Tom
console.log(child.#name); // SyntaxError: Private field '#name' must be declared in an enclosing class
console.log(child.name); // Jerry

4. 类的私有方法和访问器(ES2022)

ts 复制代码
class Child{
    #name = 'Tom';
    name = 'Jerry';
    
    // 私有访问器
    get #fullName(){
        return `${this.name} Smith`;
    }

    get fullName(){
        return `${this.name} Williams`;
    }

    // 私有实例方法
    #getName(){
        return this.#name;
    }

    getName(){
        return this.#name;
    }

}

const child = new Child();
console.log(child.#fullName); // SyntaxError: Private field '#name' must be declared in an enclosing class
console.log(child.fullName); // Jerry Williams
console.log(child.#getName()); // SyntaxError: Private field '#name' must be declared in an enclosing class
console.log(child.getName()); // Tom

5. 类的静态字段和静态方法(ES2022)

使用符号static来声明静态字段和方法,静态类字段和方法属于整个类,并非某一具体的实例

ts 复制代码
class Child{
    static NAME = 'Tom';

    static getName(){
        return this.NAME;
    }
}

const child = new Child();
console.log(child.NAME); // undefined
console.log(child.getName()); // TypeError: child.getName is not a function
console.log(Child.NAME); // Tom
console.log(Child.getName()); // Tom

6. 类的静态字段和私有静态方法(ES2022)

ts 复制代码
class Child {
    // 静态字段
    static NAME = 'Tom';
 
    // 静态私有字段
    static #NAME = 'Jerry';

    // 静态方法
    static getName(){
        return this.NAME;
    }

    // 静态私有方法
    static #getName(){
        return this.#NAME;
    }
}

// 实例无法访问静态字段和静态方法,公有私有都不行
const child = new Child();
console.log(child.NAME);  // undefined
console.log(child.#NAME);  // Private field '#NAME' must be declared in an enclosing class
console.log(child.getName());  // TypeError: child.getName is not a function
console.log(child.#getName());  // TypeError: child.getName is not a function

// 类只能访问静态公有字段和静态公有方法,无法访问静态私有字段和静态私有方法
console.log(Child.NAME); // Tom
console.log(Child.#NAME); // Private field '#NAME' must be declared in an enclosing class
console.log(Child.getName()); // Tom
console.log(Child.#getName()); // Private field '#getName' must be declared in an enclosing class

7. at() 负索引查找(ES2022)

取数组的倒数元素:.at()

ts 复制代码
const arr = [1, 2, 3, 4, 5];
// 老方法获取第一个元素
console.log(arr[0]); // 1
// 老方法获取最后一个元素
console.log(arr[arr.length - 1]); // 5

// 获取倒数第一个元素
console.log(arr.at(-1)); // 5
// 获取倒数第二个元素
console.log(arr.at(-2)); // 4
// 获取第一个元素
console.log(arr.at(0)); // 1

// 处理动态数组
console.log(arr.map((i) => (i = i - 1)).at(-1)); // 4

8. 空值合并(ES2020)

旧版本给属性设置默认值的两种方式,但这两种方式有明显弊端,它会覆盖所有的假值,如(0, '', false)

ts 复制代码
let defaultString = '';
let age = defaultString ? defaultString : '18';
console.log(age); // ''

let defaultHeight = 0;
let height = defaultHeight || 10;
console.log(height); // 10

空位合并操作符( ?? ):对假值的处理更加的全面,当且仅当表达式左侧为 undefined 或 null,就返回其右侧默认值

ts 复制代码
// 空位合并操作符不会覆盖假值
let defaultVal = 0;
let value = defaultVal ?? 10;
console.log(value); // 0

// undefined 或 null 的场景
let x = null;
let y = x ?? 10;
console.log(y); // 10

9. 逻辑赋值运算符(ES2021)

逻辑运算符 ( &&, ||, ??),赋值表达式 ( =)

ts 复制代码
a && (a = b);
// 合并写法
a &&= b;

c || (c = e);
// 合并写法
c ||= e;

f ?? (f = g);
// 合并写法
f ??= g;

// 当age的值为null 或 undefined 时,赋值defaultAge
const defaultAge = 18;
let age = null;
age ??= defaultAge;
console.log(age); // 18

10. 数字分隔符(ES2021)

数字组之间使用 _ 作为分隔符来使其在视觉上更具可读性,尤其对于大数场景

ts 复制代码
1000000000; // 这是多少?

1_000_000_000; // 十亿

1_234_500; // 1234500

console.log(1_000_000_000 === 1000000000); // true

11. 去掉开头结尾空格文本(ES2019)

trimStart() 和 trimEnd() 方法,用于去掉字符串前后的空格

ts 复制代码
let str = '   This is an apple   ';

str.trim(); // 'This is an apple'
str.trimStart(); // 'This is an apple   ' 
str.trimEnd(); // '   This is an apple'

12. flat(),flatMap()(ES2019)

flat() 可指定深度的展开数组

ts 复制代码
let arr = [1,2,[3,4,[5,6,[7,8]]]];
const flatArr = arr.flat();
console.log(flatArr); // 从外层开始展开数据,默认深度为1,得到输出:[1,2,3,4,[5,6,[7,8]]]
const flatArrDeep = arr.flat(2);
console.log(flatArrDeep); // 从外层开始展开数据,指定深度为2,得到输出:[1,2,3,4,5,6,[7,8]]
const flatArrInfinity = arr.flat(Infinity);
console.log(flatArrInfinity); // 从外层开始展开数据,指定深度为Infinity,得到输出:[1,2,3,4,5,6,7,8]

flatMap(),等价于在调用 map() 方法后再调用深度为 1 的 flat() 方法

ts 复制代码
let arr = [1,2,3,4,5,6,7,8];
const map = arr.map(num => [num * 2]);
console.log(map); // [[ 2 ],[ 4 ],[ 6 ],[ 8 ],[ 10 ], [ 12 ],[ 14 ], [ 16 ]]
const result = arr.flatMap(num => [num * 2]);
console.log(result); // [2,4,6,8,10,12,14,16];

13. 装饰器(Decorator)

是Javascript类(class)的一个增强功能,是面向对象的语言的一种常见用法。

装饰器是一种函数,使用:@ + 函数名。装饰器函数的返回值,是一个新版本的装饰对象,但也可以不返回任何值(void)。 类 类的属性 类的方法 属性存取器(accessor)

ts 复制代码
type Decorator = (value:Input, context:{
    kind: string;
    name: string | symbol;
    access: {
        get?(): unknown;
        set?(value: unknown): void;
    };
    private?:boolean;
    static?: boolean;
    addInitializer?(initializer: () => void):void;
}) => Output | void;

装饰器的执行步骤如下。 计算各个装饰器的值,按照从左到右,从上到下的顺序。 调用方法装饰器。 调用类装饰器。


业务应用案例 - 方法装饰器

写一个方法装饰器 EventTrack 实现前端埋码平台的事件埋码逻辑

ts 复制代码
export function EventTrack(eventId:EVENT_LIST,eventType: EVENT_TYPE,eventDesc:string) {
    return (target: Object, key: string, descriptor: PropertyDescriptor) => {
        const origin = descriptor.value;
        descriptor.value = function(...args:any) {
            try {
              // 埋点代码
            }catch(err) {}
            
            return origin.apply(this, args);
         };
        
        return descriptor;
    }
}

业务代码内使用此方法装饰器,装饰器的优点为:不污染原有方法业务,不用插入多余代码影响原有代码逻辑

ts 复制代码
@EventTrack(EVENT_LIST.START_VIRUS,EVENT_TYPE.CUSTOM, EVENT_DESC[EVENT_LIST.START_VIRUS].SELECT_ENGINE)
someFunc(){
// 业务代码
}

业务应用案例 - 属性装饰器

通过属性装饰器实现:根据传入的事件来控制某一个公共头部组件 app-version-operation 中,各类操作按钮是否显示的逻辑。

首页,我们写一个属性装饰器 Detection, 通过 Detection 检测并设置当前对象私有属性的值。后续就可以通过判断该私有属性是否有值来判断对应事件是否传入。

ts 复制代码
export function Detection(target, name) {
    let event = new EventEmitter<any>();
    Object.defineProperty(target, name, {
        get() {
            // 设置私有属性的值
            this['#' + name] = true;
            return event;
        }
    });
}

完成 Detection 的实现后,我们再通过装饰器 HasEvent 来控制是否显示的属性值。

ts 复制代码
export function HasEvent(eventName) {
    return (target, name) => {
        Object.defineProperty(target, name, {
            get(){
                // 判断是否存在此私有属性
                return this['#' + eventName];
            }
        });
    }
}

在组件中,如果有传入baseline事件,我们通过装饰器 Detection 来设置私有变量 #baseline 为 true。然后我们再通过装饰器 HasEvent 来对变量 showBaseline进行赋值,如果私有变量 #baseline 为 true,那么showBaseline的值也会设置为true。页面上就会展示baseline操作对应的按钮。

ts 复制代码
@Output() @Detection baseline;
@HasEvent('baseline') showBaseline;

组件侧,只用判断是否传入事件即可同时控制按钮是否显示。不需要显示此操作按钮的场景,直接不绑定事件即可。简化了组件使用的复杂度。

ts 复制代码
<app-version-operation (baseline)="baselineEvent()"></app-version-operation>

🔥 加入我们

DevUI是面向企业中后台产品的开源前端解决方案,其设计价值观基于高效、开放、可信、乐趣四种自然与人文相结合的理念,旨在为设计师、前端开发者提供标准的设计体系,并满足各类落地场景,是一款企业级开箱即用的产品。

如果你今天刚刚加入我们,可以先看看官网上的示例组件,你可以在左侧导航栏中切换想要查看的组件,然后通过右侧的快速前往在不同Demo之间切换。

如果你准备添加 Vue DevUI,请前往快速开始文档,只需要几行代码。

如果你对我们的开源项目感兴趣,并希望参与共建,欢迎加入我们的开源社区,关注DevUI微信公众号:DevUI 。

文 / DevUI社区贡献者 TongxinXie

文献引用

相关推荐
dr李四维5 分钟前
iOS构建版本以及Hbuilder打iOS的ipa包全流程
前端·笔记·ios·产品运营·产品经理·xcode
雯0609~26 分钟前
网页F12:缓存的使用(设值、取值、删除)
前端·缓存
℘团子এ29 分钟前
vue3中如何上传文件到腾讯云的桶(cosbrowser)
前端·javascript·腾讯云
学习前端的小z35 分钟前
【前端】深入理解 JavaScript 逻辑运算符的优先级与短路求值机制
开发语言·前端·javascript
彭世瑜1 小时前
ts: TypeScript跳过检查/忽略类型检查
前端·javascript·typescript
FØund4041 小时前
antd form.setFieldsValue问题总结
前端·react.js·typescript·html
Backstroke fish1 小时前
Token刷新机制
前端·javascript·vue.js·typescript·vue
小五Five1 小时前
TypeScript项目中Axios的封装
开发语言·前端·javascript
小曲程序1 小时前
vue3 封装request请求
java·前端·typescript·vue
临枫5411 小时前
Nuxt3封装网络请求 useFetch & $fetch
前端·javascript·vue.js·typescript