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

文献引用

相关推荐
我要洋人死13 分钟前
导航栏及下拉菜单的实现
前端·css·css3
科技探秘人24 分钟前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人25 分钟前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR30 分钟前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香32 分钟前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q24985969335 分钟前
前端预览word、excel、ppt
前端·word·excel
小华同学ai40 分钟前
wflow-web:开源啦 ,高仿钉钉、飞书、企业微信的审批流程设计器,轻松打造属于你的工作流设计器
前端·钉钉·飞书
Gavin_9151 小时前
【JavaScript】模块化开发
前端·javascript·vue.js
懒大王爱吃狼2 小时前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
逐·風6 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#