随着JavaScript开发变得愈来愈广泛,命名空间和依赖性变得愈来愈难以处理。前端开发者都以模块化的方式处理该问题。在这篇文章中,咱们将探讨前端开发人员目前使用的模块化方案以及试图解决的问题。javascript
模块化可使你的代码低耦合,功能模块直接不相互影响。html
讲完了JavaScript模块化的好处,咱们来看下有哪些解决方案来实现JavaScript的模块化。前端
var myRevealingModule = (function () { var privateVar = "Ben Cherry", publicVar = "Hey there!"; function privateFunction() { console.log( "Name:" + privateVar ); } function publicSetName( strName ) { privateVar = strName; } function publicGetName() { privateFunction(); } // Reveal public pointers to // private functions and properties return { setName: publicSetName, greeting: publicVar, getName: publicGetName }; })(); myRevealingModule.setName( "Paul Kinlan" );
经过这种构造,咱们经过使用函数有了本身的做用域或“闭包”。java
这种方法的好处在于,你能够在函数内部使用局部变量,而不会意外覆盖同名全局变量,但仍然可以访问到全局变量。node
优势:es6
缺点:编程
CommonJS是一个旨在定义一系列规范的项目,以帮助开发服务器端JavaScript应用程序。CommonJS团队试图解决的一个领域就是模块。Node.js开发人员最初打算遵循CommonJS规范,但后来决定反对它。segmentfault
在 CommonJS 的规范中,每一个 JavaScript 文件就是一个独立的模块上下文(module context),在这个上下文中默认建立的属性都是私有的。也就是说,在一个文件定义的变量(还包括函数和类),都是私有的,对其余文件是不可见的。浏览器
须要注意的一点是,CommonJS以服务器优先的方式来同步载入模块,假使咱们引入三个模块的话,他们会一个个地被载入。服务器
// In circle.js const PI = Math.PI; exports.area = (r) => PI * r * r; exports.circumference = (r) => 2 * PI * r; // In some file const circle = require('./circle.js'); console.log( `The area of a circle of radius 4 is ${circle.area(4)}`);
Node.js的模块系统经过library的方式对CommonJS的基础上进行了模块化实现。
在Node和CommonJS的模块中,基本上有两个与模块系统交互的关键字:require和exports。
require是一个函数,可用于将接口从另外一个模块导入当前范围。传递给的参数require是模块的id。在Node的实现中,它是node_modules目录中模块的名称(或者,若是它不在该目录中,则是它的路径)。
exports是一个特殊的对象:放入它的任何东西都将做为公共元素导出。
Node和CommonJS之间的一个独特区别在于module.exports对象的形式。
在Node中,module.exports是导出的真正特殊对象,而exports它只是默认绑定到的变量module.exports。
另外一方面,CommonJS没有任何module.exports对象。实际意义是,在Node中,没法经过如下方式导出彻底预构造的对象module.exports:
// This won't work, replacing exports entirely breaks the binding to // modules.exports. exports = (width) => { return { area: () => width * width }; } // This works as expected. module.exports = (width) => { return { area: () => width * width }; }
优势
require
能够在任何地方调用:模块能够经过编程方式加载。缺点
AMD诞生于一群对CommonJS的研究方向不满的开发人员。事实上,AMD在开发早期就与CommonJS分道扬镳,AMD和CommonJS之间的主要区别在于它支持异步模块加载。
//Calling define with a dependency array and a factory function define(['dep1', 'dep2'], function (dep1, dep2) { //Define the module value by returning a value. return function () {}; }); // Or: define(function (require) { var dep1 = require('dep1'), dep2 = require('dep2'); return function () {}; });
经过使用JavaScript的传统闭包来实现异步加载:
在请求的模块加载完成时调用函数。模块定义和导入模块由同一个函数承载:定义模块时,其依赖关系是明确的。所以,AMD加载器能够在运行时具备项目的模块依赖图。所以能够同时加载彼此不依赖的库。这对于浏览器尤为重要,由于启动时间对于良好的用户体验相当重要。
优势
缺点
除了异步加载之外,AMD的另外一个优势是你能够在模块里使用对象、函数、构造函数、字符串、JSON或者别的数据类型,而CommonJS只支持对象。
统一模块定义(UMD:Universal Module Definition )就是将 AMD 和 CommonJS 合在一块儿的一种尝试,常见的作法是将CommonJS 语法包裹在兼容 AMD 的代码中。
(function(define) { define(function () { return { sayHello: function () { console.log('hello'); } }; }); }( typeof module === 'object' && module.exports && typeof define !== 'function' ? function (factory) { module.exports = factory(); } : define ));
该模式的核心思想在于所谓的 IIFE(Immediately Invoked Function Expression),该函数会根据环境来判断须要的参数类别
支持JavaScript标准化的ECMA团队决定解决模块问题, 兼容同步和异步操做模式。
//------ lib.js ------ export const sqrt = Math.sqrt; export function square(x) { return x * x; } export function diag(x, y) { return sqrt(square(x) + square(y)); } //------ main.js ------ import { square, diag } from 'lib'; console.log(square(11)); // 121 console.log(diag(4, 3)); // 5
ES6 模块的设计思想是尽可能的静态化,使得编译时就能肯定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时肯定这些东西。
因为 ES6 模块是编译时加载,使得静态分析成为可能。有了它,就能进一步拓宽 JavaScript 的语法,好比引入宏(macro)和类型检验(type system)这些只能靠静态分析实现的功能。除了静态加载带来的各类好处,ES6 模块还有如下好处。
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。
export语法被用来建立JavaScript模块。你能够用它来导出对象(包括函数)和原始值(primitive values)。导出有两种类型:named和default。
// named // lib.js export function sum(a, b) { return a + b; } export function substract(a, b) { return a - b; } function divide(a, b) { return a / b; } export { divide };
// default // dog.js export default class Dog { bark() { console.log('bark!'); } }
import语句用来导入其余模块。
整个导入
// index.js import * as lib from './lib.js'; console.log(lib.sum(1,2)); console.log(lib.substract(3,1)); console.log(lib.divide(6,3));
导入一个或多个named导出
// index.js import { sum, substract, divide } from './lib'; console.log(sum(1,2)); console.log(substract(3,1)); console.log(divide(6,3));
须要注意,相应的导入导出名字必须匹配。
导入一个default导出
// index.js import Dog from './dog.js'; const dog = new Dog(); dog.bark(); // 'bark!'
注意,defualt导出在导入时,能够用任意的名字。因此咱们能够这样作:
import Cat from './dog.js'; const dog = new Cat(); dog.bark(); // 'bark!'
原文出处:https://www.cnblogs.com/jingh/p/10915466.html