95992828九五至尊2

模块机制882828九五至尊手机版,js学习笔记

一月 25th, 2019  |  882828九五至尊手机版

CommonJS规范 

模块是 Node.js 应用程序的为主组成部分,文件和模块是逐一对应的。一个
Node.js 文件就是一个模块,那几个文件或者是 JavaScript 代码、JSON
或者编译过的 C/C++ 扩大。

   
早在Netscape诞生不久后,JavaScript就直接在追究本地编程的路,Rhino是其象征产物。无奈那时服务端JavaScript走的路均是参照众多劳动器端语言来促成的,在那样的背景之下,一没有特色,二从未有过实用价值。可是随着JavaScript在前者的选取更加宽广,以及劳动端JavaScript的推动,JavaScript现有的规范格外薄弱,不便宜JavaScript大规模的利用。那么些以JavaScript为宿主语言的环境中,唯有自己的根基原生对象和连串,更加多的目的和API都有赖于宿主的提供,所以,我们得以看到JavaScript缺少这个意义:

出于JavaScript没有模块系统,所以Node.js依靠CommonJS规范自身已毕了模块系统。

  • JavaScript没有模块系统。没有原生的援救密闭功用域或倚靠管理。 
  • JavaScript没有标准库。除了有的骨干库外,没有文件系统的API,没有IO流API等。 
  • JavaScript没有标准接口。没有如Web Server或者数据库的集合接口。 
  • JavaScript没有包管理种类。不能自动加载和设置信赖。 

模块的概括利用——exports 、require 和 module

在编辑和运用各类模块时,Node.js都有require、exports、module多个先行定义好的变量可供使用。

  1. exports

    exports对象是当前模块的导出对象,用于导出模块公有方法和特性。
    实际上,exports 本身只是是一个惯常的空对象,即
    {},它更加用来声称接口。例如:

    //module.js
    exports.sayHello = function(name) { 
        console.log('Hello '    + name);
    };
    
  2. require

    require函数用于在当前模块中加载和选取其他模块,传入一个模块名,重回一个模块导出对象。模块名可使用相对路径(以./最先),或者是相对路径(以/或C:之类的盘符开头)。此外,模块名中的.js扩大名可以大致。例如:

    //index.js
    var myModule = require('./module');
    myModule.sayHello("node");
    
  3. module

    透过module对象足以访问到当下模块的局地相关新闻,但最多的用处是覆盖
    exports。例如模块导出对象默许是一个平日对象,如若想改成一个函数的话:

    //module.js
    module.exports = function(name) { 
        console.log('Hello '    + name);
    };
    
    //index.js
    var sayHello = require('./module');
    sayHello("node");
    

   
