Neusofts

科技改变生活,创新引领未来!

ES6+笔记

参考文档

ES6入门: ES6 前言 – ES6文档 (caibaojian.com)

SegmentFault:一文搞定ES6常用API_个人文章 – SegmentFault 思否

菜鸟教程:3.2.3 ES6 对象 | 菜鸟教程 (runoob.com)

知乎:JavaScript ES7~ES11新特性 – 知乎 (zhihu.com)

let 命令

  • let作用域为区块内;
  • let同作用域下只能声明一次;
  • for循环中setTimeout适合使用let,如下:
for (var i = 0; i < 10; i++) {
  setTimeout(function(){
    console.log(i);
  });
}
// 输出十个 10

for (let j = 0; j < 10; j++) {
  setTimeout(function(){
    console.log(j);
  });
}
// 输出 0123456789
  • let不存在变量提升,如下:
console.log(a);  // ReferenceError: a is not defined
let a = "apple";
 
console.log(b);  // undefined
var b = "banana";

const 命令

const 声明简单类型的变量等同于常量,可做只读保护;而声明复杂类型时需慎用,如Object、Array等,仍可修改其成员值(非直接修改常量),如下:

const OPTS = {
	config: {
    	a: 1,
      b: 2
    }
}
OPTS.config = {};		// Uncaught TypeError: Assignment to constant variable.
OPTS.config.a = 111;	// 注意:其成员仍允许修改

解构赋值

  • 解构Array模型
// 基本
let [a, b, c] = [1, 2, 3];
// a = 1
// b = 2
// c = 3

// 可嵌套
let [a, [[b], c]] = [1, [[2], 3]];
// a = 1
// b = 2
// c = 3

// 可忽略
let [a, , c] = [1, 2, 3];
// a = 1
// c = 3

// 不完全解构
let [a = 1, b] = [];
// a = 1, b = undefined

// 剩余运算符
let [a, ...b] = [1, 2, 3];
// a = 1
// b = [2, 3]

// 遍历字符串
let [a, b, c, d, e] = 'hello';
// a = 'h'
// b = 'e'
// c = 'l'
// d = 'l'
// e = 'o'

// 字符串转数组
let [...a] = 'hello';
// a = ["h", "e", "l", "l", "o"]

// 解构默认值
let [a = 2] = [undefined];		// a = 2
let [a = 3, b = a] = [];		// a = 3, b = 3
let [a = 3, b = a] = [1];		// a = 1, b = 1
let [a = 3, b = a] = [1, 2];	// a = 1, b = 2
  • 解构Object模型
// 基本
let { foo, bar } = { foo: 'aaa', bar: 'bbb' };
// foo = 'aaa'
// bar = 'bbb'
let { baz : foo } = { baz : 'ddd' };
// foo = 'ddd'

// 可嵌套、可忽略
let obj = {p: ['hello', {y: 'world'}] };
let {p: [x, { y }] } = obj;
// x = 'hello'
// y = 'world'
let obj = {p: ['hello', {y: 'world'}] };
let {p: [x, {  }] } = obj;
// x = 'hello'

// 不完全解构
let obj = {p: [{y: 'world'}] };
let {p: [{ y }, x ] } = obj;
// x = undefined
// y = 'world'

// 剩余运算符
let {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40};
// a = 10
// b = 20
// rest = {c: 30, d: 40}

// 解构默认值
let {a = 10, b = 5} = {a: 3};
// a = 3; b = 5;
let {a: aa = 10, b: bb = 5} = {a: 3};
// aa = 3; bb = 5;

箭头函数

  • 使用=>定义箭头函数,如下:
let fun = () => {
	console.log('这是一个箭头函数');
}
fun(); // 这是一个箭头函数
  • 箭头函数可以闭包自执行,如下:
;(() => {
	console.log('这是一个闭包箭头函数');
})();
// 这是一个闭包箭头函数
  • 箭头函数不可作为构造函数实例化对象,如下:
let Fun = () => {
    console.log('不可作为构造函数');
}
new Fun(); // Uncaught TypeError: Fun is not a constructor
  • 箭头函数没有arguments变量,如下:
let fun = () => {
	console.log(arguments);
}
fun(); // Uncaught ReferenceError: arguments is not defined
  • 箭头函数可以再简写,如下:
// 只有一个参数的时候,可以省略括号
let fun = val => {};

// 方法体只有一条语句,可省略大括号和return
let sum = num => num + num;
sum(1); // 2
  • 箭头函数不改变this指向即便call或apply传入也改变不了),例如setTimeout在循环中,如react组件绑定事件等等,不再赘述;

模板字符串调用

// 示例
function fun() {
	console.log(arguments[0]);
}

// 调用方法1:
fun(11, 22, 33); // 11

// 调用方法2:
fun`abcd`; // ["abcd", raw: ["abcd"]]

数值扩展

  • Number.EPSILON = 2.220446049250313e-16,是JS表示的最小精度,当JS进行小数运算的时候结果会不准确,可以使用Number.EPSILON进行参考,如下:
// JS中比较浮点数方法
function equal(a, b){
     if(Math.abs(a-b) < Number.EPSILON){
         return true;
     } else {
         return false;
     }
}
console.log(0.1 + 0.2 === 0.3);		// false
console.log(equal(0.1 + 0.2, 0.3));	// true
  • Number.isNaN(number),检测一个数值是否为NaN,如下:
Number.isNaN(11111);	// false
Number.isNaN(NaN);		// true
  • Number.isInteger(number),判断一个数是否为整数,如下:
Number.isInteger(2.5);	// false
Number.isInteger(2.0);	// true
Number.isInteger(-2.0);	// true
Number.isInteger(0.00);	// true
  • Number.isFinite(number),检测一个数值是否为有限数,如下:
Number.isFinite(1/0);		// false
Number.isFinite(Infinity); 	// false
Number.isFinite(0/1);		// true
  • Number.parseInt(string, ?radix) 和 Number.parseFloat(string),字符串转整数和字符串转浮点数,并且去掉不能转换的部分,如下:
Number.parseInt('12.12ABC'); 	// 12
Number.parseInt('ABC12.12'); 	// NaN
Number.parseInt('11', 2);		// 3
Number.parseFloat('12.12ABC'); 	// 12.12
Number.parseFloat('ABC12.12'); 	// NaN

对象方法扩展

  • Object.is(value1, value2),判断两个值是否完全相等,几乎和===等价,如下:
console.log(Object.is(NaN, NaN)); 	// true
console.log(NaN === NaN); 			// false,注意此处

var aa = {};
var bb = {};
console.log(aa === bb); 			// false
console.log(Object.is(aa === bb));	// false
  • Object.assign(target, source[…source[source1, source2, ?source3]]),用于合并对象,将一个或多个源对象的所有可枚举属性,复制到目标对象,如下:
let rec = {};
const conf = {
    target:'获取经书',
    person:['唐僧','悟空']
}

// enumerable为false的属性,不会被复制
Object.defineProperty(conf, 'height' ,{
    value: '188',
    enumerable: false
})    

Object.assign(rec,conf);    
console.log(rec); // {target: "获取经书", person: Array(2)}

// 引用类型复制的是指针,会修改conf对象的person属性数据
rec.person.push('八戒');
console.log(conf); // {target: "获取经书", person: Array(3), height: "188"}

Symbol

ES6 引入一种新的原始数据类型 Symbol ,表示独一无二的值,方便用来定义对象的唯一属性名。ES6 数据类型除了 Number 、 String 、 Boolean 、 Object、 null 和 undefined ,还新增了 Symbol 。

  • 基本用法:因 Symbol 是原始数据类型,不是对象,故不能用 new 命令。可接受一个字符串参数,即new Symbol(?string),为新创建的 Symbol 提供描述,用来显示在控制台或者作为字符串的时候使用,便于区分,如下:
let sy = Symbol("KK");
console.log(sy);	// Symbol(KK)
typeof(sy);			// "symbol"
 
// 相同参数 Symbol() 返回的值不相等
let sy1 = Symbol("kk"); 
sy === sy1;			// false
  • 作为属性名:由于每一个 Symbol 的值都不相等,所以 Symbol 作为对象的属性名,可保证属性不重名。
let sy = Symbol("key1");
 
// 写法1
let syObject = {};
syObject[sy] = "kk";
console.log(syObject);    // {Symbol(key1): "kk"}
 
// 写法2
let syObject = {
  [sy]: "kk"
};
console.log(syObject);    // {Symbol(key1): "kk"}
 
// 写法3
let syObject = {};
Object.defineProperty(syObject, sy, {value: "kk"});
console.log(syObject);		// {Symbol(key1): "kk"}

注意,1:Symbol 作为对象属性名时不能用 “.” 运算符,需用方括号。因为 “.” 运算符后面是字符串,所以取到的是字符串 sy 属性,而不是 Symbol 值 sy 属性,如下:

let sy = Symbol("key1");

let syObject = {};
syObject[sy] = "kk";
 
syObject[sy];  // "kk"
syObject.sy;   // undefined

注意,2:Symbol 值作为属性名时,该属性是公有属性不是私有属性,可在类的外部访问。但不会出现在 for…in 、 for…of 的循环中,也不会被 Object.keys() 、 Object.getOwnPropertyNames() 返回。如果要读取到一个对象的 Symbol 属性,可以通过 Object.getOwnPropertySymbols() 和 Reflect.ownKeys() 取到,如下:

let sy = Symbol("key1");

let syObject = {};
syObject[sy] = "kk";
console.log(syObject);
 
for (let i in syObject) {
  console.log(i); // 无输出
}
 
Object.keys(syObject);             	// []
Object.getOwnPropertySymbols(syObject);  // [Symbol(key1)]
Reflect.ownKeys(syObject);           // [Symbol(key1)]
  • 定义常量:Symbol 的值是唯一的,所以不会出现相同值的常量,如下:
const COLOR_RED = Symbol("red");
const COLOR_YELLOW = Symbol("yellow");
const COLOR_BLUE = Symbol("blue");
  • Symbol.for(key: any):类似单例模式,首先在全局搜索被登记的 Symbol 中是否有该字符串参数为名称的 Symbol 值,如果有即返回该 Symbol 值,若没有则新建并返回一个以该字符串参数为名称的 Symbol 值,并登记在全局环境中供搜索,如下:
let yellow = Symbol("Yellow");
let yellow1 = Symbol.for("Yellow");
yellow === yellow1;      // false(新创建的检索的不同)
 
let yellow2 = Symbol.for("Yellow");
yellow1 === yellow2;     // true(检索的结果相同)
  • Symbol.keyFor(sym: Symbol):返回一个已登记的 Symbol 类型值的 key ,用来检测该字符串参数作为名称的 Symbol 值是否已被登记,如下:
let yellow1 = Symbol.for("Yellow");
Symbol.keyFor(yellow1);    // "Yellow"
  • Symbol.hasInstance,指向一个内部方法钩子,当对象使用instanceof运算符判断是否为该对象实例时会自动调用该方法,如下:
class MyArray {
    [Symbol.hasInstance](arr) {
        console.log('调用了Symbol.hasInstance内置方法1。');
        return arr instanceof Array && arr[0] === 'neusofts';
    }
}

['neusofts', 123, true] instanceof new MyArray; // 注:是类实例
// 调用了Symbol.hasInstance内置方法1。
// true

