ES6常用重要知识点

记录ECMAScript6中常用的、重要的知识。
其它数组API、正则API、字符串API等比较简单,参考文档的demo即可,如阮一峰的ECMAScript 6 入门

一、模板字符串

  模板字符串就是一种字符串的新的表现形式,它解决了一些以前字符串的痛点,例如字符串和变量拼接、字符串换行。
  其用法如下:

    // a. 基本用法
    var s1 = ` hello `
    var s2 = ' hello '

    // b. 字符串和变量拼接
    var s3 = " a " + s1 + " b " + s2;
    var s4 = ` a ${s1} b ${s2}`;  // 减少了错误几率

    // c. 字符串换行
    var s5 = `<div>
                <p>
                    <span>123</span>
                </p>
                <p>${s2}</p>
                <p>${s3}</p>
                <p>${s1}</p>
            </div>`;
    console.log(s5);

二、解构赋值

  对象的解构赋值用法:

    var obj = {name: "abc", age: 18};
    // 用解构赋值的方式获取name
    let {name} = obj;  // 创建了一个变量name,值=obj.name
    console.log(name);  // "abc"

    var obj3 = {gender: "abc", height: 18, grade: "一年级"};
    var {gender, height, grade} = obj3;
    // 创建了gender=obj3.gender 、height=obj3.height、grade=obj3.grade

  解构赋值的意义所在 - 函数参数的解构赋值:

    function fn({width, height, age}) {
        // 创建了width、height、age三个局部变量,值来自于实参
        // 这样写代码又节省了一些字符
    }

    fn({
        width: 100,
        height: 100,
        age: 50
    })

  解构赋值的其他用法:

    var obj = {name: "张三", age: 18}

    // 创建一个新的变量:objName,值=obj.name
    var {name: objName} = obj;

    // 创建了2个新变量
    var {name: objName2, age: objAge2} = obj;

三、函数的扩展

1. rest参数

  使用背景:es6箭头函数内部不能使用arguments,为了弥补这个问题,rest参数应孕而生。
  以前用arguments的写法:

    function fn() {
        // arguments是函数内部的一个局部变量,
        // arguments.length表示函数的实参的个数
        console.log(arguments.length);  // 3

        for (var i = 0; i < arguments.length; i++) {
            console.log(arguments[i]);
        }

    }

    fn(1, 3, 5)

  使用rest参数写法:

    // ...args就是rest参数
    // 产生了一个变量,这个变量是一个数组,数组里面包含了这个函数调用时传递的所有实参
    function q(...args) {
        // 验证args是不是数组?
        console.log(args instanceof Array);  // true
        console.log(Object.prototype.toString.call(args));  // "[object Array]"
        console.log(Array.isArray(args));  // true es5中的新方法

        console.log(args);
    }

    q(1, 3, 5);

  优点:arguments是伪数组,而rest参数是真数组

2. 箭头函数

  场景:用于替换匿名函数。
  基本用法:
  无参的匿名函数:

    // 匿名函数
    div.onclick = function () {
        console.log('你好')
    }
    // 箭头函数
    div.onclick = () => {
        console.log('你好')
    }

  有一个参数的匿名函数:

    // 匿名函数
    var fn = function (name) {
        console.log(name);
    }
    // 箭头函数
    var fn = name => {
        console.log(name);
    }
    // 也等价于:
    var fn = (name) => {
        console.log(name);
    }

  有2个或以上的参数的匿名函数:

    // 匿名函数
    var fn = function (name, age) {

    }
    // 箭头函数
    var fn = (name, age) => {
        // 切记:()不能省略
    }

  好处:有了箭头函数之后,我们的代码清晰了很多。

    var students = [1, 3, 5];
    students.forEach(function (value, index) {

    })
    students.forEach((value, index) => {

    })
    // 如果只需要一个参数
    students.forEach(value => {

    })

  箭头函数和普通匿名函数有哪些不同?
  • 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
  • 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
  • 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
  • (不常用)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
    ৹ generator函数现在经常用async替代

  针对this对象的不同点,举个例子。
  注意:
  普通匿名函数和具名函数,都有独立作用域,都可以决定函数内部的this值;
  箭头函数不具有独立作用域,它里面的this是由外层函数决定的。

    var p = {
        age: 18,
        // es6中对象方法的箭头函数表示形式
        run: () => {
            setTimeout(() => {
                // this:window
                console.log(this);  // this是window
            }, 100)
        },
        travel: function () {
            // this:p
            setTimeout(() => {
                console.log(this);  // this是p
            }, 100)
        },
        // 推荐使用的方式☆☆☆:es6中对象方法的简写形式
        say() {
            console.log("say方法中的this:", this);
            setTimeout(() => {
                console.log("say内部的延迟函数:", this);  // this是p
            }, 100)
        },
    }

    p.run();
    p.travel();
    p.say();