于是便有了CommonJS(http://www.commonjs.org)规范的出现,其目标是为了构建JavaScript在包括Web服务器,桌面,命令行工具,及浏览器方面的生态系统。CommonJS制定了解决这些问题的一些规范,而Node.js就是这些规范的一种实现。Node.js自身实现了require方法作为其引入模块的方法,同时NPM也基于CommonJS定义的包规范,实现了依赖管理和模块自动安装等功能。这里我们将深入一下Node.js的require机制和NPM基于包规范的应用。

模块进阶——模块载入策略

Node.js的模块分为两类,一类为原生(主题)模块,一类为文件模块。原生模块在Node.js源代码编译的时候编译进了二进制执行文书,加载的进程最快。另一类公事模块是动态加载的,加载速度比原生模块慢。

 

里面贯彻机制

加载文件模块的工作,首要由原生模块module来完成和成功,该原生模块在启动时已经被加载,进度平素调用到runMain静态方法。

Module源码:

function Module(id, parent) {
  this.id = id;
  this.exports = {};
  this.parent = parent;
  if (parent && parent.children) {
    parent.children.push(this);
  }

  this.filename = null;
  this.loaded = false;
  this.children = [];
}

模块解析流程:

  1. 命令行执行主模块

    命令行执行主模块

    // bootstrap main module.
    Module.runMain = function() {
      // Load the main module--the command line argument.
      Module._load(process.argv[1], null, true);
      // Handle any nextTicks added in the first tick of the program
      process._tickCallback();
    };
    
  2. 拍卖模块

    Module.runMain方法会在最后执行_load静态方法,该方法又会在分析文件名将来执行:

    //实例化Module函数
    var module = new Module(id, parent);
    

    并基于文件路径882828九五至尊手机版,缓存当下模块对象,该模块实例对象则基于文件名加载。

    module.load(filename);
    

    那时候,Node.js会按照分裂文件模块类型的后缀名来决定加载方法。

    • js:通过fs模块同步读取js文件并编译执行。
    • node:通过C/C++举办编辑的Addon。通过dlopen方法进行加载。
    • json:读取文件,调用JSON.parse解析加载。
  3. 输出结果

    终极js文件形式的模块会成为以下格局的内容:

    (function (exports, require, module, __filename, __dirname) {
        var circle = require('./circle.js');
            console.log('The area of a circle of radius 4 is ' +circle.area(4));
    });
    

    因此那时,主模块内可以使用exports, require,
    module等变量了,而其他模块又会透过require引进主模块,require方法会同runMain一样调用_load静态方法,以此类推。

    require源码:

    // 传入模块路径作为参数. 返回 模块的exports属性.
    
    Module.prototype.require = function(path) {
      assert(path, 'missing path');
      assert(typeof path === 'string', 'path must be a string');
      return Module._load(path, this, /* isMain */ false);
    };
    

大约模块定义和利用

Q&A

  1. console.log(this)在浏览器和Node中分头打印出哪些?

    答:分明浏览器中直接打印this指向Window对象,而在Node中,我们编辑的文书其实外面都打包了一层函数,而且该函数执行时强制apply将this指向了module.exports,由此那里打印为{}。

  2. 为什么require、__filename、__dirname、module、exports等多少个变量并不曾定义在app.js
    文件中,不过这一个方法却存在的原因。

    答:那里提到的兼具属性均是_load方法中大家编辑的js文件外层包裹函数提供给大家的,因而得以直接调用。

   
在Node.js中,定义一个模块卓殊造福。大家以计算圆形的面积和周长四个点子为例,来显现Node.js中模块的定义格局。

1 var PI = Math.PI; 
2 exports.area = function (r) {
3  return PI * r * r; 
4 }; 
5 exports.circumference = function (r) {
6  return 2 * PI * r; 
7 };

   
将以此文件存为circle.js,并新建一个app.js文件,并写入以下代码:

1 var circle = require('./circle.js'); 
2 console.log( 'The area of a circle of radius 4 is ' + circle.area(4));

   
可以见见模块调用也丰硕有利,只需求require须要调用的文本即可。

   
在require了那些文件之后,定义在exports对象上的主意便足以无限制调用。Node.js将模块的定义和调用都打包得最为简约方便,从API对用户自己这个角度来说,Node.js的模块机制是非凡美丽的。

 

模块载入策略

   
Node.js的模块分为两类,一类为原生(焦点)模块,一类为文件模块。原生模块在Node.js源代码编译的时候编译进了二进制执行文书,加载的快慢最快。另一类公事模块是动态加载的,加载速度比原生模块慢。可是Node.js对原生模块和文件模块都进展了缓存,于是在第二次require时,是不会有再次开销的。其中原生模块都被定义在lib这些目录下边,文件模块则不定性。

node app.js

   
由于通过命令行加载启动的文书大约都为文件模块。大家从Node.js怎么样加载文件模块开头谈起。加载文件模块的办事,主要由原生模块module来已毕和到位,该原生模块在启动时已经被加载,进度一直调用到runMain静态方法。

1 // bootstrap main module. 
2 Module.runMain = function () {
3     // Load the main module--the command line argument. 
4     Module._load(process.argv[1], null, true); 
5 };

   
_load静态方法在解析文件名自此执行

var module = new Module(id, parent);

    并按照文件路径缓存当前模块对象,该模块实例对象则按照文件名加载。

module.load(filename);

   
实际上在文书模块中,又分为3类模块。那三类文件模块然后缀来不相同,Node.js会依据后缀名来控制加载方法。

  • .js。通过fs模块同步读取js文件并编译执行。 
  • .node。通过C/C++举办编制的Addon。通过dlopen方法开展加载。 
  • .json。读取文件,调用JSON.parse解析加载。

   
这里我们将详细描述js后缀的编译进度。Node.js在编译js文件的历程中实际到位的手续有对js文件内容展先导尾包装。

    以app.js为例,包装之后的app.js将会成为以下方式:

1 (function (exports, require, module, __filename, __dirname) { 
2     var circle = require('./circle.js');
3     console.log('The area of a circle of radius 4 is ' + circle.area(4)); 
4 });

   
这段代码会通过vm原生模块的runInThisContext方法执行(类似eval,只是有所无可争辨上下文,不传染全局),重回为一个实际的function对象。最终传入module对象的exports,require方法,module,文件名,目录名作为实参并履行。

    那就是为啥require并没有定义在app.js
文件中,不过这些措施却存在的原故。从Node.js的API文档中可以看看还有__filename、__dirname、module、exports多少个没有定义然而却存在的变量。其中__filename和__dirname在查找文件路径的进程中分析获得后传入的。module变量是以此模块对象自我,exports是在module的构造函数中开端化的一个空对象({},而不是null)。

   
在那个主文件中,可以通过require方法去引入其余的模块。而事实上那些require方法其实调用的就是load方法。

   
load方法在载入、编译、缓存了module后,重返module的exports对象。那就是circle.js文件中唯有定义在exports对象上的办法才能被外表调用的原委。

    以上所讲述的模块载入机制均定义在lib/module.js中。

 

Your Comments

近期评论

    功能


    网站地图xml地图