// 或者:
Object.defineProperty(MyArray, Symbol.hasInstance, {
    value: function (arr) {
        console.log('调用了Symbol.hasInstance内置方法2。');
        return arr instanceof Array && arr[0] === 'neusofts';
    }
});
['neusofts', 123, true] instanceof MyArray; // 注:是类
// 调用了Symbol.hasInstance内置方法2。
// true

// 上述结果可以说明:重新定义对象Symbol.hasInstance属性,当用instanceof操作符时,会触发Symbol.hasInstance属性上定义的方法,判断一个对象是否是构造函数的实例,完全可以使用自定义规则来判断。
  • Symbol.iterator属性(迭代器),数组之所以可以使用for…of(自动判断结束循环)方式遍历,原因是数组原型上包含该属性(迭代器),数组调用该原型方法时,即可返回一个生成器对象,如下:
let arr = ['A', 'B', 'C', 'D'];
let iterator = arr[Symbol.iterator]();
iterator.next(); // {value: "A", done: false}
iterator.next(); // {value: "B", done: false}
iterator.next(); // {value: "C", done: false}
iterator.next(); // {value: "D", done: false}
iterator.next(); // {value: undefined, done: true}

// 小结:Symbol.iterator符号被定义为对象的默认迭代器。内置对象和自定义对象都可以使用这个符号,以提供一个能返回迭代器的方法。当Symbol.iterator在一个对象上存在时,该对象就会被认为是可迭代对象。

// 自定义迭代器:
const myObj = {
    name: [
        'AA',
        'BB',
        'CC',
        undefined,
        'DD'
    ],
    [Symbol.iterator]: function () {
        let i = 0;
        return {
            next: () => {
                if (i < this.name.length) {
                    const result = {
                        value: this.name[i],
                        done: false
                    };
                    i++;
                    return result;
                } else {
                    return {
                        value: undefined,
                        done: true
                    };
                }
            }
        }
    }
}

for (let val of myObj) {
    console.log(val);
}
// AA
// BB
// CC
// undefined
// DD

// 为了for-of更易用,ES6中的许多类型都具有默认的迭代器。所有的集合类型(数组、Map与Set)都具有迭代器。

生成器

  • 生成器定义和调用,生成器是特殊函数,用于实现异步编程,可以迭代器的方式实现调用。生成器的定义用一个*标记在函数名称前,生成器函数返回的结果是迭代器对象,调用迭代器对象的next方法可得到yield语句后的值,如下:
// function generate(): Generator<"AA" | "BB" | "CC", void, unknown>
function *generate() {
    yield 'AA';
    yield 'BB';
    yield 'CC';
}

let iterator = generate();
// 使用next
iterator.next(); // {value: "AA", done: false}
iterator.next(); // {value: "BB", done: false}
iterator.next(); // {value: "CC", done: false}
iterator.next(); // {value: undefined, done: true}

// 或者使用for...of
for (let i of iterator) {
    console.log(i);
}
// AA
// BB
// CC
  • 生成器函数传参,前面yield的执行结果是后面表达式的入参,如下:
function *generate(id) {
    console.log('拿到id' + id);
    let name = yield '通过id' + id + '拿到用户名';
    let info = yield '通过id' + id + '和用户名,拿到用户资料';
}

let iterator = generate(33);

for (let i of iterator) {
    console.log(i);
}
// 拿到id33
// 通过id33拿到用户名
// 通过id33和用户名,拿到用户资料
  • 异步回调场景,如下:
function wrapper(id) {
    var iterator;

    //模拟获取  用户数据  订单数据  商品数据 
    function getCustomer(id) {
        setTimeout(() => {
            let res = '用户数据 - ' + id;
            //调用 next 方法, 并且将数据传入
            iterator.next(res);
        }, 1e3);
    }

    function getOrders(customer) {
        setTimeout(() => {
            let res = '订单数据 - ' + customer;
            iterator.next(res);
        }, 1e3);
    }

    function getGoods(orders) {
        setTimeout(() => {
            let res = '商品数据 - ' + orders;
            iterator.next(res);
        }, 1e3);
    }

    iterator = (function* () {
        console.log('拿到:' + id);
        let customer = yield getCustomer(id);
        console.log('拿到:' + customer);
        let orders = yield getOrders(customer);
        console.log('拿到:' + orders);
        let goods = yield getGoods(orders);
        console.log('拿到:' + goods);
        return goods;
    })();

    iterator.next();
}

