J历史

  我们都知道,js在刚被创建的时候,只是为了在网页上写一些小脚本而已,比如网页特效,表单验证等等,创立者也许没觉悟到以后的js会发展到如此规模。这是web1.0时代。

  在web 2.0时代,各种前端库,前端框架被开发出来,jquery,angular就是代表。此时js的功能也就从写写小特效啥的跃迁到了应用开发的级别上。可以说,js经历了工具类库,组件库,前端框架,前端应用的变迁。

  于是在越来越广泛的应用中,js暴露了它先天就缺乏的一项功能:模块。在其它高级语言,都有模块的定义,java有类,python有import机制,ruby有require,php有require和include。而js此时还单纯的用script标签引入,用命名空间来约束代码,杂乱无章,于是,commonjs规范便应运而出。

  commonJS的模块规范

  commonjs规范的出发点就是让js在任何地方都能运行。它弥补了js此时的几点缺陷:

  没有模块概念

  标准库较少

  没有标准接口

  没有包管理系统

  当然,如今commonjs规范已经解决了大部分问题,而且涵盖了模块,二进制,Buffer,字符集编码,I/O流,单元测试,web服务网关接口,包管理,等。

  commonjs对模块的定义很简单,包含了模块定义,模块引用,模块标识3个部分。

  模块引用

  var xx = require('xxx')

  关键字require来接受模块标识,引入这个模块的API到上下文中。

  模块定义

  一个例子来解释:

  //add.jsfunction add(a,b){ return a+b;

  }// 这样导出的 add是作为 exports 的一个方法被导出的exports.add = add;// main.jsvar Add = require('add');console.log(Add.add(1,2));//Add是require引入的模块名,add是方法名。

  node在编译的时候,会把代码封装成如下的样子

  // require 是对 Node.js 实现查找模块的 Module._load 实例的引用// __finename 和 __dirname 是 Node.js 在查找该模块后找到的模块名称和模块绝对路径(function(exports,require,module,__filename,__dirname){ function add (a,b){ return a+b;

  }

  exports.add = add;

  })

  为了将函数直接导出成一个模块,而不是一个方法,用到了全局变量module,下面就是我们常见的样子了:

  // add.jsfunction add (a,b){ return a+b ;

  }module.exports = add;// main.jsvar add = require('add');console.log(add(1,2));

  模块标识

  模块标识就是require()里的参数,必须是小驼峰命名的字符串,或者是路径(./ 或../)。可以没有后缀.js

  Node的模块机制

  Node并不是完全按照commonjs规范来实现,而是进行了一些取舍,并增加了自己的一些特性。

  Node中引入模块,经历3个步骤:

  路径分析

  文件定位

  编译执行

  Node中模块分为2种,核心模块(Node提供的)和文件模块(用户自己编写的)

  Node对引入过的模块会进行缓存,就像前端浏览器会缓存静态脚本来提高性能一样。require()方法在对同一模块的二次加载一律采用缓存优先的方式。但是对核心模块的缓存检查优先于对文件模块的缓存检查。

  路径分析

  就是对模块标识的分析呗。

  模块标识符在Node中分以下几类:

  核心模块,如http,path

  ./ ../相对路径

  / 绝对路径

  非路径形式的文件模块。

  文件分析

  如果模块标识符没有后缀,默认补上后缀从.js,.json,.node来次序查找。

  模块编译

  js编译上面已经提到了。Node对JS文件进行了包装,在头部添加了(function (exports, require, module, __filename, __dirname) {...})。这样每个模块都进行了作用域隔离。包装之后通过vm原生模块的runInThisContext()方法执行(类似eval,只是具有明确上下文,不污染全局)返回一个function。然后将上述参数传给这个function执行。

  json编译更简单,Node直接用JSON.parse()方法编译json内容,得到的对象赋给exports。

  包和NPM

  commonjs的包规范包含2个组成部分,包结构和包描述文件

  包描述文件:package.json

  包结构:

  package.json 包描述文件

  bin: 存放可执行二进制文件的目录

  lib 存放js代码的目录

  doc. 存放文档的目录

  test: 存放单元测试用例的代码

  NPM的用法就不多说了。

  前后端公用模块

  自从Node出来以后,js也可以运用在后端。但是前后端的JS扮演的角色不同,浏览器端的js需要经历从同一个服务器分发到多个客户端执行。服务端的js则是相同的代码多次执行。前者的瓶颈在于带宽。后者的瓶颈在于CUP和内存。前者需要通过网络加载。后者从磁盘中加载。

  因为Node基于commonjs规范来同步的加载模块的。前端若用同步方式来加载模块,在用户体验上会造成很大的问题。UI在初始化的时候需要等待很长时间来加载js脚本。所以提出了异步模块定义AMD和CMD。这在我的另一篇博客中有提到。就不多说了。

  为了写个能兼容前后端的模块规范,类库的开发者要把代码包装在一个 闭包里。这样就能兼容Node,AMD,CMD和常见的浏览器。

