资讯专栏INFORMATION COLUMN

[面试专题]JS中模块AMD,CMD,import

dreamans / 2056人阅读

摘要:服务端使用模块规范。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。注意是数组格式工厂方法,返回一个模块函数如果一个模块不依赖其他模块,那么可以直接定义在函数之中。

js中的require、import和export require时代

Javascript社区做了很多努力,在现有的运行环境中,实现"模块"的效果。

对象写法

把模块写成一个对象,所有的模块成员都放到这个对象里面

var module1 = new Object({
  _count : 0,
 m1 : function (){
  //...
 },
 m2 : function (){
  //...
 }
});

上面的函数m1()和m2(),都封装在module1对象里。使用的时候,就是调用这个对象的属性

module1.m1();

这样的写法会暴露所有模块成员,内部状态可以被外部改写。比如,外部代码可以直接改变内部计数器的值。

module._count = 1;
立即执行函数写法

使用"立即执行函数"(Immediately-Invoked Function Expression,IIFE),可以达到不暴露私有成员的目的

var module = (function() {
    var _count = 0;
    var m1 = function() {
        alert(_count)
    }
    var m2 = function() {
        alert(_count + 1)
    }

    return {
        m1: m1,
        m2: m2
    }
})()

module就是Javascript模块的基本写法。

主流模块规范

在es6以前,还没有提出一套官方的规范,从社区和框架推广程度而言,目前通行的javascript模块规范有两种:CommonJS 和 AMD

CommonJS规范

node编程中最重要的思想之一就是模块,而正是这个思想,让JavaScript的大规模工程成为可能。服务端使用CommonJS模块规范。

在CommonJS中,有一个全局性方法require(),用于加载模块。假定有一个数学模块math.js,就可以像下面这样加载。

 var math = require("math");
 math.add(2,3); // 5

正是由于CommonJS 使用的require方式的推动,才有了后面的AMD、CMD 也采用的require方式来引用模块的风格

AMD规范

Common.js起源于node,因此在服务端广泛使用.对服务端,所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是,对于浏览器,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于"假死"状态。
因此,浏览器端的模块,不能采用"同步加载"(synchronous),只能采用"异步加载"(asynchronous)。这就是AMD规范诞生的背景。

AMD是"Asynchronous Module Definition"的缩写,意思就是"异步模块定义"。它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。

模块必须采用特定的define()函数来定义。

define(id?, dependencies?, factory)

id:字符串,模块名称(可选)

dependencies: 是我们要载入的依赖模块(可选),使用相对路径。,注意是数组格式

factory: 工厂方法,返回一个模块函数

如果一个模块不依赖其他模块,那么可以直接定义在define()函数之中。

// math.js
  define(function (){
    var add = function (x,y){
      return x+y;
    };
    return {
      add: add
    };
  });

如果这个模块还依赖其他模块,那么define()函数的第一个参数,必须是一个数组,指明该模块的依赖性。

define(["Lib"], function(Lib){
    function foo(){
      Lib.doSomething();
    }
    return {
      foo : foo
    };
  });

当require()函数加载上面这个模块的时候,就会先加载Lib.js文件。

AMD也采用require()语句加载模块,但是不同于CommonJS,它要求两个参数:

require([module], callback);

第一个参数[module],是一个数组,里面的成员就是要加载的模块;第二个参数callback,则是加载成功之后的回调函数。如果将前面的代码改写成AMD形式,就是下面这样:

require(["math"], function (math) {
 math.add(2, 3);
});

math.add()与math模块加载不是同步的,浏览器不会发生假死。所以很显然,AMD比较适合浏览器环境。

目前,主要有两个Javascript库实现了AMD规范:require.js和curl.js。

CMD规范

CMD (Common Module Definition), 是seajs推崇的规范,CMD则是依赖就近,用的时候再require。它写起来是这样的:

define(function(require, exports, module) {
   var clock = require("clock");
   clock.start();
});

CMD与AMD一样,也是采用特定的define()函数来定义,用require方式来引用模块

define(id?, dependencies?, factory)

id:字符串,模块名称(可选)

dependencies: 是我们要载入的依赖模块(可选),使用相对路径。,注意是数组格式

factory: 工厂方法,返回一个模块函数

define("hello", ["jquery"], function(require, exports, module) {

  // 模块代码

});

如果一个模块不依赖其他模块,那么可以直接定义在define()函数之中。