wrapper('ID33');
// 拿到:ID33
// 拿到:用户数据 - ID33
// 拿到:订单数据 - 用户数据 - ID33
// 拿到:商品数据 - 订单数据 - 用户数据 - ID33

Promise

  • 定义Promise对象,如下:
const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('返回成功!');
        // reject('返回失败!');
    }, 2e3);
});

promise.then(
    success => console.log(success), 	// 调用resolve时,返回成功!
    fail => console.error(fail) 		// 调用reject时,返回失败!
).finally(() => {
	// 无参,无论成功失败都执行的语句
});

// 相关知识点:
// Promise.prototype.finally(?onFinally); // 无论成功失败都执行的语句
  • 用Promise封装读取文件,如下:
const fs = require('fs');

const promise = new Promise((resolve, reject) => {
    fs.readFile('license.txt', (err, data) => {
        err && reject(err);
        resolve(data);
    });
});

promise.then(
    data => data.toString(),
    err => console.log(err)
);
  • 用Promise封装Ajax,如下:
const promise = new Promise((resolve, reject) => {
    let xhr = new XMLHttpRequest();
    xhr.open('GET', '/test.api');
    xhr.send();
    xhr.onreadystatechange = () => {
        if (xhr.readyState === 4) {
            // 判断响应状态码 200 ~ 299
            if (xhr.status >= 200 && xhr.status < 300) {
                resolve(xhr.response);
            } else {
                reject(xhr.status);
            }
        }
    }
});

promise.then(
    resData => resData,
    errStatus => errStatus
);
  • Promise异步调用,如下:
// 异步函数aFun
var aFun = function (data) {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve(data);
            console.log(data);
        }, 2e3);
    });
}

// 异步函数bFun
var bFun = function (data) {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve(data + 'b');
            console.log(data + 'b');
        }, 2e3);
    });
}

// 异步函数cFun
var cFun = function (data) {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve(data + 'c');
            console.log(data + 'c');
        }, 2e3);
    });
}

async function queue(arr, paramInit) {
    let res = paramInit;

    for (let promise of arr) {
        res = await promise(res);
    }

    return await res;
}

queue([aFun, bFun, cFun], 'a').then(data => data);
// a
// ab
// abc

Map 对象

Map 对象保存键值对。任何值(对象或者原始值)都可以作为一个键或一个值。

Maps 和 Objects 的区别

  • 键名:一个 Object 的键只能是字符串或者 Symbols,但Map允许任意值;
  • 排序:Map 中键值是有序的(FIFO 原则),而添加到Object中的键则不是;
  • 数量:Map 的键值对数量可从 size 属性获取,而 Object 的只能手动计算;
  • 冲突:Object 原型链上的键名有可能和自定义的键名产生冲突。

Map中的key:

  • key是字符串;
  • key是对象,如下:
var myMap = new Map();
var keyObj = {}, 
 
myMap.set(keyObj, "和键 keyObj 关联的值");
myMap.get(keyObj);	// "和键 keyObj 关联的值"
myMap.get({});		// undefined, 此{}非彼keyObj
  • key是函数,如下:
var myMap = new Map();
var keyFunc = function () {}, // 函数
 
myMap.set(keyFunc, "和键 keyFunc 关联的值");
 
myMap.get(keyFunc); 		// "和键 keyFunc 关联的值"
myMap.get(function() {})	// undefined, 此function () {}非彼keyFunc
  • key是NaN,如下:
var myMap = new Map();
myMap.set(NaN, "not a number");
 
