CommonJS规范

2017-01-16 15:22:30

目录

概述

Node应用由模块组成,采用CommonJS模块规范。

根据这个规范,每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。

// example.jsvar x = 5;var addX = function (value) {
  return value + x;};

上面代码中,变量x和函数addX,是当前文件example.js私有的,其他文件不可见。

如果想在多个文件分享变量,必须定义为global对象的属性。

global.warning = true;

上面代码的warning变量,可以被所有文件读取。当然,这样写法是不推荐的。

CommonJS规范规定,每个模块内部,module变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。加载某个模块,其实是加载该模块的module.exports属性。

var x = 5;var addX = function (value) {
  return value + x;};module.exports.x = x;module.exports.addX = addX;

上面代码通过module.exports输出变量x和函数addX

require方法用于加载模块。

var example = require('./example.js');console.log(example.x); // 5console.log(example.addX(1)); // 6

require方法的详细解释参见《Require命令》一节。

CommonJS模块的特点如下。

  • 所有代码都运行在模块作用域,不会污染全局作用域。

  • 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。

  • 模块加载的顺序,按照其在代码中出现的顺序。

module对象

Node内部提供一个Module构建函数。所有模块都是Module的实例。

function Module(id, parent) {
  this.id = id;
  this.exports = {};
  this.parent = parent;
  // ...

每个模块内部,都有一个module对象,代表当前模块。它有以下属性。

  • module.id 模块的识别符,通常是带有绝对路径的模块文件名。

  • module.filename 模块的文件名,带有绝对路径。

  • module.loaded 返回一个布尔值,表示模块是否已经完成加载。

  • module.parent 返回一个对象,表示调用该模块的模块。

  • module.children 返回一个数组,表示该模块要用到的其他模块。

  • module.exports 表示模块对外输出的值。

下面是一个示例文件,最后一行输出module变量。

// example.jsvar jquery = require('jquery');exports.$ = jquery;console.log(module);

执行这个文件,命令行会输出如下信息。

{ id: '.',
  exports: { '$': [Function] },
  parent: null,
  filename: '/path/to/example.js',
  loaded: false,
  children:
   [ { id: '/path/to/node_modules/jquery/dist/jquery.js',
       exports: [Function],
       parent: [Circular],
       filename: '/path/to/node_modules/jquery/dist/jquery.js',
       loaded: true,
       children: [],
       paths: [Object] } ],
  paths:
   [ '/home/user/deleted/node_modules',
     '/home/user/node_modules',
     '/home/node_modules',
     '/node_modules' ]}

如果在命令行下调用某个模块,比如node something.js,那么module.parent就是undefined。如果是在脚本之中调用,比如require('./something.js'),那么module.parent就是调用它的模块。利用这一点,可以判断当前模块是否为入口脚本。

if (!module.parent) {
    // ran with `node something.js`
    app.listen(8088, function() {
        console.log('app listening on port 8088');
    })} else {
    // used with `require('/.something.js')`
    module.exports = app;}

module.exports属性

module.exports属性表示当前模块对外输出的接口,其他文件加载该模块,实际上就是读取module.exports变量。

var EventEmitter = require('events').EventEmitter;module.exports = new EventEmitter();setTimeout(function() {
  module.exports.emit('ready');}, 1000);

上面模块会在加载后1秒后,发出ready事件。其他文件监听该事件,可以写成下面这样。

var a = require('./a');a.on('ready', function() {
  console.log('module a is ready');});

exports变量

为了方便,Node为每个模块提供一个exports变量,指向module.exports。这等同在每个模块头部,有一行这样的命令。

var exports = module.exports;

造成的结果是,在对外输出模块接口时,可以向exports对象添加方法。

exports.area = function (r) {
  return Math.PI * r * r;};exports.circumference = function (r) {
  return 2 * Math.PI * r;};

注意,不能直接将exports变量指向一个值,因为这样等于切断了exportsmodule.exports的联系。

exports = function(x) {console.log(x)};

上面这样的写法是无效的,因为exports不再指向module.exports了。

下面的写法也是无效的。

exports.hello = function() {
  return 'hello';};module.exports = 'Hello world';

上面代码中,hello函数是无法对外输出的,因为module.exports被重新赋值了。

这意味着,如果一个模块的对外接口,就是一个单一的值,不能使用exports输出,只能使用module.exports输出。

module.exports = function (x){ console.log(x);};

如果你觉得,exportsmodule.exports之间的区别很难分清,一个简单的处理方法,就是放弃使用exports,只使用module.exports

AMD规范与CommonJS规范的兼容性

CommonJS规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。AMD规范则是非同步加载模块,允许指定回调函数。由于Node.js主要用于服务器编程,模块文件一般都已经存在于本地硬盘,所以加载起来比较快,不用考虑非同步加载的方式,所以CommonJS规范比较适用。但是,如果是浏览器环境,要从服务器端加载模块,这时就必须采用非同步模式,因此浏览器端一般采用AMD规范。

AMD规范使用define方法定义模块,下面就是一个例子:

define(['package/lib'], function(lib){
  function foo(){
    lib.log('hello world!');
  }

  return {
    foo: foo
  };});

  • 2019-10-23 10:20:48

    php 去掉 头尾 空格 2种方法

    看似很简单的问题,其实还是有点坑的,首先这里 空格转义,不是字符串,直接用trim()是去不掉。

  • 2019-10-23 23:25:22

    PostMapping,GetMapping不固定路径的写法

    PostMapping中的value属性是数组,所以可以定义多个路径,required属性默认是true,不必再写required=true,默认表示该参数是必须要有的,如果写required=false,表示该参数是可选的,可有可无。

  • 2019-10-24 01:14:29

    通过属性搜索商品mysql设计

    属性是可以动态扩展的,问题:属性是搜索条件 问:请展示出 材质为塑料 防水为YES,颜色为绿色的商品详细信息,按照sort_filed排序

  • 2019-10-25 15:18:18

    Vue.use Vue.prototype区别

    1、不是为了vue写的插件(插件内要处理)不支持Vue.use()加载方式 2、非vue官方库不支持new Vue()方式 3、每一个vue组件都是Vue的实例,所以组件内this可以拿到Vue.prototype上添加的属性和方法。

  • 2019-10-25 23:33:45

    php制作漂亮的随机颜色标签云效果

    代码中的array里面的字符串就是各种随机色的编码,大家可以根据自己的需求来配置颜色。 然后input1里面是字体大小的随机代码,也可以自行修改。

  • 2019-10-27 00:23:42

    mac卡住,强制关闭应用

    要正常退出(关闭)Mac 应用,请从菜单栏的应用菜单中选取“退出”,或按下 Command (⌘)-Q。如果应用不退出,请按照以下步骤强制该应用退出。