美文网首页
第二十二章 Module的语法

第二十二章 Module的语法

作者: A郑家庆 | 来源:发表于2019-01-11 18:31 被阅读0次

    概述

      javascript一直没有模块体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方式将它们拼装起来。在ES6之前,社区制定了一些模块加载方案,最主要的有CommonJS和AMD两种。前者用于服务器,后者用于浏览器。ES6在语言规格的层面上实现了模块功能,完全可以取代现有的CommonJS和AMD规范,成为浏览器和服务器通用的模块解决方案。ES6模块的设计思想是尽量静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS和AMD模块都只能在运行时确定这些东西。比如,CommonJS模块就是对象,输入时必须查找对象属性。

    let {stat, exists} = require('fs')
    

      上面代码的实质是整体加载fs模块(即加载fs的所有方法),生成一个对象,然后再从这个对象上面读取两个方法。这种加载被称为"运行时加载",因为只有运行时才能得到这个对象,导致完全没有办法在编译时进行"静态优化"。
      ES6模块不是对象,而是通过export命令显式指定输出的代码,再通过import命令输入。

    import {stat, exists} from 'fs'
    

      上面代码的实质是从fs模块加载3个方法,而不是加载其他方法。这种加载称为"编译加载"或静态加载,即ES6可以在编译时就完成模块加载,效率比CommonJS模块加载方式高。当然,这也导致了ES6模块本身无法被引用,因为它不是对象。

    export命令

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

    // profile.js
    // 输出
    export let firstName = "Michael"
    export let lastName = "Jackson"
    或
    let firstName = "Michael"
    let lastName = "Jackson"
    export {firstName, lastName}
    
    // 输入
    import {firstName, lastName} from 'profile.js'
    

      上面代码我们看到export输出代码,import后面必须跟大括号,而且大括号内的变量必须跟输出的变量对应,这里可以理解为解构赋值。最后export命令可以出现在模块的任何位置,只要处于模块顶层就可以。如果处于块级作用域就会报错,import也是如此。

    import命令

      import的执行早于函数的调用,这种行为的本质是import命令是编译阶段执行的,在代码运行之前。由于import是静态执行,所以不能使用表达式和变量,只有在运行时才能得到结果的语法结构。

    // 报错
    import {'f' + 'oo'} from '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',这个代码仅仅执行lodash模块,但是不会输入任何值。

    模块的整体加载

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

    // circle.js
    import * as circle from './circle'
    

    export default命令

    // demo.js
    export default function () {
        console.log('foo')
    }
    
    import custom from './demo.js'
    custom()    // foo
    

    注意:export default后面跟任意数据类型或变量,import命令这时候后面不使用大括号。

    // 第一组
    export default function crc32 () {}
    import crc32 from 'crc32'
    // 第二组
    export function crc32 () {}
    import {crc32} from 'crc32'
    

    上面的两组写法中,第一组使用export default对应的import语句后面不跟大括号,第二组使用export对应的import语句需要使用大括号。
    export default命令用于指定模块的默认输出。显然一个模块只能有一个默认输出,因此这个命令在一个文件中只能使用一次。所以import命令后面才不用加大括号,因为只可能对应一个方法。本质上,export default就是输出一个叫作default的变量或方法,然后系统允许我们为它取任意名字。

    function add (x, y) {
        return x*y
    }
    export {add as default}
    // 等同于
    export default add
    
    import {default as xxx} from 'modules'
    // 等同于
    import xxx from 'modules'
    

    正是因为export default命令其实只是输出一个叫作default的变量,所以它后面不能跟变量生命语句。

    // 正确
    export let a = 1
    // 正确
    let a = 1
    export default a
    // 错误
    export default let a = 1
    

    上面的代码中,export default中的a的含义是将变量a的值赋给变量default。所以最后一种写法会报错。

    // 正确
    export default 42
    //错误
    export 42
    

    上面的代码中,后一句报错是因为没有指定对外的接口,而前一句指定对外接口为default。

    静态和动态:编译时加载代码叫静态,运行时加载代码叫动态。
    export
    运行和编译
    静态和动态
    ES6 export export default Node require module export区别
    总结:export后必须跟接口名以及值,或者是一个对象,import后面必须跟大括号,并且大括号里面必须跟export的接口名对应,export default后面跟任意数据类型都可以,import后不能有大括号,表示整体输入,module export后面需要跟对象,import后面可以跟大括号也可以不跟大括号,有大括号就是解构赋值,没有的话就是整体输入。export输出指定的接口,export default输出一个整体。

    相关文章

      网友评论

          本文标题:第二十二章 Module的语法

          本文链接:https://www.haomeiwen.com/subject/tmvtdqtx.html