四、对象的扩展

  在ES6之前,需要通过 for…in 遍历属性或者jquery的 $.extend 实现拷贝,在ES6之后,多了一些新的方法。

1. Object.assign:实现拷贝继承

    // Object.assign 就是进行对象的浅拷贝
    var source = {age: 18, height: 170, className: "3年2班"}

    // 克隆一个新对象出来
    var newObj = Object.assign({}, source);
    console.log(newObj);

    // 也可以先创建目标对象
    var newObj2 = {};
    Object.assign(newObj2, source);
    console.log(newObj2);

2. 对象扩展运算符

    var car = {brand: "BMW", price: "368000", length: "3米"}

    // 克隆一个跟car完全一样的对象出来:
    var car2 = {...car}
    console.log(car2);

    // 新车子,跟car的长度不同,其他相同
    var car3 = {...car, length: "4米"}
    console.log(car3);

    // 在原对象的基础上新增属性
    var car4 = {...car, type: "SUV"}
    console.log(car4);

    // 复制一个数组
    var s1 = [1, 3, 5, 7, 9];
    var s2 = [...s1];
    console.log(s2);

五、Promise

1. 为什么要有promise

  Promise用于解决(回调地狱)的问题,所谓回调地狱,如下:

    $.get("/getUser",function(res){
        $.get("/getUserDetail",function(){
            $.get("/getCart",function(){
                $.get("/getBooks",function(){
                    //...
                })
            })
        })
    })

2. Promise函数基本用法

  把异步操作封装在一个promise对象中,通过执行resolve函数,告诉外界你可以执行下一步操作了。而执行的下一步操作,其实就是写在then的回调函数中的。

    function fn() {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log('你好');
                resolve();
            }, 1000);
        })
    }

    fn().then(res => {
        console.log("第一步");
        fn().then(res => {
            console.log("第二步");
        })
    })

    // 输出顺序:
    // 你好
    // 第一步
    // 你好
    // 第二步

3. Promise函数实现多层回调

  上面的代码看上去似乎并没有解决回调地狱的问题,于是我们看下面的代码。

    function f1() {
        return new Promise(resolve => {
            setTimeout(() => {
                console.log('第一步');
                resolve();
            }, 1000)
        })
    }

    function f2() {
        return new Promise(resolve => {
            setTimeout(() => {
                console.log('第二步');
                resolve();
            }, 1000)
        })
    }

    f1().then(res => {
        return f2();
    }).then(res => {
        return f1();
    }).then(res => {
        return f2();
    }).then(res => {
        setTimeout(() => {
            console.log('完成');
        }, 1000)
    })

    // 输出顺序:
    // 第一步
    // 第二步    
    // 第一步
    // 第二步
    // 完成

4. Promise传参

    function getUser() {
        return new Promise(resolve => {
            $.get("/getUser", res => {
                // res是从服务器中接收到的数据
                resolve(res)
            })
        })
    }

    getUser().then(res => {
        // res就表示上一个异步操作返回的参数值:从服务器中获取的数据
    })

5. Promise函数错误处理

    function getBooks() {
        // 执行了resolve()表示异步操作是成功的
        // 执行了reject()表示异步操作是失败的
        return new Promise((resolve, reject) => {
            $.ajax({
                url: "/getBooks",
                success(res) {
                    // 成功获取数据
                    resolve(res);
                },
                error(resError) {     // resError表示错误信息
                    // 如果失败,执行error方法
                    // 通过执行reject函数,把错误信息传递给外界
                    reject(resError)
                }
            })
        })
    }

    // 第一种处理错误的方式:
    getBooks().then(res=>{
        // res表示请求成功时候获取到的数据
    },resError=>{
        console.log(resError);
    })

    // 第二种错误处理的方式:
    getBooks().then(res => {
        //成功了
    }).catch(resError => {
        //这里也可以获取到错误信息
    })

  上面2种错误处理的方式,第二种更加推荐。
  第二种方式更强大的地方在于:
  不仅仅可以捕获到reject传递的参数,还可以捕获到成功的回调中发生的错误。

