2022年 5月
 1
2345678
9101112131415
16171819202122
23242526272829
3031  

近期发布

近期评论

    2022 年 5 月 25 日

    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.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, , b] = [1, 2, 3];
    // a = 1
    // b = 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 = 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!"