• 关于 ES6 / ES7


     写在前面

    0.1 JavaScript 语句的自动分号插入

    一条语句以“(”、“[”、“/”、“+”或“-”开始,那么它极有可能和前一条语句合在一起解析。保守起见,可以在语句前加上一个分号以保证程序的正确执行;

    var a = 0   // 这里省略了分号
    
    ;[a, a+1, a+2].forEach(console.log) // 前面的分号保证了正确地语句解析

    0.2 闭包( closure ) 

    函数内部形成一个局部作用域,定义在这个局部作用域上的内部函数,会持有对这个原始作用域的引用,只要在外部作用域上执行这个内部函数并访问原始作用域,就会形成闭包。

    0.3 this 判断优先级

    (1) 函数在 new 中调用,this 绑定的是新创建的实例化对象。

    var bar = new foo()          // this 指向 bar

    (2) 函数通过 call、apply 绑定调用(显式绑定),this 绑定的是指定的对象

    var bar = foo.call(obj_other)      // this 指向obj

    (3)函数在某个上下文对象中调用(隐式绑定),this 绑定的是上下文对象

    obj.foo()            // this 指向 obj

    (4)否则,属于默认绑定,this 绑定的是全局对象(严格模式下为undefined)

    var bar = obj.foo;
    bar()                         // this 指向全局对象

     0.4 new 运算符

      假设有一个构造函数 Base,对其实例化一个对象 obj : 

    var obj = new Base()

      new 关键字会进行如下的操作:

    //  1. 创建一个对象
    var obj={};
    
    //  2. 将这个空对象 obj 的 __proto__ 指向了 Base 函数对象 prototype 成员对象
    obj.__proto__ = Base.prototype;
    
    //  3. 将 Base 函数对象的 this 指针替换成 obj,然后再调用Base函数。即:改变构造函数的 this 指向,来给 obj 添加属性和方法
    Base.call(obj);

    1. async / await

    async 确保了函数的返回值是一个 promise,也会包装非 promise 的值。

    // 常规函数
    function foo(){
      return 123
    }
    foo()       // 123
    
    // 加限定词 async
    async function foo(){
      return 123
    }
    foo()       // Promise {<resolved>: 123}
    
    // 显式返回一个 promise
    async function foo(){
      return Promise.resolve(123);
    }
    foo()       // Promise {<resolved>: 123}

    await 让 JavaScript 引擎等待直到 promise 完成 ( * ) 并返回结果 

    async function ff() {
    
      let promise = new Promise( resolve=> {
        setTimeout(() => resolve(456), 1000)
      });
    
      let result = await promise;   // 等待直到 promise 决议 并返回 resolve 的值 (*)
    
      console.log(result);          // 456
    }
    
    ff();

    相比 promise.then 来获取 promise 结果,这只是一个更优雅的语法,同时也更可读和更易书写。

    如果 promise 被拒绝(rejected),就会抛出一个错误,就像在那一行有个  throw  语句那样。可以用 try..catch 来捕获上面的错误,或者我们也可以在函数调用后添加 .catch 来处理错误:

    // try... catch...
    async function f() {
      try {
        let response = await fetch('http://url');
      } catch(err) {
        console.log(err);                // TypeError: failed to fetch
      }
    }
    f();
    
    // .catch
    async function f() {
      let response = await fetch('http://url');
    }
    f().catch(console.log);            // TypeError: failed to fetch 

    2. 箭头函数

      函数体内的 this 对象,就是定义时所在的对象,而不是使用时所在的对象。这意味着 this 和 arguments 都是从它们的父函数继承而来的。如下:

    const func = {
      name: 'example',
      anonFunction: function() {
        return function() {
          console.log('--------anon-------');
          console.log(this);
          console.log(this.name);
          console.log(arguments);
        };
      },
     
      arrowFunction: function() {
        return () => {
          console.log('-------arrow--------');
          console.log(this);
          console.log(this.name);
          console.log(arguments);
        };
      }
    };
    
    const anon = func.anonFunction('hello', 'world');
    const arrow = func.arrowFunction('hello', 'world');
    
    anon()
    // Window {parent: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …}
    // 
    // Arguments [callee: ƒ, Symbol(Symbol.iterator): ƒ]
    
    anon('other')
    // Window {parent: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …}
    // 
    // Arguments ["other", callee: ƒ, Symbol(Symbol.iterator): ƒ]
    
    arrow()
    arrow('other')
    // {name: "example", anonFunction: ƒ, arrowFunction: ƒ}
    // example
    // Arguments(2) ["hello", "world", callee: ƒ, Symbol(Symbol.iterator): ƒ]

      与其他形式的函数不同,箭头函数没有自己的执行期上下文。this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。正是因为它没有this,所以也就不能用作构造函数。

    3. 解构赋值

       3.1 数组解构

    let [a, b, c] = [1, 2, 3];        // 从数组中提取值,按照对应位置,对变量赋值
    
    /** 不完全匹配 **/
    let [head, ...tail] = [1, 2, 3, 4];
    head        // 1
    tail                // [2, 3, 4] 
    
    let [x, , y] = [1, 2, 3];
    x     // 1
    y        // 3
    
    /** 添加默认值 **/
    let [i, j = 'b'] = ['a'];          // i='a', j='b' 
    
    /** 交换值 **/
    let [m, n] = [1, 2];
    [m, n] = [n, m];
    m    // 2
    n      // 1

      3.2  对象解构

      对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。形式如下:

    let { foo: foo, bar: bar } = { foo: 'aaa', bar: 'bbb' };

      对象的属性没有次序,变量必须与属性同名,才能取到正确的值。

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

      若想重写变量名

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

      可以用于多层嵌套结构的对象

    let obj = {
      p: [
        'Hello',
        { y: 'World' }
      ]
    };
    let { p: [x, { y }] } = obj;
    
    x // "Hello"
    y // "World"

      可用于函数的默认值解构

    function fetch(url, { body = '', method = 'GET', headers = {} } = {}) {
      console.log(method);
    }
    
    fetch('http://example.com')
    // "GET"

    4. 数组、对象 的扩展

      4.1 新增数组方法

      Array.of() 用于将一组值,转换为数组

    Array.of(3, 11, 8)   // [3,11,8]
    Array.of(3)          // [3]
    Array.of(3).length   // 1

      entries(),keys() 和 values() 都返回一个遍历器对象,可用  for...of  循环进行遍历。keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历。

    for (let index of ['a', 'b'].keys()) {
      console.log(index);
    }
    // 0
    // 1
    
    for (let elem of ['a', 'b'].values()) {
      console.log(elem);
    }
    // 'a'
    // 'b'
    
    for (let [index, elem] of ['a', 'b'].entries()) {
      console.log(index, elem);
    }
    // 0 "a"
    // 1 "b"

      4.2 对象新增方法

      Object.assign  用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target),并返回目标对象。第一个参数是目标对象,后面的参数都是源对象,后面的属性会覆盖前面的属性。实行的是浅拷贝

    const target = { a: 1, b: 1 };
    
    const source1 = { b: 2, c: 2 };
    const source2 = { c: 3 };
    
    Object.assign(target, source1, source2);
    target // {a:1, b:2, c:3}

      Object.keys(),Object.values(),Object.entries()  同数组,keys()是对键名的遍历、values()是对键值的遍历,entries()是对键值对的遍历

      4.3 扩展运算符

      结构为( ... ),可以将一个数组转为用逗号分隔的参数序列,

    console.log(1, ...[2, 3, 4], 5)
    // 1 2 3 4 5

      也可作用于 Object

    let z = { a: 3, b: 4 };
    let n = { ...z };
    n     // { a: 3, b: 4 }

    5. Promise

      then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。

    new Promise((resolve,reject)=>{
      resolve(123)
    }).then((res)=>{ 
      console.log(res);
      return res 
    }).then((res)=>{
      console.log(res)
    })
    
    // 123
    // 123
    
    // 甚至可以这样
    Promise.resolve()
    .then(_=>{console.log('ok')}) .then(_=>{console.log('ok')}) .then(_=>{console.log('ok')}) .then(_=>{console.log('ok')}) .then(_=>{console.log('ok')}) .then(_=>{console.log('ok')})

      .catch 方法是 .then(null, rejection) .then(undefined, rejection) 的别名。Promise 对象后面的 catch 方法,可以处理 Promise 内部,以及 catch 前面的 then 方法内发生的错误。catch方法返回的还是一个 Promise 对象,因此后面还可以接着调用then方法。运行完catch方法指定的回调函数,会接着运行后面那个then方法指定的回调函数。如果没有报错,则会跳过catch方法。

    Promise.resolve().catch((error)=> {
      console.log('catch:', error);
    }).then(()=> {
      console.log('carry on');
    });
    // carry on

      finally 方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。

    6. Class

     ES6 的 class 只是一个语法糖,它的绝大部分功能,ES5 都可以做到。本质是使用 es5 的构造函数来模拟 class 类。

    // class 声明
    class Point {
      constructor(x, y) {
        this.x = x;
        this.y = y;
      }
      toString() {
        return '(' + this.x + ', ' + this.y + ')';
      }
    }
    
    /**
     *  等同于:
     * 
     * function Point(x, y) {
     *   this.x = x;
     *   this.y = y;
     * }
     * Point.prototype.toString = function () {
     *   return '(' + this.x + ', ' + this.y + ')';
     * };
     * 
     */ 

    因此,以上代码中,可以直接通过原型链方式,给 Point 添加 类方法

    class Point {
      // ...
    }
    
    Point.prototype.join = function () {
      console.log(this.x + '__' + this.y);
    };
    
    let p = new Point(1,2)
    p.join()                // 1__2

    constructor 方法是类的默认方法,通过 new 命令生成对象实例时,自动调用该方法。默认返回实例对象(即this),也可以指定返回另外一个对象。

    在类内部的方法前面,加上 static 关键字,表示该方法为静态方法,不会被实例继承,而是直接通过类来调用。

    class Foo {
      static func() {
        return 'hello';
      }
    }
    
    Foo.func()                // 'hello'
    
    var foo = new Foo();
    foo.func()                // TypeError: foo.funcis not a function

    可以通过 extends 关键字实现 class类 的继承。父类的静态方法,也会被子类继承。

    class ColorPoint extends Point {
      constructor(x, y, color) {
        super(x, y);         // 调用父类的constructor(x, y)
        this.color = color;
      }
      toString() {
        return this.color + ' ' + super.toString();     // 调用父类的toString()
      }
    }

    子类自己的 this 对象,必须先通过父类的构造函数来构造,然后才能加上子类自己的实例属性和方法。super 关键字,表示父类的构造函数,用来新建父类的this对象。因此,子类必须在 constructor 方法中调用super方法,不调用super方法,子类就得不到this对象,新建实例时会报错。

    class Parent {}
    class Child extends Parent {
      constructor(){}
    }
    new Child()
    // ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor

    需要注意的是,如果子类没有定义 constructor 方法,这个方法会被默认添加

    class Parent {}
    class Child extends Parent {
      // ...
    }
    new Child()
    
    // 这段代码没有报错,因为以上操作等同于:
    
    class Parent {}
    class Child extends Parent {
      constructor(...args){
        super(...args);
      }
    }
    new Child()

    同样,子类的构造函数中,只有调用super之后,才可以使用 this 关键字,否则会报错。这是因为子类实例的构建,基于父类实例,只有 super 方法才能调用父类实例。

    super 可用作函数调用,也可作为对象使用

    (1)super 作为函数调用时,代表父类的构造函数,只能用在子类的构造函数之中。super 虽然代表了父类的构造函数,但是返回的是子类的实例;

    (2)super 作为对象时,在普通方法中,指向父类的原型对象,但是无法调用父类实例上的方法或属性( 例如 this.xx 方式定义);在静态方法中,指向父类。

    7. Proxy

  • 相关阅读:
    HTML撑起浮动子元素得父元素高度
    H5弃用标签和属性
    HTML常用转义字符
    php微信公众号开发入门
    常见正则表达式总结
    解决上下两个相邻图片之间存在默认间距的问题
    移动端真机调试的两种方法
    H5使用小结
    CF 11D
    Codeforces Round #639 (Div. 2) C Hilbert's Hotel (数学)
  • 原文地址:https://www.cnblogs.com/_error/p/11357590.html
Copyright © 2020-2023  润新知