浅谈NodeJs的模块机制的更多相关文章

  1. 浅谈Python时间模块

    浅谈Python时间模块 今天简单总结了一下Python处理时间和日期方面的模块,主要就是datetime.time.calendar三个模块的使用.希望这篇文章对于学习Python的朋友们有所帮助 ...

  2. 浅谈Java的反射机制和作用

    浅谈Java的反射机制和作用 作者:Java大师 欢迎转载,转载请注明出处 很多刚学Java反射的同学可能对反射技术一头雾水,为什么要学习反射,学习反射有什么作用,不用反射,通过new也能创建用户对象 ...

  3. 浅谈:Redis持久化机制(一)RDB篇

    浅谈:Redis持久化机制(一)RDB篇 ​ 众所周知,redis是一款性能极高,基于内存的键值对NoSql数据库,官方显示,它的读效率可达到11万次每秒,写效率能达到8万次每秒,因为它基于内存以及存 ...

  4. 浅谈:Redis持久化机制(二)AOF篇

    浅谈:Redis持久化机制(二)AOF篇 ​ 上一篇我们提及到了redis的默认持久化方式RDB,是一种通过存储快照数据方式持久化的机制,它在宕机后会丢失掉最后一次更新RDB文件后的数据,这也是由于它 ...

  5. 浅谈C语言中断处理机制

    一.中断机制 1.实现中断响应和中断返回 当CPU收到中断请求后,能根据具体情况决定是否响应中断,如果CPU没有更急.更重要的工作,则在执行完当前指令后响应这一中断请求.CPU中断响应过程如下:首先, ...

  6. 浅谈 ArrayList 及其扩容机制

    浅谈ArrayList ArrayList类又称动态数组,同时实现了Collection和List接口,其内部数据结构由数组实现,因此可对容器内元素实现快速随机访问.但因为ArrayList中插入或删 ...

  7. 60.浅谈nodejs中的Crypto模块

    转自:https://www.cnblogs.com/c-and-unity/articles/4552059.html node.js的crypto在0.8版本并没有改版多少,这个模块的主要功能是加 ...

  8. 浅谈nodejs中HTTP模块应用

    这里给大家分享下后端人员如果利用nodejs对数据的一些处理情况  适用于初学者使用 大牛勿喷 给大家分享下主要后端思想部分代码,前端部分就不展示了 const http = require(&quo ...

  9. 浅谈Nodejs应用的主文件index.js的组成部分

    前言 Node妹子的问世,着实让我们前端攻城狮兴奋了一把,尤其本屌听说Javascript可以写服务端后,兴奋的像是看到了二次元萝莉的胖子...(●'◡'●).呃哼...YY先到这里,原谅本屌是个二次 ...

随机推荐

  1. excel判断单元格包含指定内容的函数用=IF(COUNTIF(A1,"*内容*"),"0","1")

    前面我们聊过怎样将Excel包含某字符的单元格填充颜色,这边我们用另外一种方法来实现:excel判断单元格包含指定内容的函数 选中需要显示结果的单元格,假设我们要判断第一行第一列的单元格A1是否含有“ ...

  2. python学习笔记(二十三)私有方法和私有属性

    在Class内部,可以有属性和方法,而外部代码可以通过直接调用实例变量的方法来操作数据,这样,就隐藏了内部的复杂逻辑. 但是,从前面Person类的定义来看,外部代码还是可以自由地修改一个实例的nam ...

  3. ambari关于ranger的一个大坑----端口永远是3306,需要手动修改

    ambari关于ranger的一个大坑----端口永远是3306 这个坑是我在搭建ambari环境的时候发现的,我并没有找到原因,求助同事,然后一步步循着蛛丝马迹和试探,终于解决了,然而也揭露了amb ...

  4. ftp 工作原理

  5. 汇编文件后缀.s与.S

    转载:http://www.cnblogs.com/IamEasy_Man/archive/2011/08/10/2134212.html 一.大小写后缀的区别: .s:  汇编语言源程序;汇编 .S ...

  6. lua在线手册汇总

    1. Lua官方参考手册 Lua 4.0 : http://www.lua.org/manual/4.0/Lua 5.0 : http://www.lua.org/manual/5.0/Lua 5.1 ...

  7. CCF 字符串匹配(find()函数的使用)

    问题描述 试题编号: 201409-3 试题名称: 字符串匹配 时间限制: 1.0s 内存限制: 256.0MB 问题描述: 问题描述 给出一个字符串和多行文字,在这些文字中找到字符串出现的那些行.你 ...

  8. ruby中的私有方法和保护方法

    ruby中的私有方法是指方法只能被隐含调用,不能被显示调用.而当没有显示接收者的时候,会把self当成接收者.因此,只能在自身中调用私有方法,这也是私有方法的调用规则. ruby的私有方法机制目的是: ...

  9. 《UML和模式应用》读书笔记(一)面向对象分析和设计简单示例

    在开始进行对象分析和设计之前,先通过“扔骰子”这个软件(游戏者扔两个骰子,如果总是是7,则赢,否则输),来简单分析下这个过程. 1:用例 需求分析,可能包括人们如何应用的场景或情节,这些都可以被编写成 ...

  10. Java 创建数组的方式, 以及各种类型数组元素的默认值

    ①创建数组的方式3种 ①第1种方法 public class MyTest { public static void main(String[] args){ //method 1 int[] arr ...