Module 的语法
来源:互联网 发布:域名批量查询软件 编辑:程序博客网 时间:2024/06/08 09:38
文章编写参考 阮一峰《ECMAScript 6 入门》
1. 概述
模块化开发对于现在的大型的应用系统来说是必不可少的一种模式,【ES6模块的设计思想是尽量的静态化】使得编译时就能确定模块的依赖关系,以及输入和输出的变量。
ES6模块不是对象,而是通过export命令显示指定输出的代码,再通过import命令导入。
// ES6模块import { stat, exists, readFile } from 'fs';
上面代码从fs模块中加载了3个方法,其他方法不加载。这种加载成为“编译时加载”或者静态加载,即ES6可以在编译时就完成模块的加载。
2. 严格模式
ES6的模块自动采用严格模式,不管你是否在模块的头部加上“use strict”
严格模式中有以下几种限制
- 变量必须声明后再使用
- 函数的参数不能有同名属性,否则报错
- 不能使用with语句
- 不能对只读属性赋值,否则报错
- 不能使用前缀0表示八进制数,否则报错
- 不能删除不可删除的属性,否则报错
- 不能删除变量delete prop,会报错,只能删除属性delete global[prop]
- eval不会在它的外层作用域引入变量
- eval和arguments不能被重新赋值
- arguments不会自动反映函数参数的变化
- 不能使用arguments.callee
- 不能使用arguments.caller
- 禁止this指向全局对象
- 不能使用fn.caller和fn.arguments获取函数调用的堆栈
- 增加了保留字(比如protected、static和interface)
其中,尤其需要注意this的限制。ES6 模块之中,顶层的this指向undefined,即不应该在顶层代码使用this。
3. export命令
模块的功能主要由两个命令构成:export和import。export用于在模块中导出接口;import命令用于输入其他模块提供的功能。export和import是配套使用的。
一个模块就是一个JS文件,【模块中所有的变量外部都是无法获取的】。如果你希望外部能访问到模块内部的某个变量,就必须使用【export命令导出变量】。
//// profile.jsexport var firstName = 'Michael';export var lastName = 'Jackson';export var year = 1958;
上面代码是profile.js文件,保存了用户信息。ES6 将其视为一个模块,里面用export命令对外部输出了三个变量。
export除了上面的那样的写法,还有另外一种更常用的方式
// profile.jsvar firstName = 'Michael';var lastName = 'Jackson';var year = 1958;export {firstName, lastName, year};
上面代码在export命令后面,使用大括号指定所要输出的一组变量。它与前一种写法(直接放置在var语句前)是等价的,但是应该【优先考虑使用这种写法】。因为这样就可以在脚本尾部,一眼看清楚输出了哪些变量。
【export命令除了输出变量,还可以输出函数或类(class)】
//输出函数export function multiply(x, y) { return x * y;};//输出类export class Person{}
通常情况下,export输出的变量就是本来的名字,但是可以使用as关键字重命名。
function v1() { //.....}function v2() { //...}export { v1 as streamV1, v2 as streamV2, v2 as anotherV2}
上面代码使用as关键字,重命名了函数v1和v2的对外接口。重命名后,v2可以用不同的名字输出两次。
【注意】export命令规定的是对外的接口,必须与模块内部建立一一对应关系。
//报错let m = 1;export m;//报错export 1;
上面的两种写法都会报错,因为没有提供对外的接口。第一种写法导出一个变量,变量为1,也就是跟第二种写法一样,都是导出1,1是一个值,不是一个接口。
// 写法一export var m = 1;// 写法二var m = 1;export {m};// 写法三var n = 1;export {n as m};
上面三种写法都是正确的,规定了对外的接口m。其他脚本可以通过这个接口,取到值1。它们的实质是,在接口名与模块内部变量之间,建立了一一对应的关系。
同样的,function和class的输出,也必须遵守这样的写法。
//报错function fun() { }export fun;//正确export function fun() { }//正确function fun() { }export { fun }
【注意】export输出的接口,与其对应的值是动态绑定的,也就是说通过模块接口可以取得模块内部实时的值。
export let name = 'Blue';setTimeout(function () { name = 'Crazy'}, 500);
上面代码导出的name接口,初始为Blue,但是500毫秒之后就变成了Crazy,模块外部的值与模块内部的值是实时绑定的。
【export可以出现在模块的任何位置,但是必须处于顶层作用域中】
function foo() { export default 'bar' // SyntaxError}foo()
上面代码由于export命令出现在了函数中,所以导致代码报错。
4. import命令
在模块中使用了export导出接口之后,我们使用import在其他JS文件中导入接口以加载模块。
////modulelet sayHi = () => "I am blue";class Person {}let name = 'Blue';export {sayHi,Person,name}
//导入模块import { sayHi, Person, name } from './model'
上面代码中,从model模块中导入了三个接口,分别是一个方法、一个类、和一个变量。【import大括号中的名称必须和模块导出的名称一致,又别名的与别名一致】
如果我们想对输出的接口进行重名名,那么可以在import的大括号中进行操作
import { sayHi as sayHello, Person, name } from './model'sayHello() //I am blue
import后面的from指定模块文件的位置,可以是相对路径,也可以是绝对路径,.js路径可以省略。如果只是模块名,不带有路径,那么必须有配置文件,告诉 JavaScript 引擎该模块的位置。
import {myMethod} from 'util';
上面代码中,util是模块文件名,由于不带有路径,必须通过配置,告诉引擎怎么取到这个模块。
【注意】import命令存在命令提升,会提升到整个代码的头部
sayHello() //I am blueimport { sayHi as sayHello, Person, name } from './model'
上面的代码不会报错,因为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';
上面代码仅仅执行lodash模块,但是不输入任何值。
如果多次重复执行同一句import语句,那么只会执行一次,而不会执行多次。
import 'lodash';import 'lodash';
上面代码加载了两次lodash,但是只会执行一次。
import { foo } from 'my_module';import { bar } from 'my_module';// 等同于import { foo, bar } from 'my_module';
上面代码中,虽然foo和bar在两个语句中加载,但是它们对应的是同一个my_module实例。也就是说,import语句是 Singleton 模式。
5. 模块的整体加载
除了指定加载某个输出值,还可以使用整体加载,即用星号(*)指定一个对象,所有输出值都加载在这个对象上面。
//逐一列举导出接口import { sayHi as sayHello, Person, name } from './model'//整体加载import * as exportobj from './model'exportobj;/* { sayHi: [Function: sayHi], Person: [Function: Person], name: 'Blue' }*/
上面代码中用*整体加载了一个模块,在这个对象拥有所有模块导出的接口
【注意】模块的整体加载也是静态的,不允许导入模块之后再模块外部运行时对接口进行改变。
import * as circle from './circle';// 下面两行都是不允许的circle.foo = 'hello';circle.area = function () {};
6. export default命令
从前面的例子可以看出,使用import命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载。但是,用户肯定希望快速上手,未必愿意阅读文档,去了解模块有哪些属性和方法。
为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default命令,为模块指定默认输出。
//// export-default.jsexport default function () { console.log('foo');}
上面代码是一个模块文件export-default.js,它的默认输出是一个函数
他模块加载该模块时,import命令可以为该匿名函数指定任意名字。
//// import-default.jsimport customName from './export-default';customName(); // 'foo'
上面代码的import命令,可以用任意名称指向export-default.js输出的方法,这时就不需要知道原模块输出的函数名。需要注意的是,这时import命令后面,不使用大括号。
export default还可以用于非匿名函数
export default function sayHi() { console.log('I am Blue');}//等同于function sayHi() { console.log('I am Blue');}export default sayHi;
上面代码中,函数名sayHi在模块外部是无效的,在加载的时候视为匿名函数。
下面比较一下默认输出和正常输出
//第一组export default function crc32() { // 输出 // ...}import crc32 from 'crc32'; // 输入// 第二组export function crc32() { // 输出 // ...};import {crc32} from 'crc32'; // 输入
上面代码的两组写法,第一组是使用export default时,对应的import语句不需要使用大括号;第二组是不使用export default时,对应的import语句需要使用大括号。
【一个模块只能有一个默认输出】所以,import命令后面才不用加大括号,因为只可能对应一个方法。
本质上,export default就是输出的一个叫作default的变量或方法,然后系统允许你为它取别名。
// modules.jsfunction add(x, y) { return x * y;}export {add as default};// 等同于// export default add;// app.jsimport { default as xxx } from 'modules';// 等同于// import xxx from 'modules';
【注意】因为export default输出的是一个叫做default的变量,所以他后面不能再跟变量声明语句了。
// 正确export var a = 1;// 正确var a = 1;export default a;// 错误export default var a = 1;
有了export default命令,输入模块时就非常直观了,以输入 lodash 模块为例。
import _ from 'lodash';
如果想在一条import语句中,同时输入默认方法和其他接口,可以写成下面这样。
import _, { each, each as forEach } from 'lodash';
对应上面代码的export语句如下。
export default function (obj) { // ···}export function each(obj, iterator, context) { // ···}export { each as forEach };
上面代码的最后一行的意思是,暴露出forEach接口,默认指向each接口,即forEach和each指向同一个方法。
export default也可以用来输出类。
const Person = class { constructor(name) { this.name = name } sayHi() { console.log('I am ', this.name); }}export default Person;
import Person from './model';let p = new Person('Blue');p.sayHi(); //I am Blue
7. export 与import的复合写法
如果在一个模块中,先输入后输出同一个模块,import语句可以与export语句写在一起。
export { Person } from './model';//等同于import Person from './model';export { Person };
上面代码中,将model模块导入后导出,写成了一个export和import的复合写法。
【改名输出】
export { Person as People } from './model';
上面代码使用 as 将导入的接口改名后导出。
【整体输出】
export * from './model';
【默认输出】
export { default } from './model';
【具名接口改成默认接口输出】
export { Person as default } from './model';
【默认接口改成具名接口输出】
export { default as Person } from './model';
8. 模块的继承
模块之间产生继承给我的感觉就是把一个模块导入这个模块儿,在该模块中添加新的方法或者变量,然后导出。
假设有一个circleplus模块,继承了circle模块。
// circleplus.jsexport * from 'circle';export var e = 2.71828182846;export default function(x){ return Math.exp(x);}
上面代码中的export **,表示再输出circle模块的所有属性和方法。然后,上面代码又输出了自定义的e变量和默认方法。
【注意】export * 命令会忽略circle模块的default方法。
这时也可以将circle模块的属性或者方法,改名后再输出。
// circleplus.jsexport { area as circleArea } from 'circle';
上面代码表示,只输出circle模块的area方法,且将其改名为circleArea。
加载上面模块的写法如下。
// main.jsimport * as math from 'circleplus';import exp from 'circleplus';console.log(exp(math.e));
上面代码中的import exp表示,将circleplus模块的默认方法加载为exp方法。
9. 跨模块常量
介绍const命令的时候说过,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); // 1console.log(constants.B); // 3// test2.js 模块import {A, B} from './constants';console.log(A); // 1console.log(B); // 3
如果要使用的常量非常多,可以建一个专门的constants目录,将各种常量写在不同的文件里面,保存在该目录下。
// constants/db.jsexport const db = { url: 'http://my.couchdbserver.local:5984', admin_username: 'admin', admin_password: 'admin password'};// constants/user.jsexport const users = ['root', 'admin', 'staff', 'ceo', 'chief', 'moderator'];
然后,将这些文件输出的常量,合并在index.js里面。
// constants/index.jsexport {db} from './db';export {users} from './users';
使用的时候,直接加载index.js就可以了。
// script.jsimport {db, users} from './constants';
- Module 的语法
- ES6的module语法--export
- ES6之Module的语法(1)
- ES6之Module的语法(2)
- ES6之Module的语法(3)
- ES6学习笔记- Module的语法
- 简要记录ES6中Module的语法
- ES6 —(Module 的语法)
- ES6 Module语法
- python语法[module/package+import]
- python语法31[module/package+import]
- python语法31[module/package+import]
- Python语法31[module/package+import]
- module和class module 的区别
- CVS 的module使用
- Module间的通讯
- FLEX module的使用
- FLEX module的使用
- varchar类型的字段存储纯数字的排序
- Cookies与Session的总结
- 靡不有初,鲜克有终,用博客记录自己进步的点点滴滴
- 一个爬虫工程师的梦
- Xcode8 注释插件 注释快捷键 VVDocumenter-Xcode?
- Module 的语法
- 20.判断括号的使用是否合法
- 菜鸟,大牛和教主,三者的区别
- hdu1114 完全背包
- linux常用命令
- 平衡二叉树-LintCode
- MIUI拖动效果
- C++之友元学习笔记
- Win7报错“部分便笺的元数据已被损坏” 80%解决问题