• JavaScript 模块体系






    CommonJS

    用于服务器



    AMD

    用于浏览器



    ES6 Module 的语法

    太长不看版:ES6 Module export命令 和 import命令

    ES6 的模块自动采用严格模式,不管你有没有在模块头部加上"use strict";

    ES6 模块之中,顶层的this指向undefined,即不应该在顶层代码使用this


    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 };
    

    写法三:

    function v1() { ... }
    function v2() { ... }
    
    export {
      v1 as streamV1,
      v2 as streamV2,
      v2 as streamLatestVersion // 重命名后,v2可以用不同的名字输出两次
    };
    

    错误写法:

    // export命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。
    
    // 报错
    export 1; // 没有提供对外的接口。直接输出 1
    // 正确
    export var m = 1;
    
    // 报错
    var m = 1;
    export m; // 没有提供对外的接口。通过变量m,还是直接输出 1
    // 正确
    var m = 1;
    export {m};
    
    // 报错
    function f() {}
    export f;
    // 正确
    export function f() {};
    // 正确
    function f() {}
    export {f};
    

    *注意:

    一、动态绑定关系

    export语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值。

    export var foo = 'bar';
    setTimeout(() => foo = 'hello', 500);
    

    上面代码输出变量foo,值为bar,500 毫秒之后变成hello

    这一点与 CommonJS 规范完全不同。CommonJS 模块输出的是值的缓存,不存在动态更新,详见《Module 的加载实现》一节。

    二、处于模块顶层

    export命令可以出现在模块的任何位置,只要处于模块顶层就可以。如果处于块级作用域内,就会报错,下一节的import命令也是如此。这是因为处于条件代码块之中,就没法做静态优化了,违背了 ES6 模块的设计初衷。

    function foo() {
      export default 'bar' // SyntaxError
    }
    foo()
    

    import 命令

    import命令用于输入其他模块提供的功能。

    写法一:

    import { lastName } from './profile.js';
    

    写法二:重命名

    import { lastName as surname } from './profile.js';
    

    写法三:整体加载

    // circle.js
    
    export function area(radius) {
      return Math.PI * radius * radius;
    }
    
    export function circumference(radius) {
      return 2 * Math.PI * radius;
    }
    

    现在,加载这个模块。

    import * as circle from './circle';
    
    console.log('圆面积:' + circle.area(4));
    console.log('圆周长:' + circle.circumference(14));
    

    注意,模块整体加载所在的那个对象(上例是circle),应该是可以静态分析的,所以不允许运行时改变。下面的写法都是不允许的。

    import * as circle from './circle';
    
    circle.foo = 'hello'; // 不允许
    circle.area = function () {};  // 不允许
    

    *注意:

    一、只读特点

    import命令输入的变量都是只读的,因为它的本质是输入接口。也就是说,不允许在加载模块的脚本里面,改写接口。

    import {a} from './xxx.js'
    
    a = {}; // Syntax Error : 'a' is read-only;
    

    但是,如果a是一个对象,改写a的属性是允许的。

    import {a} from './xxx.js'
    
    a.foo = 'hello'; // 合法操作
    

    上面代码中,a的属性可以成功改写,并且其他模块也可以读到改写后的值。不过,这种写法很难查错,建议凡是输入的变量,都当作完全只读,不要轻易改变它的属性。

    二、提升效果

    foo();
    
    import { foo } from 'my_module';
    

    上面的代码不会报错,因为import的执行早于foo的调用。这种行为的本质是,import命令是编译阶段执行的,在代码运行之前。

    三、静态执行

    由于import是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构。

    // 报错
    import { 'f' + 'oo' } from 'my_module';
    
    // 报错
    let module = 'my_module';
    import { foo } from module;
    
    // 报错
    if (x === 1) {
      import { foo } from 'module1';
    } else {
      import { foo } from 'module2';
    }
    

    上面三种写法都会报错,因为它们用到了表达式、变量和if结构。在静态分析阶段,这些语法都是没法得到值的。

    四、import语句会执行所加载的模块,因此可以有下面的写法

    import 'lodash';
    

    五、多次重复执行同一句import语句,那么只会执行一次,而不会执行多次。

    import 'lodash';
    import 'lodash';
    

    上面代码加载了两次lodash,但是只会执行一次。

    import { foo } from 'my_module';
    import { bar } from 'my_module';
    
    // 等同于
    import { foo, bar } from 'my_module';
    

    上面代码中,虽然foobar在两个语句中加载,但是它们对应的是同一个my_module模块。也就是说,import语句是 Singleton 模式。

    六、CommonJS 模块的require命令和 ES6 模块的import命令最好不要写在同一个模块里

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

    require('core-js/modules/es6.symbol');
    require('core-js/modules/es6.promise');
    import React from 'React';
    

    export default 命令

    使用import命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载。但是,用户肯定希望快速上手,未必愿意阅读文档,去了解模块有哪些属性和方法。

    为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default命令,为模块指定默认输出。

    写法:

    // export-default.js
    // 写法一、匿名函数
    export default function () {
      console.log('foo');
    }
    
    // 写法二、非匿名直接导出
    export default function foo() {
      console.log('foo');
    }
    
    // 写法三、非匿名变量导出
    function foo() {
      console.log('foo');
    }
    
    export default foo;
    
    // import-default.js
    import customName from './export-default';
    customName(); // 'foo'
    
    • import命令可以为该匿名函数指定任意名字。

    • import命令后面,不使用大括号。

    • 一个模块只能有一个默认输出,因此export default命令只能使用一次、也因此命令后面才不用加大括号。

      export default命令本质(传送门:https://es6.ruanyifeng.com/#docs/module#export-default-命令)



    区别

    1. ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。ES6 模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。


    参考

    https://es6.ruanyifeng.com/#docs/module

  • 相关阅读:
    Conv2 GPU加速(有代码有图有真相)
    OpenACC指令适不适合我的程序吗?
    MongoDBHelper
    Js事件 事件绑定
    xslt元素 applyimports
    博客成立,开启一段追逐之旅,留个小小的纪念 ^^
    函数
    C语言基本特性
    预处理
    数组
  • 原文地址:https://www.cnblogs.com/huangtq/p/15492907.html
Copyright © 2020-2023  润新知