define(function(require, exports, module) {
  // 模块代码
});

注意:带 id 和 dependencies 参数的 define 用法不属于 CMD 规范,而属于 Modules/Transport 规范。

CMD与AMD区别

AMD和CMD最大的区别是对依赖模块的执行时机处理不同,而不是加载的时机或者方式不同,二者皆为异步加载模块。

AMD依赖前置,js可以方便知道依赖模块是谁,立即加载;

而CMD依赖就近,需要使用把模块变为字符串解析一遍才知道依赖了那些模块,这也是很多人诟病CMD的一点,牺牲性能来带来开发的便利性,实际上解析模块用的时间短到可以忽略。

现阶段的标准

ES6标准发布后,module成为标准,标准使用是以export指令导出接口,以import引入模块,但是在我们一贯的node模块中,我们依然采用的是CommonJS规范,使用require引入模块,使用module.exports导出接口。

import引入模块

import语法声明用于从已导出的模块、脚本中导入函数、对象、指定文件(或模块)的原始值。

import模块导入与export模块导出功能相对应,也存在两种模块导入方式:命名式导入(名称导入)和默认导入(定义式导入)。

注意:import必须放在文件的最开始.import命令是编译阶段执行的,在代码运行之前,表达式和变量只有在运行时才能得到结果的语法结构。import命令会被 JavaScript 引擎静态分析,先于模块内的其他模块执行(叫做”连接“更合适)所以import中不能含有表达式或者变量,因此无法实现动态加载.
因此,import和export命令只能在模块的顶层,不能在代码块之中(比如,在if代码块之中,或在函数之中)。
这样的设计,有利于编译器提高效率,但也导致无法在运行时加载模块。在语法上,条件加载就不可能实现。如果import命令要取代 Node 的require方法,这就形成了一个障碍。因为require是运行时加载模块,import命令无法取代require的动态加载功能。

ES6 模块与 CommonJS 模块的差异

来自阮一峰ES6教程

CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
CommonJS 模块是运行时加载,ES6 模块是编译时输出接口
ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。换句话说,ES6 的import有点像 Unix 系统的“符号连接”,原始值变了,import加载的值也会跟着变。因此,ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/85046.html

相关文章

  • [面试专题]JS模块AMD,CMD,import

    摘要:服务端使用模块规范。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。注意是数组格式工厂方法,返回一个模块函数如果一个模块不依赖其他模块,那么可以直接定义在函数之中。 js中的require、import和export require时代 Javascript社区做了很多努力,在现有的运行环境中,实现模块的效果。 对象写法 把模块写成一个对象,所...

    xinhaip 评论0 收藏0
  • 面试系列】之一:关于CmdAmd

    摘要:开篇我叫王彬,现在是百度首页业务部原网页搜团队索部前端的实习,两天前我得知我所在的部门只有两个,而且要分给策略,这就意味着我要面临千军万马过独木桥的秋招。总结我在百度实习时接触到过一个框架,用于百度首页和首页的模块化开发。 之一:关于Cmd和Amd 为什么想起来做这样一个专题呢,答案应该是为了勉励面试笔试秋招中的自己吧!而且也是为了和我一样的你。 1.开篇 我叫王彬,现在是百度首页业务...

    lordharrd 评论0 收藏0
  • 前端面试题(3)现代技术

    摘要:什么是单页面应用单页面应用是指用户在浏览器加载单一的页面,后续请求都无需再离开此页目标旨在用为用户提供了更接近本地移动或桌面应用程序的体验。流程第一次请求时,将导航页传输到客户端,其余请求通过获取数据实现数据的传输通过或远程过程调用。 什么是单页面应用(SPA)? 单页面应用(SPA)是指用户在浏览器加载单一的HTML页面,后续请求都无需再离开此页 目标:旨在用为用户提供了更接近本地...

    EasonTyler 评论0 收藏0
  • 前端面试题(3)现代技术

    摘要:什么是单页面应用单页面应用是指用户在浏览器加载单一的页面,后续请求都无需再离开此页目标旨在用为用户提供了更接近本地移动或桌面应用程序的体验。流程第一次请求时,将导航页传输到客户端,其余请求通过获取数据实现数据的传输通过或远程过程调用。 什么是单页面应用(SPA)? 单页面应用(SPA)是指用户在浏览器加载单一的HTML页面,后续请求都无需再离开此页 目标:旨在用为用户提供了更接近本地...

    trigkit4 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<