【小白学爬虫】JavaScript define--模块的闭包?

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

require时代的模块

node编程中最重要的思想之一就是模块,而正是这个思想,让JavaScript的大规模工程成为可能。模块化编程在js界流行,也是基于此,随后在浏览器端,requirejs和seajs之类的工具包也出现了,可以说在对应规范下,require统治了ES6之前的所有模块化编程,即使现在,在ES6 module被完全实现之前,还是这样。

node的module遵循CommonJS规范,requirejs遵循AMD,seajs遵循CMD,虽各有不同,但总之还是希望保持较为统一的代码风格。


// a.js

// -------- node -----------

module.exports = {

a:function(){},

b:'xxx'

};

// ----------- AMD or CMD ----------------

define(function(require, exports, module){

module.exports = {

a:function(){},

b:'xxx'

  };

});

为了保持风格的高度统一,除了在浏览器端的模块中要使用一个define函数来提供模块的闭包以外,其他代码可以完全一致。

// b.js

// ------------ node ---------

varm =require('./a');m.a();

// ------------ AMD or CMD -------------

define(function(require, exports,module){varm =require('./a');  m.a();});

AMD

CommonJS组织

JavaScript架构方案和标准

Asynchronous Module Definition,异步模块加载机制

模块的定义,依赖关系,引用关系以及加载机制

只定义其语法API,而不关心其实现

AMD API

AMD规范只有一个API,即define函数

define([module-name?], [array-of-dependencies?], [module-factory-or-object]);

示例:

define("pc/page/login/main", ["require", "exports", "module", "dep/jquery-placeholder/jquery.placeholder", "pc/modules/country-code/main", "dep/jquery-colorbox/jquery.colorbox-min"], function(require){})


module-name: 模块标识,可省

array-of-dependencies: 所依赖的模块,可省

module-factory-or-object: 模块的实现,或者一个JavaScript对象,是模块的具体实现本身


CommonJS定义的模块分为:模块标识(module)、模块定义(exports)、模块引用(require)

后面三个是自定义的模块:

"dep/jquery-placeholder/jquery.placeholder", "pc/modules/country-code/main", "dep/jquery-colorbox/jquery.colorbox-min"

/*!dep/jquery-placeholder/jquery.placeholder.js*/

/*!pc/modules/country-code/main.js*/

/*!dep/jquery-colorbox/jquery.colorbox-min.js*/


define函数的异步性

当define函数执行时,它首先会异步的去调用第二个参数中列出的依赖模块,当所有的模块被载入完成之后,如果第三个参数是一个回调函数则执行,然后告诉系统模块可用,也就通知了依赖于自己的模块自己已经可用。

如何定义一个模块

AMD实例:如何定义一个模块

  下面代码定义了一个alpha模块,并且依赖于内置的require,exports模块,以及外部的beta模块。可以看到,第三个参数是回调函数,可以直接使用依赖的模块,他们按依赖声明顺序作为参数提供给回调函数。

  define("alpha", ["require", "exports", "beta"], function (require, exports, beta) {

  exports.verb = function() {

  return beta.verb();

  //或者:

  return require("beta").verb();

  }

  });

这里的require函数让你能够随时去依赖一个模块,即取得模块的引用,从而即使模块没有作为参数定义,也能够被使用;exports是定义的alpha 模块的实体,在其上定义的任何属性和方法也就是alpha模块的属性和方法。通过exports.verb = ...就是为alpha模块定义了一个verb方法。例子中是简单调用了模块beta的verb方法。

require 是同步往下执行,require.async 则是异步回调执行。require.async 一般用来加载可延迟异步加载的模块。


匿名模块

  define 方法允许你省略第一个参数,这样就定义了一个匿名模块,这时候模块文件的文件名就是模块标识。如果这个模块文件放在a.js中,那么a就是模块名。可以在依赖项中用"a"来依赖于这个匿名模块。这带来一个好处,就是模块是高度可重用的。你拿来一个匿名模块,随便放在一个位置就可以使用它,模块名就是它的文件路径。这也很好的符合了DRY(Don't Repeat Yourself)原则。

  下面的代码就定义了一个依赖于alpha模块的匿名模块:

  define(["alpha"], function (alpha) {

  return {

  verb: function(){

  return alpha.verb() + 2;

  }

  };

  });

仅有一个参数的define

  前面提到,define的前两个参数都是可以省略的。第三个参数有两种情况,一种是一个JavaScript对象,另一种是一个函数。

  如果是一个对象,那么它可能是一个包含方法具有功能的一个对象;也有可能是仅提供数据。后者和JSON-P非常类似,因此AMD也可以认为包含了一个完整的 JSON-P实现。模块演变为一个简单的数据对象,这样的数据对象是高度可用的,而且因为是静态对象,它也是CDN友好的,可以提高JSON-P的性能。考虑一个提供中国省市对应关系的JavaScript对象,如果以传统JSON-P的形式提供给客户端,它必须提供一个callback函数名,根据这个函数名动态生成返回数据,这使得标准JSON-P数据一定不是CDN友好的。但如果用AMD,这个数据文件就是如下的形式:(CDN的全称是Content Delivery Network,即内容分发网络)

  define({

  provinces: [

  {

  name: '上海',

  areas: ['浦东新区', '徐汇区']},

  {

  name: '江苏',

  cities: ['南京', '南通']}

  //.....

  ]

  });

  假设这个文件名为china.js,那么如果某个模块需要这个数据,只需要:

  define(['china', function(china){

  //在这里使用中国省市数据

  });

  通过这种方式,这个模块是真正高度可复用的,无论是用远程的,还是Copy到本地项目,都节约了开发时间和维护时间。

  如果参数是一个函数,其用途之一是快速开发实现。适用于较小型的应用,你无需提前关注自己需要什么模块,自己给谁用。在函数中,可以随时require自己需要的模块。例如:

  define(function(){

  var p = require('china');

  //使用china这个模块

  });

  即你省略了模块名,以及自己需要依赖的模块。这不意味着你无需依赖于其他模块,而是可以让你在需要的时候去require这些模块。define方法在执行的时候,会调用函数的toString方法,并扫描其中的require调用,提前帮助你载入这些模块,载入完成之后再执行。这使得快速开发成为可能。需要注意的一点是,Opera不能很好的支持函数的toString方法,因此,在浏览器中它的适用性并不是很强。但如果你是通过build工具打包所有的 JavaScript文件,这将不是问题,构建工具会帮助你扫描require并强制载入依赖的模块。

你可能感兴趣的:(【小白学爬虫】JavaScript define--模块的闭包?)