zoukankan      html  css  js  c++  java
  • es6学习-201803

    1、ES6 规定暂时性死区和letconst语句不出现变量提升,主要是为了减少运行时错误,防止在变量声明前就使用这个变量,从而导致意料之外的行为。这样的错误在 ES5 是很常见的,现在有了这种规定,避免此类错误就很容易了。

    2、变量的解构赋值

    数组

    对象

    3、扩展运算符(...

    //数组
    const [a, ...b] = [1, 2, 3];
    a // 1
    b // [2, 3]
    
    //对象
    let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
    x // 1
    y // 2
    z // { a: 3, b: 4 }

    4、函数的扩展

    1)函数参数的默认值

     

    5、对象的扩展

    ES6 允许在对象之中,直接写变量。这时,属性名为变量名, 属性值为变量的值。

    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 o = {
      method() {
        return "Hello!";
      }
    };
    // 等同于
    const o = {
      method: function() {
        return "Hello!";
      }
    };

     6、Promise

    Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数

    1)ES6 规定,Promise对象是一个构造函数,用来生成Promise实例。Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolvereject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”,在异步操作成功时调用,并将异步操作的结果作为参数传递出去;reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”,在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

    const promise = new Promise(function(resolve, reject) {
      if (/* 异步操作成功 */){
        resolve(value);
      } else {
        reject(error);
      }
    });

    2)Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。

    promise.then(function(value) {
      // success
    }, function(error) {
      // failure
    });

    3)Promise 实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。可以采用链式写法,即then方法后面再调用另一个then方法。采用链式的then,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用。

    getJSON("/post/1.json").then(
      post => getJSON(post.commentURL)
    ).then(
      comments => console.log("resolved: ", comments),  //funcA
      err => console.log("rejected: ", err)  //funcB
    );
    /*
    第一个then方法指定的回调函数,返回的是另一个Promise对象。这时,第二个then方法指定的回调函数,就会等待这个新的Promise对象状态发生变化。
    如果变为resolved,就调用funcA,如果状态变为rejected,就调用funcB。
    */

    4)Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。

    getJSON('/posts.json').then((val) => console.log('fulfilled:', val))
    .catch((err) => console.log('rejected', err));
    /*
    getJSON方法返回一个 Promise 对象,如果该对象状态变为resolved,则会调用then方法指定的回调函数;
    如果异步操作抛出错误,状态就会变为rejected,就会调用catch方法指定的回调函数,处理这个错误。
    另外,then方法指定的回调函数,如果运行中抛出错误,也会被catch方法捕获。
    */

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

    // bad
    promise
      .then(function(data) {
        // success
      }, function(err) {
        // error
      });
    
    // good
    promise
      .then(function(data) {
        // success
      })
      .catch(function(err) {
        // error
      });
    /*
    第二种写法要好于第一种写法,理由是第二种写法可以捕获前面then方法执行中的错误,也更接近同步的写法(try/catch)。
    因此,建议总是使用catch方法,而不使用then方法的第二个参数。
    */

    一般总是建议,Promise 对象后面要跟catch方法,这样可以处理 Promise 内部发生的错误。catch方法返回的还是一个 Promise 对象,因此后面还可以接着调用then方法。

    const someAsyncThing = function() {
      return new Promise(function(resolve, reject) {
        // 下面一行会报错,因为x没有声明
        resolve(x + 2);
      });
    };
    someAsyncThing()
    .catch(function(error) {
      console.log('oh no', error);
    })
    .then(function() {
      console.log('carry on');
    });
    // oh no [ReferenceError: x is not defined]
    // carry on
    
    /*上面代码运行完catch方法指定的回调函数,会接着运行后面那个then方法指定的回调函数。如果没有报错,则会跳过catch方法。*/
    Promise.resolve()
    .catch(function(error) {
      console.log('oh no', error);
    })
    .then(function() {
      console.log('carry on');
    });
    // carry on

    7、模块Module

    模块功能主要由两个命令构成:exportimportexport命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。

    1)export命令

    a、一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量。

    export var firstName = 'Michael';
    export var lastName = 'Jackson';
    export var year = 1958;
    
    //等价于
    var firstName = 'Michael';
    var lastName = 'Jackson';
    var year = 1958;
    
    export {firstName, lastName, year};
    /*优先考虑使用这种写法。因为这样就可以在脚本尾部,一眼看清楚输出了哪些变量。*/

    export命令除了输出变量,还可以输出函数或类(class)。

    b、export命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。

    // 错误写法
    export 1;
    var m = 1;export m; 
    function f() {};export f;
    
    // 正确写法
    export var m = 1;
    var m = 1;export {m};
    export function f() {};
    function f() {};export {f};

    c、通常情况下,export输出的变量就是本来的名字,但是可以使用as关键字重命名。(import命令类似)

    function v1() { ... }
    function v2() { ... }
    
    export {
      v1 as streamV1,
      v2 as streamV2
    };

    2)import 命令

    a、使用export命令定义了模块的对外接口以后,其他 JS 文件就可以通过import命令加载这个模块。

    import {firstName, lastName, year} from './profile.js'; //加载profile.js文件,并从中输入变量

    import命令接受一对大括号,里面指定要从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块(profile.js)对外接口的名称相同(建议凡是输入的变量,都当作完全只读,轻易不要改变它的属性)。import后面的from指定模块文件的位置,可以是相对路径,也可以是绝对路径,.js后缀可以省略。

    b、import语句会执行所加载的模块。

    import 'lodash';  //仅仅执行lodash模块,但是不输入任何值

    目前阶段,通过 Babel 转码,CommonJS 模块的require命令和 ES6 模块的import命令,可以写在同一个模块里面,但是最好不要这样做。因为import在静态解析阶段执行,所以它是一个模块之中最早执行的。

    c、模块的整体加载

    除了指定加载某个输出值,还可以使用整体加载,即用星号(*)指定一个对象,所有输出值都加载在这个对象上面。

    // circle.js
    export function area(radius) {
      return Math.PI * radius * radius;
    }
    export function circumference(radius) {
      return 2 * Math.PI * radius;
    }
    
    // xxx.js
    //1、逐一指定要加载的方法
    import { area, circumference } from './circle';
    console.log('圆面积:' + area(4));
    console.log('圆周长:' + circumference(14));
    
    //2、整体加载
    import * as circle from './circle';
    console.log('圆面积:' + circle.area(4));
    console.log('圆周长:' + circle.circumference(14));

    3)export default 命令

    a、使用import命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载。export default命令能为模块指定默认输出。

    // export-default.js  默认输出是一个函数
    export default function () {
      console.log('foo');
    }
    // import-default.js
    // 其他模块加载该模块时,import命令可以为该匿名函数指定任意名字。这时import命令后面,不使用大括号。
    import customName from './export-default';
    customName(); // 'foo'

    export default命令用于指定模块的默认输出。显然,一个模块只能有一个默认输出,因此export default命令只能使用一次。所以,import命令后面才不用加大括号,因为只可能唯一对应export default命令。

    b、export default命令用在非匿名函数前,也是可以的。

    export default function foo() {
      console.log('foo');
    }
    // 或者写成
    function foo() {
      console.log('foo');
    }
    export default foo;

    c、因为export default命令其实只是输出一个叫做default的变量,所以它后面不能跟变量声明语句。

    export var a = 1;// 正确
    var a = 1;export default a;// 正确
    export default var a = 1;// 错误
    /*上面代码中,export default a的含义是将变量a的值赋给变量default。所以,最后一种写法会报错*/
    
    export default 42;// 正确
    export 42;// 报错
    /*
    同样地,因为export default命令的本质是将后面的值,赋给default变量,所以可以直接将一个值写在export default之后。
    上面代码中,后一句报错是因为没有指定对外的接口,而前一句指定外对接口为default。
    */

    4)export 与 import 的复合写法

    a、如果在一个模块之中,先输入后输出同一个模块,import语句可以与export语句写在一起。

    export { foo, bar } from 'my_module';
    // 可以简单理解为
    import { foo, bar } from 'my_module';
    export { foo, bar };
    /*
    上面代码中,export和import语句可以结合在一起,写成一行。但需要注意的是,写成一行以后,foo和bar实际上并没有被导入当前模块,
    只是相当于对外转发了这两个接口,导致当前模块不能直接使用foo和bar*/

    b、模块的接口改名和整体输出,也可以采用这种写法。

    // 接口改名
    export { foo as myFoo } from 'my_module';
    // 整体输出
    export * from 'my_module';//会忽略my_module模块的default方法
    // 默认接口
    export { default } from 'foo';
    // 默认接口也可以改名为具名接口
    export { default as es6 } from './someModule';

    4)跨模块常量

    const声明的常量只在当前代码块有效。如果想设置跨模块的常量(即跨多个文件),或者说一个值要被多个模块共享,可以采用下面的写法。

    // constants.js 模块
    export const A = 1;
    export const B = 3;
    export const C = 4;
    
    // test1.js 模块
    import * as constants from './constants';
    console.log(constants.A); // 1
    console.log(constants.B); // 3
    
    // test2.js 模块
    import {A, B} from './constants';
    console.log(A); // 1
    console.log(B); // 3

     5)import()函数

    import命令会被 JavaScript 引擎静态分析,先于模块内的其他语句执行。

    if (x === 2) {// 报错
      import MyModual from './myModual';
    }
    /*
    上面代码中,引擎处理import语句是在编译时,这时不会去分析或执行if语句,所以import语句放在if代码块之中毫无意义,因此会报句法错误,而不是执行时错误。
    也就是说,import和export命令只能在模块的顶层,不能在代码块之中(比如,在if代码块之中,或在函数之中)。
    */

    这样的设计,固然有利于编译器提高效率,但也导致无法在运行时加载模块。在语法上,条件加载就不可能实现。如果import命令要取代 Node 的require方法,这就形成了一个障碍。因为require是运行时加载模块,import命令无法取代require的动态加载功能

    const path = './' + fileName;
    const myModual = require(path);
    /*上面的语句就是动态加载,require到底加载哪一个模块,只有运行时才知道*/

    a、有一个提案,建议引入import()函数,完成动态加载。import(specifier):import函数的参数specifier,指定所要加载的模块的位置。

    import()函数可以用在任何地方,不仅仅是模块,非模块的脚本也可以使用。它是运行时执行,也就是说,什么时候运行到这一句,就会加载指定的模块import()类似于 Node 的require方法,区别主要是前者是异步加载,后者是同步加载

    b、import()的一些适用场合

    (1)按需加载:import()可以在需要的时候,再加载某个模块。

    button.addEventListener('click', event => {
      import('./dialogBox.js')
      .then(dialogBox => {
        dialogBox.open();
      })
      .catch(error => {
        /* Error handling */
      })
    });
    /*上面代码中,import()方法放在click事件的监听函数之中,只有用户点击了按钮,才会加载这个模块*/

    (2)条件加载:import()可以放在if代码块,根据不同的情况,加载不同的模块。

    if (condition) {
      import('moduleA').then(...);
    } else {
      import('moduleB').then(...);
    }
    /*上面代码中,如果满足条件,就加载模块 A,否则加载模块 B*/

    (3)动态的模块路径:import()允许模块路径动态生成。

    import(f()).then(...);
    /*上面代码中,根据函数f的返回结果,加载不同的模块*/

    c、注意点

    import()加载模块成功以后,这个模块会作为一个对象,当作then方法的参数。因此,可以使用对象解构赋值的语法,获取输出接口。

    import('./myModule.js')
    .then(({export1, export2}) => {
      // ...·
    });
    /*上面代码中,export1和export2都是myModule.js的输出接口,可以解构获得*/
    
    // 如果模块有default输出接口,可以用参数直接获得。
    import('./myModule.js')
    .then(myModule => {
      console.log(myModule.default);
    });

    8、ES6 模块与 CommonJS 模块的差异(Node 应用由模块组成,采用 CommonJS 模块规范)

    1)它们有两个重大差异。

    • CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
    • CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。

    第二个差异是因为 CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。

    // lib.js
    export let obj = {};
    
    // main.js
    import { obj } from './lib';
    obj.prop = 123; // OK
    obj = {}; // TypeError
    /*上面代码中,main.js从lib.js输入变量obj,可以对obj添加属性,但是重新赋值就会报错。因为变量obj指向的地址是只读的,不能重新赋值*/

    export通过接口,输出的是同一个值。不同的脚本加载这个接口,得到的都是同样的实例

    vue:当一个组件被定义,data 必须声明为返回一个初始数据对象的函数,因为组件可能被用来创建多个实例。如果 data 仍然是一个纯粹的对象,则所有的实例将共享引用同一个数据对象!通过提供 data 函数,每次创建一个新实例后,我们能够调用 data 函数,从而返回初始数据的一个全新副本数据对象。

    2)import后面的from指定模块文件的位置,可以是相对路径,也可以是绝对路径,.js后缀可以省略。如果只是模块名,不带有路径,那么必须有配置文件,告诉 JavaScript 引擎该模块的位置。如果模块名不含路径,那么import命令会去node_modules目录寻找这个模块

    3)CommonJS—module.exports属性

    module.exports属性表示当前模块对外输出的接口,其他文件加载该模块,实际上就是读取module.exports变量。为了方便,Node为每个模块提供一个exports变量,指向module.exports。这等同在每个模块头部,有一行这样的命令—var exports = module.exports。造成的结果是,在对外输出模块接口时,可以向exports对象添加方法。

    exports.area = function (r) {
      return Math.PI * r * r;
    };

    注意,不能直接将exports变量指向一个值,因为这样等于切断了exportsmodule.exports的联系。

    exports = function(x) {console.log(x)};
    // 上面这样的写法是无效的,因为exports不再指向module.exports了。
    
    // 下面的写法也是无效的。
    // hello函数是无法对外输出的,因为module.exports被重新赋值了
    exports.hello = function() {
      return 'hello';
    };
    module.exports = 'Hello world';

    这意味着,如果一个模块的对外接口,就是一个单一的值,不能使用exports输出,只能使用module.exports输出。如果你觉得,exportsmodule.exports之间的区别很难分清,一个简单的处理方法,就是放弃使用exports,只使用module.exports

    module.exports = function (x){ console.log(x);};

    9、字符串的扩展—模板字符串

    模板字符串是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。模板字符串中嵌入变量,需要将变量名写在${}之中。

    $('#result').append(
      'There are <b>' + basket.count + '</b> ' +
      'items in your basket, ' +
      '<em>' + basket.onSale +
      '</em> are on sale!'
    );
    // 上面这种写法相当繁琐不方便,ES6 引入了模板字符串解决这个问题。
    $('#result').append(`
      There are <b>${basket.count}</b> items
       in your basket, <em>${basket.onSale}</em>
      are on sale!
    `);

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

    console.log(`string text line 1
    string text line 2`);
    /*
    string text line 1
    string text line 2
    */
    $('#list').html(`
    <ul>
      <li>first</li>
      <li>second</li>
    </ul>
    `);
    /*上面代码中,所有模板字符串的空格和换行,都是被保留的,比如<ul>标签前面会有一个换行。如果你不想要这个换行,可以使用trim方法消除它。*/
    $('#list').html(`
    <ul>
      <li>first</li>
      <li>second</li>
    </ul>
    `.trim());

    10、编程风格

    1)块级作用域

    a、let 取代 var:var命令存在变量提升效用,let命令没有这个问题

    b、全局常量

    letconst之间,建议优先使用const,尤其是在全局环境,不应该设置变量,只应设置常量。

    const优于let有几个原因。一个是const可以提醒阅读程序的人,这个变量不应该改变;另一个是const比较符合函数式编程思想,运算不改变值,只是新建值,而且这样也有利于将来的分布式运算;最后一个原因是 JavaScript 编译器会对const进行优化,所以多使用const,有利于提高程序的运行效率,也就是说letconst的本质区别,其实是编译器内部的处理不同。

    // bad
    var a = 1, b = 2, c = 3;
    // good
    const a = 1;
    const b = 2;
    const c = 3;
    // best
    const [a, b, c] = [1, 2, 3];

    const声明常量还有两个好处,一是阅读代码的人立刻会意识到不应该修改这个值,二是防止了无意间修改变量值所导致的错误。所有的函数都应该设置为常量。

    2)静态字符串一律使用单引号或反引号,不使用双引号。动态字符串使用反引号。

    // bad
    const a = "foobar";
    const b = 'foo' + a + 'bar';
    // acceptable
    const c = `foobar`;
    // good
    const a = 'foobar';
    const b = `foo${a}bar`;

    3)解构赋值

    a、使用数组成员对变量赋值时,优先使用解构赋值。函数的参数如果是对象的成员,优先使用解构赋值。

    const arr = [1, 2, 3, 4];
    // bad
    const first = arr[0];
    const second = arr[1];
    // good
    const [first, second] = arr;
    
    // bad
    function getFullName(user) {
      const firstName = user.firstName;
      const lastName = user.lastName;
    }
    // good
    function getFullName(obj) {
      const { firstName, lastName } = obj;
    }
    // best
    function getFullName({ firstName, lastName }) {
    }

    b、如果函数返回多个值,优先使用对象的解构赋值,而不是数组的解构赋值。这样便于以后添加返回值,以及更改返回值的顺序。

    // bad
    function processInput(input) {
      return [left, right, top, bottom];
    }
    // good
    function processInput(input) {
      return { left, right, top, bottom };
    }
    const { left, right } = processInput(input);

    4)对象

    对象的属性和方法,尽量采用简洁表达法,这样易于描述和书写。

    var ref = 'some value';
    // bad
    const atom = {
      ref: ref,
      value: 1,
      addValue: function (value) {
        return atom.value + value;
      },
    };
    // good
    const atom = {
      ref,
      value: 1,
      addValue(value) {
        return atom.value + value;
      },
    };

    5)数组

    使用扩展运算符(...)拷贝数组。

    // bad
    const len = items.length;
    const itemsCopy = [];
    let i;
    for (i = 0; i < len; i++) {
      itemsCopy[i] = items[i];
    }
    // good
    const itemsCopy = [...items];

    6)函数

    a、不要在函数体内使用 arguments 变量,使用 rest 运算符(...)代替。因为 rest 运算符显式表明你想要获取参数,而且 arguments 是一个类似数组的对象,而 rest 运算符可以提供一个真正的数组。

    // bad
    function concatenateAll() {
      const args = Array.prototype.slice.call(arguments);
      return args.join('');
    }
    // good
    function concatenateAll(...args) {
      return args.join('');
    }

    b、使用默认值语法设置函数参数的默认值。

    // bad
    function handleThings(opts) {
      opts = opts || {};
    }
    // good
    function handleThings(opts = {}) {
      // ...
    }

    7)模块

    Module 语法是 JavaScript 模块的标准写法,坚持使用这种写法。使用import取代require,使用export取代module.exports

    // bad
    const moduleA = require('moduleA');
    const func1 = moduleA.func1;
    const func2 = moduleA.func2;
    // good
    import { func1, func2 } from 'moduleA';
    
    // commonJS的写法
    var React = require('react');
    var Breadcrumbs = React.createClass({
      render() {
        return <nav />;
      }
    });
    module.exports = Breadcrumbs;
    // ES6的写法
    import React from 'react';
    class Breadcrumbs extends React.Component {
      render() {
        return <nav />;
      }
    };
    export default Breadcrumbs;
  • 相关阅读:
    python依赖包整体迁移方法
    ubuntu关于ssh协议登录问题
    k8s部署02-----kubeadm部署k8s
    k8s部署01-----what is k8s?
    兼容到ie10的js文件导出、下载到本地
    webstorm减少内存占用
    foxmail占cpu 100%解决办法
    原生js返回顶部
    js字符串驼峰和下划线互相转换
    element-ui的rules中正则表达式
  • 原文地址:https://www.cnblogs.com/colorful-coco/p/8442679.html
Copyright © 2011-2022 走看看