myMap.get(NaN); 		// "not a number"
 
var otherNaN = Number("foo");
myMap.get(otherNaN); 	// "not a number",虽然 NaN 和任何值甚至和自己都不相等(NaN !== NaN 返回true),但NaN作为Map的键来说没什么区别。

Map遍历

  • for…of 、解构方法,如下:
var myMap = new Map();
myMap.set(0, "zero");
myMap.set(1, "one");

for (var [key, value] of myMap) {
  console.log(key + " = " + value);
}
// 0 = zero
// 1 = one

for (var [key, value] of myMap.entries()) {
  console.log(key + " = " + value);
}
// 0 = zero
// 1 = one

for (var key of myMap.keys()) {
  console.log(key);
}
// 0
// 1

for (var value of myMap.values()) {
  console.log(value);
}
// zero
// one
  • forEach() 、箭头函数,如下:
var myMap = new Map();
myMap.set(0, "zero");
myMap.set(1, "one");

myMap.forEach((value, key) => {
  console.log(key + " = " + value);
}, myMap);
// 0 = zero
// 1 = one

Map 操作

  • 与Array转换,如下:
var kvArray = [["key1", "value1"], ["key2", "value2"]];

// 转为Map
var myMap = new Map(kvArray); // Map(2) {"key1" => "value1", "key2" => "value2"}

// 转为Array
var outArray = Array.from(myMap);
// (2) [Array(2), Array(2)]
// 0: (2) ["key1", "value1"]
// 1: (2) ["key2", "value2"]
  • 克隆,如下:
var kvArray = [["key1", "value1"], ["key2", "value2"]];
var myMap1 = new Map(kvArray);
var myMap2 = new Map(myMap1);
 
console.log(myMap1 === myMap2); // false,Map对象会构造出新的对象
  • 合并,如下:
// 合并2个二维数组
var first = new Map([[1, 'one'], [2, 'two'], [3, 'three'],]);
var second = new Map([[1, 'uno'], [2, 'dos']]);

var merged = new Map([...first, ...second]);
// Map(3) {1 => "uno", 2 => "dos", 3 => "three"}
// 合并时,如有重复的键值,后面的会覆盖前面的

Set对象

Set 对象允许存储任何类型的唯一值,无论是原始值或者是对象引用。

特殊值:

  • +0 与 -0 在存储判断唯一性的时候是恒等的,所以不重复;
  • undefined 与 undefined 是恒等的,所以不重复;
  • NaN 与 NaN 是不恒等的,但是在 Set 中只能存一个,不重复。
let mySet = new Set([+0, -0, undefined, undefined, NaN, NaN]);
// Set(3) {0, undefined, NaN} 验证以上3项不重复

var o = {a: 1, b: 2}; 
mySet.add(o);
mySet.add({a: 1, b: 2});
// Set(5) {0, undefined, NaN, {…}, {…}} 对象类型认为非同一个
  • 类型转换Array,如下:
// Array转Set
var mySet = new Set(["value1", "value2", "value3"]);
// Set(3) {"value1", "value2", "value3"}

// Set转Array
var myArray = [...mySet];
// (3) ["value1", "value2", "value3"]

// String转Set
new Set('hello');
// Set(4) {"h", "e", "l", "o"}
  • 数组去重,如下:
new Set([1, 2, 3, 4, 4, 'a', 'a', 'b']);
// Set(6) {1, 2, 3, 4, "a", "b"}
  • 并集,如下:
var a = new Set([1, 2, 3]);
var b = new Set([4, 3, 2]);
var union = new Set([...a, ...b]); // {1, 2, 3, 4}
  • 交集,如下:
var a = new Set([1, 2, 3]);
var b = new Set([4, 3, 2]);
var intersect = new Set([...a].filter(x => b.has(x))); // {2, 3}
  • 差集,如下:
var a = new Set([1, 2, 3]);
var b = new Set([4, 3, 2]);
var difference = new Set([...a].filter(x => !b.has(x))); // {1}

模块化

  • 导出方式,如下:
// 逐一暴露
export let name = 'Neusofts';
export function action() {
    console.log('Neusofts');
}

// 统一暴露
let name = 'Neusofts';

function action() {
    console.log('Neusofts');
}
export {
    name,
    action
};

// 默认暴露
export default {
    name: 'Neusofts',
    action: function () {
        console.log('Neusofts');
    }
}
  • 导入方式,如下:
// 通用的导入方式
import * as All from './m1.js';

// 解构赋值形式
import {
    name,
    action
} from './m2.js';

// 默认暴露引入方式
import obj from './m3.js';

其他

  • class类
  • function可设置默认值

附加内容

.

ES7新特性

  • Array.prototype.includes(searchElement: any, fromIndex?: number),检测数组中是否包含某个元素,如下:
var arr = ['www', 'neusofts', 'com'];
arr.includes('neusofts'); // true
  • 指数操作符**,用来幂运算,功能与 Math.pow(x: number, y: number): number 相同,如下:
console.log(2 ** 3); 	// 8
Math.pow(2, 3); 		// 8
  • async和await,改造Promise实现的Ajax的示例,如下:
function Ajax(url) {
    return new Promise((resolve, reject) => {
        let xhr = new XMLHttpRequest();
        xhr.open('GET', url);
        xhr.send();
        xhr.onreadystatechange = () => {
            if (xhr.readyState === 4) {
                // 判断响应状态码 200 ~ 299
                if (xhr.status >= 200 && xhr.status < 300) {
                    resolve(xhr.response);
                } else {
                    reject(xhr.status);
                }
            }
        }
    });
}

async function getInfo() {
    let res1 = await Ajax('https://api.apiopen.top/getJoke');
    console.log(res1);

    console.log('-'.repeat(43));
    
    let res2 = await Ajax('https://api.apiopen.top/getJoke');
    console.log(res2);
}

getInfo();
// text1_text1_text1_text1_text1_text1_text1 ...
// -------------------------------------------
// text2_text2_text2_text2_text2_text2_text2 ...

提案

  • 定义类的私有变量“#”,如下:
class Test {
    #mySelf = '私有变量';
    fun(){
        console.log(this.#mySelf);
    }
}

let test = new Test();
test.fun(); // 私有变量
  • Optional Chaining(可选链式调用),如下:
// 需求:读取某对象下的某个属性
const data = {
  user: {},
};

// 直接读取,报错:
console.log(data.user.address.street); 
// Uncaught TypeError: Cannot read property 'street' of undefined

// 报错原因是 user 中没有 address 对象,然后我们这样判断:
const street = data && data.user && data.user.address && data.user.address.street;
console.log(street); // undefined
// 这样的写法很不爽

// 新特性可以这样写( ?. 类似于angular5的安全操作符)
console.log(data.user ?. address ?. street); // undefined
  • Nullish coalescing(空值合并),如下:
// 我们判断空值一般这样:
value != null ? value : 'default value';

// 或者这样:
value || 'default value';

// 新特性可以这样写( ?? ):
value ?? 'default value';

let a = 0;
let b = a || "aaa";
let c = a ?? "aaa";
console.log("b的值是 " + b); // b的值是 aaa
console.log("c的值是 " + c); // c的值是 0
  • Pipeline operator(管道运算符),目前vscode和chrome尚未支持测试,代码示例如下:
// 举个例子,通过三个函数对字符串进行处理,通常是这样:
function a(str) {
    return str + ", " + str;
}

function b(str) {
    return str[0].toUpperCase() + str.substring(1);
}

function c(str) {
    return str + '!';
}

let result = c(b(a("hello"))); // "Hello, hello!"

// 通过管道运算符,我们可以这样写:
let result = "hello" |> a |> b |> c // "Hello, hello!"

———— End ————