两种模块化语法(ports,exports,requireexport,。。
⽬录
模块
CommonJs规范中规定了每⼀个⽂件都是⼀个模块。使⽤require导⼊的⽂件会形成⼀个属于⾃⾝的模块作⽤域,这样就不会在进⾏变量以及函数声明时会污染全局作⽤域。所有的变量和函数都只有模块⾃⾝能访问,对外不可见的。
举例:
// foo.js
var name = 'foo';
// bar.js
var name = 'bar';
require('./foo.js');
console.log(name); // bar
在bar.js中通过require函数加载foo.js。运⾏之后输出的结果是‘bar’,这就说明了foo.js中的变量声明并不会影响bar.js。每个⽂件都拥有⾃⼰的作⽤域。
导出
导出是⼀个模块对外暴露⾃⾝的唯⼀⽅式。在CommonJs中,通过ports可以导出模块中的内容。
举例:
name: 'foo'
}
CommonJs模块内部会有⼀个module对象⽤于存放当前模块的信息,可以理解为在每个模块的最开始
中定义了以下对象:
var module = {};
// ...
CommonJs也⽀持另⼀种导出⽅式:exports。
exports.name = 'foo'
在实现上,这段代码与上⾯的ports没有不同,其内在机制是将exports指向了ports。可以简单的理解为CommonJs在每个模块的开头默认添加了以下代码:
var module = {
exports: {}
}
var exports = ports;
因此,为export.name赋值相当于在ports对象上添加了⼀个name属性。
Ⅰ、也很容易看出exports与ports只是指向同⼀个对象。所以对exports进⾏赋值操作,使其指向新的对象,就会失效了。
ingenious:由上⾯可以知道  即使require 导出后也⽆法改变模块对象!!
举例:
exports = {
name: 'foo'
}
此时name属性并不会被导出。
Ⅱ、另外这两个⽅法,并不能⼀起运⽤。因为使⽤ports赋值就相当于使其指向新的对象。之前的exports赋值都会失效。导⼊
CommonJs使⽤require函数进⾏导⼊操作。
举例:
// foo.js
sayname: function () {
console.log('foo');
}
};
// bar.js
var sayname = require('./foo.js').sayname;
sayname(); // foo
在bar.js中导⼊了foo.js,并调⽤了它的sayname函数。
当require⼀个模块时会有两种情况:
模块是第⼀次被require加载。这时会⾸先执⾏该模块,然后导出内容。
模块是曾经被require加载过。这时会直接导出执⾏得到的结果。
举例:
// foo.js
console.log('running foo.js')
exports.name = 'foo';
// bar.js
var firstname = require('./foo').name;
console.log('firstname:', firstname);
var lastname = require('./foo').name;
console.log('lastname:', lastname);
输出的是:
running foo.js
fistname:foo
lastname:foo
从上⾯代码看有两个地⽅都require了foo⽂件,但从结果看,只运⾏了⼀遍foo.js。
module对象中有⼀个loaded属性⽤于记录该模块是否被加载过。默认值为false,当模块第⼀次被加载时,会赋值为true,后⾯再次加载时会检查module.loaded是否为true,如果是,则直接返回结果,并不会再次执⾏代码。
require函数可以接受表达式,借助这个特性可以动态地制定模块的加载路径。
举例:
var path = ['foo.js', 'bar.js'];
path.forEach(name => {
require('./' + name);
})
ES6Module
CommonJs可以说是⽐较好的解决了模块的问题,但这些都只是由社区提出的规范,并不能算语⾔本⾝的特性。
到了2015年,ECMAScript6.0正式定义了JavaScript模块标准。从此 JavaScript 语⾔才具备了模块这⼀特性。
模块
将前⾯CommonJs的例⼦,⽤ES6Module⽅式改写。
// foo.js
export default {
import语句sayname: function () {
console.log('foo');
}
};
// bar.js
import foo from './foo.js'
foo.sayname(); // foo
ES6Module也是将每个⽂件作为⼀个模块,每个模块拥有⾃⾝的作⽤域,不同的是导⼊、导出语句。import和export也是作为保留关键字在ES6版本加⼊了进来,⽽且ES6Module会⾃动采⽤严格模式。
导出
在ES6Module中使⽤export命令来导出模块。export有两种形式:
命名导出
默认导出
1、命令导出
⼀个模块可以有多个命名导出,有两种不同的写法:
// 1
export const name = 'foo';
// 2
const name = 'foo';
export { name }
可以通过as关键字对变量重命名。
例如:
const name = 'foo';
export { name as nickname }
2、默认导出
默认导出只能有⼀个:
export default {
name: 'foo'
}
可以将export default理解为对外输出⼀个名为default的变量。
导⼊
ES6Module使⽤import语法导⼊模块。
举例:
// foo.js
export const name = 'foo';
// bar.js
import { name } from './foo'
console.log(name)
加载带有命令导出的模块时,import后⾯要跟⼀对⼤括号来将导⼊的变量名包裹起来,并且这写变量名需要与导出的变量名完全⼀致。导⼊变量的效果相当于在当前作⽤域下声明了这些变量,并且不可以对齐进⾏修改。
与命令导出类似,也可以通过as关键字对导⼊的变量重命名。
举例:
// foo.js
export const name = 'foo';
// bar.js
import { name as nickname } from './foo'
console.log(nickname)
// 也可以通过整体导⼊的⽅法
import * as name from './foo'
console.log(name.name)
默认导⼊的例⼦:
// foo.js
export default {
name: 'foo'
}
// bar
import name from './foo'
console.log(name.name)
CommonJs与ES6Module的区别
对模块依赖的处理区别
CommonJs与ES6Module最本质的区别在于前者对模块依赖的解决是动态的,⽽后者是静态的。
动态:模块依赖关系的建⽴是发⽣在代码运⾏阶段;
静态:模块依赖关系的建⽴是发⽣在代码编译阶段;
在CommonJs中,当模块A加载模块B时,会执⾏B的代码,将其ports对象作为require函数的返回值进⾏返回。并且requrie 的模块路径可以动态指定,⽀持传⼊⼀个表⽰式,甚⾄可以使⽤if语句判断是否加载某个模块。所以CommonJs模块被执⾏前,并没有办法确定明确的依赖关系,模块的导⼊,导出发⽣在代码的运⾏阶段。
ES6Module的导⼊、导出语句都是声明式的,不⽀持导⼊的路径是⼀个表达式,并且导⼊、导出语句必须位于模块的顶层作⽤域。在ES6代码的编译阶段就可以分析出模块的依赖关系。
导⼊模块值的区别
在导⼊⼀个模块时,对于CommonJs来说是得到了⼀个导出值的拷贝;⽽在ES6Module中则是值的动态映射,并且这个映射是只读的。
举例:
// foo
var count = 0;
count: count,
add: function (a, b) {
count++;
return a+b;
}
}
// bar
var count = require('./foo').count;
var add = require('./foo').add;
console.log(count); // 0
add(2,3);
console.log(count); // 0
count += 1;
console.log(count); // 1(拷贝值可以更改)
bar中的count是对foo中count的⼀份值拷贝,因此在调⽤add函数时,虽然更改了foo中count的值,但是并不会对bar中导⼊值造成更改。
另⼀⽅⾯拷贝值可以进⾏更改。
使⽤ES6Module进⾏改写
// foo
let count = 0;
const add = function (a,b) {
count++;
return a+b;
}
export { count, add }
// bar
import { count, add } from './foo';
console.log(count); // 0
add(2,3);
console.log(count); // 1(实时反映foo中count的值)
count++; // 报错 count is read-only

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。