zoukankan      html  css  js  c++  java
  • es6 学习心得

     

    1、let/ const声明变量(项目中常用)

    之前使用var关键字声明变量,无论声明在何处,都会被视为声明在它的作用域的最顶部(不在大括号内{}即在全局作用域的最顶部),而在es6之前JavaScript只有函数作用域和全局作用域。这就是变量提升。

    console.log(a);  //输出undefined
    
    if (true) {
    
        var a = 1;
    
    }

    上面的代码实际上是:

    var a;
    
    console.log(a); //输出undefined
    
    if (true) {
    
        a = 1;
    
    }

    (1)let声明变量

    let实际上为JavaScript新增了块级作用域。let声明的变量只在它所在的代码块内有效。

    将上面变量提升的例子稍微变化一下:可以看出let声明的a只在代码块内有效。

    if(true) {
    
        var a = 1;
    
    }
    
    console.log(a)  //输出1
    if(true) {
    
        let a = 1;
    
    }
    
    console.log(a) //报错ReferenceError: a is not defined

    再看一个常见的例子:

    var a = [];
    
    for (var i = 0; i < 10; i++) {
    
        a[i] = function () {
    
            console.log(i);
    
        };
    
    }
    
    a[6](); // 10

         每次循环的i指向的其实都是同一个i,很显然最后的结果是10。那么如何达到想要的效果?

         ·闭包(ES5)

    var a = [];
    
    for (var i = 0; i < 10; i++) {
    
        a[i] = (function (num) {
    
            return function () {
    
                console.log(num);
    
            };
    
        })(i)
    
    }
    
    a[6](); // 6

    ·let声明变量(ES6)

    var a = [];
    
    for (let i = 0; i < 10; i++) {
    
        a[i] = function () {
    
            console.log(i);
    
        };
    
    }
    
    a[6](); // 6

    变量ilet声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量。

    使用let声明变量的话,不存在变量提升的情况,必须在声明以后才使用,否则会报错,这在语法上称为“暂时性死区”。并且let不允许在相同作用域内重复声明变量。

    (2)const声明变量

    const声明的变量是一个只读变量,一旦声明就必须马上初始化并且不能改变值,因此如果用const只声明而不赋值也会报错。

    const的作用域与let相同,只在当前的块级作用域内有效。同样的,也不能变量提升,存在暂时性死区,不能重复声明。

    本质:

    const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指针,const只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。

    const foo = {};
    
     
    
    // 为 foo 添加一个属性,可以成功
    
    foo.prop = 123;
    
    foo.prop // 123
    
     
    
    // 将 foo 指向另一个对象,就会报错
    
    foo = {}; // TypeError: "foo" is read-only

    总结:因此,可以知道什么时候用let什么时候用const:值会改变的变量用let声明,值是一个不会改变的常量就用const声明。

    2、箭头函数(项目中常用)

    Es6使用“箭头”(=>)定义函数。箭头函数使得表达更简洁。

    箭头函数的写法:

    const fn = (a, b) => a + b;

    就相当于ES5标准下的:

    var fn = function (a, b) {
    
        return a + b;
    
    }

    箭头函数还可以与变量解构结合使用。

    const full = ({ first, last }) => first + ' ' + last;
    // 等同于
    
    function full(person) {
    
        return person.first + ' ' + person.last;
    
    }

    使用箭头函数有几点需要注意。

    (1)      函数体内的this对象,是定义时所在的对象,而不是使用时所在的对象。如果在箭头函数内使用this,那么该this是外层的this,换句话说,就是箭头函数里根本没有自己的this,而是引用外层的this。

    (2)      不可以当做构造函数,也就是说,不可以使用new命令,否则抛出错误。

    (3)      不可以使用arguments对象,该对象在函数体内不存在。如果一定要用,可以使用rest参数代替。

    上面的第一点尤其要注意,this对象的指向是可变的,但在箭头函数中,它是固定的。

    function foo() {
    
        setTimeout(() => {
    
            console.log('id:', this.id);
    
        }, 100);
    
    }
    
    var id = 21;
    
    foo.call({ id: 42 });  // id: 42

    上面代码中,setTimeout的参数是一个箭头函数,该箭头函数生效是在foo函数生成时,而它真正执行则是在100毫秒后。如果是普通函数,执行时的this执行全局对象window,输出21。而箭头函数的this总是绑定在定义生效时所在的作用域,因此输出42。

    3、模板字符串

    模板字符串解决了使用+号拼接字符串造成的不便利。模板字符串用(`)反引号标识,可以作普通字符串,也可以定义多行字符串,或者在字符串在嵌入变量。

    // 普通字符串
    
    `In JavaScript '
    ' is a line-feed.`
    // 多行字符串
    
    `In JavaScript this is
    
     not legal.`
    // 字符串中嵌入变量
    
    let name = "Bob",
    
        time = "today";
    
    `Hello ${name}, how are you ${time}?`

    使用模板字符串表示多行字符串,所以的空格和缩进都会被保留在输出中。

    使用${}包裹一个变量或表达式。

    4、变量的解构赋值

    (1)数组的解构赋值

    [基本用法]

    let [a, b, c] = [1, 2, 3];

    上面代码表示。可以从数组中提取值,按照对应位置,给变量赋值。

    本质上,这种写法属于“模式匹配”,只要等号两边模式相同,左边的变量就会被赋予对应的值。

    如果解构不成功,变量的值就等于undefined。

    let [foo] = [];
    
    foo //undefined
    
    let [bar, foo] = [1];
    
    foo //undefined

    另一种情况是不完全解构,即等号左边的模式只匹配一部分等号右边的数组,这种情况下,解构依然成功

    let [x, y] = [1, 2, 3];
    
    x // 1
    
    y // 2

    [默认值]

    解构赋值允许制定默认值。

    let [foo = true] = [];
    
    foo // true

    es6内部使用严格相等运算符(===),判断一个位置是否有值。只有数组成员严格等于undefined,默认值才会生效。

    let [x, y = 'b'] = ['a']; // x='a', y='b'
    
    let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'
    
    let [x = 'a'] = [null];
    
    x // null

    null不严格等于undefined,因此默认值不生效

    (2)对象的解构赋值

    解构不仅可以用于数组,还可以应用于对象。

    let { bar, foo } = { foo: "aaa", bar: "bbb" };
    
    foo // "aaa"
    
    bar // "bbb"

     等号左边的两个变量的次序,与等号右边两个同名属性的次序不一致,但是对取值完全没有影响。

    let { baz } = { foo: "aaa", bar: "bbb" };
    
    baz // undefined

    变量没有对应的同名属性,导致取不到值,最后等于undefined

    对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。

    let { foo: baz } = { foo: "aaa", bar: "bbb" };
    
    baz // "aaa"
    
    foo // error: foo is not defined

    foo是匹配的模式,baz才是变量。真正被赋值的是变量baz,而不是模式foo

    (3)字符串的解构赋值

    const [a, b, c, d, e] = 'hello';
    
    a // "h"
    
    b // "e"
    
    c // "l"
    
    d // "l"
    
    e // "o"

    还可以对字符串的length属性解构赋值

    let { length: len } = 'hello';
    
    len // 5

    (4)数值和布尔值的解构赋值

    解构赋值是,登等号右边是数值或布尔值,则会先转为对象。

     let { toString: s } = 123;
    
    s === Number.prototype.toString // true
    
     
    
    let { toString: s } = true;
    
    s === Boolean.prototype.toString // true

    (5)函数参数的解构赋值

    函数的参数也可使用解构赋值。

    function add([x, y]) {
    
        return x + y;
    
    }
    
    add([1, 2]); // 3

    (6)用途

    变量的解构赋值的用途:

    ·交换变量的值

    let x = 1;
    
    let y = 2;
    
    [x, y] = [y, x];

    ·从函数返回多个值

    函数只能返回一个值,如果要返回多个值,只能把他们放在数组或对象里返回。有了解构赋值,取出这些值非常方便。

    // 返回一个数组
    
    function example() {
    
        return [1, 2, 3];
    
    }
    
    let [a, b, c] = example();
    // 返回一个对象
    
    function example() {
    
        return {
    
            foo: 1,
    
            bar: 2
    
        };
    
    }
    
    let { foo, bar } = example();

    ·函数参数的定义

    解构赋值可以方便地将一组参数与变量名对应。

    // 参数是一组有次序的值
    
    function f([x, y, z]) { ... }
    
    f([1, 2, 3]);
    // 参数是一组无次序的值
    
    function f({ x, y, z }) { ... }
    
    f({ z: 3, y: 2, x: 1 });

    ·提取数据

    解构赋值对提取JSON对象中的数据尤其有用。

    let jsonData = {
    
        id: 42,
    
        status: "OK",
    
        data: [867, 5309]
    
    };
    
     
    
    let { id, status, data: number } = jsonData;
    
    console.log(id, status, number);
    
    // 42, "OK", [867, 5309]

    ·函数参数的默认值

    指定参数的默认值,就避免了在函数内部再写var foo = config.foo || ‘default foo’。

    jQuery.ajax = function (url, {
    
        async = true,
    
        beforeSend = function () {},
    
        cache = true,
    
        complete = function () {},
    
        crossDomain = false,
    
        global = true,
    
        // ... more config
    
    }) {
    
        // ... do stuff
    
    };

    5、函数参数的默认值

    (1)默认用法

    ES6之前不能给函数参数指定默认值,只能采用变通的方法。

    function log(x, y) {
    
        y = y || 'World';  
    
        console.log(x, y);
    
    }
    
     
    
    log('Hello') // Hello World
    
    log('Hello', 'China') // Hello China
    
    log('Hello', '') // Hello World

    上面的代码检查函数的参数y有没有赋值,如果没有,则指定默认值为World。但这样的缺点在于,如果参数y赋值了,但对应的布尔值为false,则该赋值不起作用。如上面代码最后一行,y等于空字符,结果还是被改为默认值。

    为避免这个问题,通常需先判断参数y是否被复制,如果没有,再等于默认值。

    if (typeof y === 'undefined') {
    
        y = 'World';
    
    }

     

    Es6允许函数的参数设置默认值,直接写在参数定义的后面。

    function log(x, y = 'World') {
    
        console.log(x, y);
    
    }
    
     
    
    log('Hello') // Hello World
    
    log('Hello', 'China') // Hello China
    
    log('Hello', '') // Hello

     

    (2)与解构赋值默认值结合使用

    函数参数默认值可以与解构赋值的默认值,结合起来使用。

    function foo({ x, y = 5 }) {
    
        console.log(x, y);
    
    }
    
     
    
    foo({}) // undefined 5
    
    foo({ x: 1 }) // 1 5
    
    foo({ x: 1, y: 2 }) // 1 2
    
    foo() // TypeError: Cannot read property 'x' of undefined

    上面的代码只使用了对象的解构赋值默认值,没有使用函数参数的默认值。只当函数foo的参数是一个对象时,变量x和y才会通过解构赋值生成。如果函数调用时没提供参数,变量x和变量y就不会生成,因此报错。提供函数的默认值就可以避免这种情况。

    function foo({ x, y = 5 } = {}) {
    
        console.log(x, y);
    
    }
    
     
    
    foo() // undefined 5

    上面的代码指定,没有提供参数,函数的参数默认为一个空对象。

    // 写法一
    
    function m1({ x = 0, y = 0 } = {}) {
    
        return [x, y];
    
    }
    
     
    
    // 写法二
    
    function m2({ x, y } = { x: 0, y: 0 }) {
    
        return [x, y];
    
    }

    上面两张写法都对函数的参数设定了默认值,区别是写法一函数参数的默认值是空对象,但设置了对象解构赋值的默认值;写法二函数参数的默认值是一个有具体属性的对象,但是没有设置对象解构赋值的默认值。

    // 函数没有参数的情况
    
    m1() // [0, 0]
    
    m2() // [0, 0]
    
     
    
    // x 有值,y 无值的情况
    
    m1({ x: 3 }) // [3, 0]
    
    m2({ x: 3 }) // [3, undefined]
    
     
    
    // x 和 y 都无值的情况
    
    m1({}) // [0, 0];
    
    m2({}) // [undefined, undefined]

    (3)作用域

    设置了参数的默认值,函数进行声明初始化时,参数会形成单独的作用域,初始化结束,则该作用域消失。

    var x = 1;
    
    function f(x, y = x) {
    
        console.log(y);
    
    }
    
    f(2) // 2

    上面代码,y的默认值等于变量x。调用函数f时,参数形成一个单独的作用域。在这个作用域里,默认值变量x指向第一个参数x,而不是全局变量x,因此输出的是2。

    let x = 1;
    
    function f(y = x) {
    
        let x = 2;
    
        console.log(y);
    
    }
    
    f() // 1

    上面代码,函数f调用时,参数y=x形成一个单独作用域,在这个作用域里,变量x本身无定义,所以指向外层的全局变量x。函数调用时,函数体内部的局部变量x不影响默认值变量x。

    var x = 1;
    
    function foo(x = x) {
    
        // ...
    
    }
    
    foo() // ReferenceError: x is not defined

    上面代码,参数x=x形成单独作用域,实际执行的是let x = x,由暂时性死区的原因,这行代码会报错“x未定义”。

    如过参数的默认值是一个函数,该函数的作用域也遵守这个规则。

    let foo = 'outer';
    
    function bar(func = () => foo) {
    
        let foo = 'inner';
    
        console.log(func());
    
    }
    
    bar(); // outer

    上面代码,函数bar的参数func的默认值是一个匿名函数,返回值为变量foo。函数参数形成的单独作用域里,没有定义变量foo,因此foo指向外层的全局变量foo,因此输出outer。

    var x = 1;
    
    function foo(x, y = function () { x = 2; }) {
    
        var x = 3;
    
        y();
    
        console.log(x);
    
    }
    
    foo() // 3
    
    x // 1

    上面代码,函数foo的参数形成一个单独作用域。改作用域里,首先声明变量x,然后声明变量y,y的默认值是一个匿名函数。这个匿名函数内部的变量x,指向同一个作用域的第一个参数x。函数foo内部又声明了一个内部变量x,该变量与第一个参数x由于不是同一个作用域,所以不是同一个变量,因此执行y后,内部变量x和外部全局变量x的值都没变。

    var x = 1;
    
    function foo(x, y = function () { x = 2; }) {
    
        x = 3;
    
        y();
    
        console.log(x);
    
    }
    
    foo() // 2
    
    x // 1

    如果将var x = 3的var去除,函数foo的内部变量x就指向第一个参数x,与匿名函数内部的x是一致的,最后输出2,而外层的全局变量x不受影响。

    6、rest参数

    引入rest参数(…变量名),用于获取函数的多余参数,这样就不需要使用arguments对象。Rest参数搭配的变量是一个数组,将多余的参数放入数组中。

    function add(...values) {
    
        let sum = 0;
    
        for (var val of values) {
    
            sum += val;
    
        }
    
        return sum;
    
    }
    
    add(2, 5, 3) // 10

    add函数是一个求和函数,利用rest参数,可以向该函数传入任意数目的参数。

    // arguments变量的写法
    
    function sortNumbers() {
    
        return Array.prototype.slice.call(arguments).sort();
    
    }
    // rest参数的写法
    
    const sortNumbers = (...numbers) => numbers.sort();

    上面是rest参数代替arguments变量的例子。比较后可发现,rest参数的写法更自然更简洁。

    arguments对象不是数组,是类似数组的对象,为使用数组方法,需先将其转为数组。Rest参数就不存在这个问题,它自身就是数组。

    // arguments变量的写法
    
    function sortNumbers() {
    
        return Array.prototype.slice.call(arguments).sort();
    
    }
    // rest参数的写法
    
    const sortNumbers = (...numbers) => numbers.sort();

    上面是利用rest参数改写数组push方法的例子。

    需注意。Rest参数之后不能再有其他参数,即只能是最后一个参数,否则报错。

    函数的length属性返回没有默认值的参数的个数,其中不包括rest参数。

    (function (a) { }).length  // 1
    
    (function (...a) { }).length  // 0
    
    (function (a, ...b) { }).length  // 1

    7、对象的扩展

    (1)Es6中可以直接写入变量和函数,作为对象的属性和方法,使书写更简洁。

    const foo = 'bar';
    
    const baz = { foo };
    
    baz // {foo: "bar"}
    // 等同于
    
    const baz = { foo: foo };

    此时,属性名为变量名,属性的值为变量的值。

    function f(x, y) {
    
        return { x, y };
    
    }
    // 等同于
    
    function f(x, y) {
    
        return { x: x, y: y
    
        };
    
    }
    
     
    
    f(1, 2) // Object {x: 1, y: 2}

    方法也可以简写。

    在一个模块对外提供接口时非常适合使用简洁写法。

    const getName = () => person.name;
    
    const getAge = () => person.age;

    commons的写法:

    module.exports = {
    
        getName: getName,
    
        getAge: getAge,
    
    };

    Es6 modules的写法:

    export default {
    
        getName,
    
        getAge
    
    };

    (2)若自适应字面量方法定义对象(使用大括号),es5中只能像下面这样定义属性:

    var obj = {
    
        foo: true,
    
        abc: 123
    
    };

    在es6中,就可以把表达式放在方括号内定义对象:

    let propKey = 'foo';
    
    let obj = {
    
        [propKey]: true,
    
        ['a' + 'bc']: 123
    
    };

    8、class

         生成实例对象的传统方法使通过构造函数,与传统的面向对象语言差异很大,es6引入class类这个概念,通过class关键字可以定义类。Es6的class可以看作是一个语法糖,它的绝大部分功能,es5都可以做到,class写法只是让对象的写法更清晰。

    es5生成实例对象:

    function Person(name, age) {
    
        this.name = name;
    
        this.age = age;
    
    }
    
    Person.prototype.getName = function () {
    
        return this.name;
    
    }
    
    var person = new Person('abc', 21);

    es6用class改写

    class Person {
    
        constructor(name, age) {
    
            this.name = name;
    
            this.age = age;
    
        }
    
     
    
        getName() {
    
            return this.name
    
        }
    
    }
    
    var person = new Person('abc', 21);

    constructor是构造方法,this代表实例对象,es5的构造函数Person,对应的就是Person类的构造方法,定义类方法使不需要function关键字,直接把函数定义放进去,另外方法之间也不需要逗号分隔。

    typeof Person // "function"
    
    Person === Person.prototype.constructor // true

    上面表明,类的数据类型就是函数,类本身事项构造函数。

    class Person {
    
        constructor() {
    
            // ...
    
        }
    
        getName() {
    
            // ...
    
        }
    
     
    
    }
    // 等同于
    
    Person.prototype = {
    
        constructor() {},
    
        getName() {}
    
    };

    类的所有方法都定义在类的prototype属性上。因此,类的新方法可以添加在prototype对象上。而Object.assign方法可以仿版地一次想类添加多个方法。

    Object.assign(Person.prototype, {
    
        getNmae() {},
    
        getAge() {}
    
    });

    9、class的继承

          1)Es6中,class可以通过extends关键字实现继承,比起es5的通过修改原型链实现继承方便的多。

    class Person {
    
        constructor(name, age) {
    
            this.name = name;
    
            this.age = age;
    
        }
    
     
    
        getName() {
    
            return this.name
    
        }
    
    }
    // Student类继承Person类
    
    class Student extends Person {
    
        constructor(name, age, gender, classes) {
    
            super(name, age);
    
            this.gender = gender;
    
            this.classes = classes;
    
        }
    
     
    
        getGender() {
    
            return this.gender;
    
        }
    
    }

    不用像es5那样需要考虑构造函数继承哈斯原型继承,只需要使用extends关键字并关注一个叫super的方法。

    如果不使用super方法,在新建实例时就会报错。

    Es5的继承,实质是县创造子类实例对象的this,在将父类的方法添加到this(Parent.apply(this))。    而es6的继承机制完全不同,实质是先创造父类的实例对象this(所以必须先调用super方法),然后再用子类构造函数修改this。

    如果子类没有定义constructor(),也会被默认添加。即任何一个子类都有constructor方法。

    class Student extends Person {}
    
     
    
    // 等同于
    
    class Student extends Person {
    
        constructor(...args) {
    
            super(...args);
    
        }
    
    }

    需要注意,只有调用super以后才能使用this,否则报错。因为上面说过,子类实例的构建,是基于父类实例加工,super方法返回父类实例。

    1) super关键字既可以当作函数使用也可以当作对象使用。

    ·Super作为函数调用时,代表父类的构造函数,子类的构造数必须执行一次super函数。

    class A {}
    
     
    
    class B extends A {
    
        constructor() {
    
            super();
    
        }
    
    }

    Super虽然代表了父类A的工业早函数,但返回的是子类B的实例,即super内部的this指的是B,因此super在这里相当于A.prototype.constructor.call(this)。

    class A {
    
        constructor() {
    
            console.log(new.target.name);
    
        }
    
    }
    
    class B extends A {
    
        constructor() {
    
            super();
    
        }
    
    }
    
    new A() // A
    
    new B() // B

    new.target指向当前正在执行的函数。从上面的代码可以看出:在super执行时,它指向的是子类B的构造函数,即super内部的this指向B

    并且,作为函数是,super只能用在子类的构造函数之中,否则报错。

    ·第二种情况,super作为对象。在普通方法中,指向父类的原型对象;在静态方法中,指向父类。

    class A {
    
        p() {
    
            return 2;
    
        }
    
    }
    
     
    
    class B extends A {
    
        constructor() {
    
            super();
    
            console.log(super.p()); // 2
    
        }
    
    }
    
     
    
    let b = new B();

    上面代码中,子类B的super.p就是把super当做对象使用,此时super在普通方法中,指向A.prototype,因此super.p()就相当于A.prototype.p()。

    注意:定义在父类实例上的方法、属性无法通过super调用。

    class A {
    
        constructor() {
    
            this.x = 1;
    
        }
    
        print() {
    
            console.log(this.x);
    
        }
    
    }
    
     
    
    class B extends A {
    
        constructor() {
    
            super();
    
            this.x = 2;
    
        }
    
        m() {
    
            super.print();
    
        }
    
    }
    
     
    
    let b = new B();
    
    b.m() // 2

    super.print()虽然调用的是A.prototype.print(),但A.prototype.print()内部的this指向子类B,因此输出2,而不是1。实际上执行的是super.print.call(this)。

    如果super作为对象,用在静态方法中,super就指向父类,而不是父类的原型对象。

    class Parent {
    
        static myMethod(msg) {
    
            console.log('static', msg);
    
        }
    
     
    
        myMethod(msg) {
    
            console.log('instance', msg);
    
        }
    
    }
    
     
    
    class Child extends Parent {
    
        static myMethod(msg) {
    
            super.myMethod(msg);
    
        }
    
     
    
        myMethod(msg) {
    
            super.myMethod(msg);
    
        }
    
    }
    
     
    
    Child.myMethod(1); // static 1
    
     
    
    var child = new Child();
    
    child.myMethod(2); // instance 2

    上面的代码,super在静态方法中指向父类,在普通方法中指向父类的原型对象。

    10、promise(项目中ajax.js中使用)

    promise的作用与回调方法(callback)一样,都是在某种情况下执行设定好的方法。但promise的多重链式调用能使代码更整洁,避免出现“回调地狱”(回调嵌套太多层)。在es6中,promise成为了原生对象可以直接使用。

    Ajax请求的传统写法:

     

    改为promise写法:

     

    很显然,promise的写法把异步调用中使用回调函数的场景改为了.then()、.catch()等函数链式调用的方式,基于promise可以把复杂的异步调用方式进行模块化。

    Promise的原理分析:

    Promise对象共有三个状态,分别是:

    ·pending(进行中)

    ·resolved(已完成,又称为fullfilled)

    ·rejected(已失败)

    由异步操作的结果决定当前是什么状态。状态的改变只有两种可能:

    ·从pending变为fullfilled

    ·从pending变为rejected

    只要这两种情况发生,状态就不会再改变了,因此状态是不能逆向改变的。

    构建promise:

     

    Promise的构造函数接受一个函数作为参数,该函数的两个参数分别为resolve和reject两个函数。

    ·Resolve函数将promise对象的状态由pending变为resolved,异步操作成功时调用,并将异步操作的结果,作为参数传递出去。

    ·reject函数将状态由pending变为rejected,异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

      Promise的实例方法:

    Promise对象拥有两个实例方法then()和catch()。

    ·then()方法接受两个函数作为参数,第一个是状态变为resolved时调用,第二个则是状态变为rejected是调用,第二个函数是可选的。

    Promise实例生成以后,可以用then方法指定resolved状态和rejected状态的回调函数,即成功和失败的回调函数。

    promise构造函数中通常都是异步的,所以then方法往往都先于resolvereject方法执行。这两个函数作为成功和失败的回调函数,都接受promise对象传出的值作为参数。

     

     

    then()方法将返回一个新的promise。

    因此then可以链式调用,在新的对象上添加失败或成功的回调。

    ·catch()方法的作用是捕获promise的错误。

    与then()方法的rejected回调作业几乎一致。

    我们知道,如果 Promise 状态已经变成resolved,再抛出错误是无效的。

     

    上面代码中,Promise 在resolve语句后面,再抛出错误,不会被捕获,等于没有抛出。因为 Promise 的状态一旦改变,就永久保持该状态,不会再变了。

     

    promise对象的错误会一直向后传递,直到被捕获,即错误总会被下一个catch所捕获。then方法指定的回调函数,若抛出错误,也会被下一个catch捕获。catch中也能抛错,则需要后面的catch来捕获。

    因此一般来说,不要在then方法里面定义 Reject 状态的回调函数(即then的第二个参数),总是使用catch方法。

    这样就能够在下一个catch()中统一处理这些错误。同时catch()也能够捕获then()中抛出的错误,所以建议不使用then()的rejected回调,而是统一使用catch()来处理错误。

    跟传统的try/catch代码块不同的是,如果没有使用catch方法指定错误处理的回调函数,Promise 对象抛出的错误不会传递到外层代码,即不会有任何反应。Promise 内部的错误不会影响到 Promise 外部的代码。

    11、Module(项目中常用)

    ES6实现了模块功能,可以取代CommonJS和AMD规范,称为浏览器和服务器通用的模块解决方案。

    模块功能主要由两个命令构成:export和import。

    Export用于规定模块的对外接口,import用于输入其他模块提供的功能。

    一个模块即一个独立文件。该文件内部的所有变量,在外部是无法获取的。如果需要读取模块内的某个变量,就必须使用export输出该变量。

    (1)export

    export可以输出变量:

    // profile.js
    
    export var firstName = 'Michael';
    
    export var lastName = 'Jackson';
    
    export var year = 1958;

    或者用大括号指定要输出的一组变量:

    // profile.js
    
    var firstName = 'Michael';
    
    var lastName = 'Jackson';
    
    var year = 1958;
    
     
    
    export { firstName, lastName, year };

    除了输出变量,还可以输出函数或class类

    export function multiply(x, y) {
    
        return x * y;
    
    };

    (2)import

    使用export定义了,模块对外的几口以后,其他js文件就可以通过import加载这个模块。

    // main.js
    
    import { firstName, lastName, year } from './profile.js';
    
     
    
    function setName(element) {
    
        element.textContent = firstName + ' ' + lastName;
    
    }

    Import接收一堆大括号,里面指定要从其他模块导入的变量名,变量名必须与被导入模块(profile.js)对外接口的名称相同。

    Import输入的变量都是只读的,不允许在加载模块的脚本里改写接口。

    From指定模块文件的位置,.js后缀可以省略。

    Import具有提升效果,会提升到整个模块的头部首先执行。

    (3)export default

    使用export default为模块指定默认输出。

    // export-default.js
    
    export default function () {
    
        console.log('foo');
    
    }

    默认输出一个匿名函数

    // import-default.js
    
    import customName from './export-default';
    
    customName(); // 'foo'

    加载该模块时,import为函数指定名字。

    需要注意的是export default对应的import不需要使用大括号{}。而export对应的import需要使用大括号。

  • 相关阅读:
    (Windows)VMware虚拟机安装Linux系统
    C++调用HNSW实现图像配准
    (Windows) OpenCV 3.1.0 + opencv_contrib编译
    (Windows)VS2017下编译FLANN1.8.4
    C++读写txt
    数据类型转换
    tensorflow模型的保存与恢复,以及ckpt到pb的转化
    Linux系统安装MySql步骤及截屏
    实时多项式拟合
    Linux系统安装NoSQL(MongoDB和Redis)步骤及问题解决办法
  • 原文地址:https://www.cnblogs.com/zichil/p/8467068.html
Copyright © 2011-2022 走看看