六、async

  async诞生于ES8,它其实是一个promise的语法糖。

1. async函数基本用法

  首先定义一个async函数,
  await表示这行代码是一个异步操作,但是await必须在async函数内执行,
  await下面的代码会在异步操作之后执行,这里的异步操作执行完毕其实就是reslove。

    function f1() {
        return new Promise(resolve => {
            setTimeout(() => {
                console.log('你好');
                resolve();
            }, 1000);
        })
    }

    (async function () {
        await f1();
        console.log('第二步');
        await f1();
        await f1();
        console.log('第三步');
    })()

    // 输出顺序:
    // 你好
    // 第二步    
    // 你好
    // 你好
    // 第三步

2. async处理返回值

  await操作可以有返回值,这个返回值表示promise操作成功的返回值。
  前面的例子我们将async函数包装成了自执行函数,其实这不是必须的,只是因为await函数必须在async函数内执行。
  我们也可以先创建一个async函数,然后在其内部使用async函数。
  下面的代码实现一个需求,在say方法和run方法内部要获取await操作的返回值,同时要求先执行完毕say,再执行run。故有两个地方要做async函数:

    function q() {
        return new Promise((resolve) => {
            setTimeout(() => {
                resolve("你好");
            }, 1000)
        })
    }

    var o1 = {
        // 可以使用箭头函数
        say: async () => {
            console.log('say方法:');
            const res = await q();
            console.log(res);
        },
        // 也可以使用普通匿名函数
        run: async function () {
            console.log('run方法');
            const res = await q();
            console.log(res);
        }
    }

    // 需求,先执行完毕say,再执行run
    var fn = async function () {
        await o1.say();
        await o1.run();
    }
    fn();

    // 输出顺序:
    // say方法
    // 你好
    // run方法
    // 你好

3. async错误处理

  如果await里面执行的异步操作发生了reject,或者发生了错误,那么只能使用try…catch语法来进行错误处理

    function q() {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                reject("你好");
            }, 100)
        })
    }

    (async function () {
        try {
            let res = await q();
            console.log(res);
        } catch (e) {
            console.log(e);
        }
    })()

七、class

1. 与构造函数的对比

  下面的例子对比了构造函数方式和类方式的区别,更多关于构造函数的知识可查看JS中的继承

    // 构造函数的方式
    function Teacher(name, age) {
        this.name = name;
        this.age = age;
    }
    Teacher.prototype.say = () => {
        console.log('我是老师');
    }
    var t1 = new Teacher("王大锤", 24);


    // 类的方式
    class Student {
        // 构造方法
        constructor(name, age) {
            this.name = name;
            this.age = age;
        }
        say() {
            console.log(`我叫${this.name},今年${this.age}岁`);
        }
    }
    var s1 = new Student("小明", 16);

2. 类的用法

  下面的例子演示了类如何:添加实例方法,添加静态方法,以及类的继承。
  其中:
  • 一般创建一个类时都要添加一个constructor方法,也就是构造方法
  • 类的静态成员包括静态属性、静态方法
    ৹ 静态属性:通过类本身来访问:Person.maxAge
    ৹ 静态方法:通过类本身来访问的一个方法:Person.born();

    class Person {
        constructor(name,age) {
            this.name=name;
            this.age=age;
        }
        // 定义方法
        say() {
            console.log("大家好,我叫:"+this.name+",今年:"+this.age+"岁");
        }
        // 定义静态方法
        static born(){
            console.log("小呆萌出生了")
        }
    }

    // 访问静态方法
    Person.born();

    // Student类继承自Person类
    class Student extends Person {
        // 构造方法
        constructor(name,age,grade){
            // 规定:必须调用父类构造方法,如果不调用就会报错
            // 调用父类构造方法,从而给子类的实例添加了name,age属性
            super(name,age);

            this.grade=grade;
        }
    }

八、ES6浏览器兼容问题

  可以通过 babel 解决。babel可以将任意版本的ES转换为任意旧版本的ES,甚至要求兼容哪些浏览器,或者说指定的版本。
  一般都会集成在脚手架里,比如 vue-cli 内置了 babel,如果需要可以修改兼容的版本。


  转载请注明: 文渊博客 ES6常用重要知识点

  目录