java require_[Java教程]你知道require是什么吗?

[Java教程]你知道require是什么吗?

0 2014-12-19 22:01:33

引题

用过node的同学应该都知道require是用来加载模块的,那你是否存在如下的疑问呢?1. require(path)是如何依据path找到对应module呢?

2. 为何在模块定义中,一定要通过module.exports暴漏出接口?module.exports与require存在什么关系

对上述问题进行概括可以抽象出如下两个问题:1. module的路径分析

2. 文件加载

切入

首先来直观地看看require是什么?// node环境下执行:console.log(require.toString)//输入结果为:'function require(path) {\n return self.require(path);\n }'

上述代码说明require函数仅仅是module.require的封装,这样就需要查看node中的module源代码了。

加载模块的方式

首先来直观来认识一下node的模块加载方式有哪些方式:

case 1:// 'path'为node的核心模块

var path = require('path')

case2:// a.js,路径为: basePath/a.jsvar myModule = require('./my-module')

// my-module的路径为basePath/node_modules/myModule.js

case 3:// a.js, 路径: basePath/a.jsvar main = require('./')

// basePath下还包括package.json, index.js

路径解析

在node的官方API中,我们可以找到这段描述:To get the exact filename that will be loaded when require() is called, use the require.resolve() function.

Putting together all of the above, here is the high-level algorithm in pseudocode of what require.resolve does:  ......

试试在node环境下用用require.resolve这个API:require.resolve('./a.js')// 这样就得到a.js的绝对路径

为了探索缘由,就从node核心代码中的mdoule.js找答案吧:require.resolve = function(request) {  return Module._resolveFilename(request, self);}Module._resolveFilename = function(request, parent) {  // 判断是否为node的核心模块  if (NativeModule.exists(request)) {   return request;  } // 得到查询路径,格式为数组:[id, [paths]] var resolvedModule = Module._resolveLookupPaths(request, parent); var paths = resolvedModule[1];  // 根据path、fileName得到绝对路径  var filename = Module._findPath(request, paths); return filename;}

那Module._resolveLookupPaths是如何得到所有查询路径的呢?为node的核心模块,stop

以./或../开头,本地查找, stop

沿着文件树,得到node_module的所有路径,直到/node_modules,在node_module中查找,stop

path为目录,则检查package.json文件是否存在main属性,否则默认为index.js

最后返回new Error('Cannot find module"' + request + '"');

模块加载

先看require的源代码:// 我们经常使用的require函数function require(path) { return self.require(path);}// 调用_load函数,加载所需的模块Module.prototype.require = function(path) {  return Module._load(path, this);}

这样模块函数的调用连接到了Module._load函数:Module.cache = {};Module._load = function() {  // 检测模块是否已经加载过  var cachedModule = Module._cache[filename]; if (cachedModule) {   return cachedModule.exports; } // 模块还未加载,则为模块创建module实例 var module = new Module(filename, parent);  // 新创建的实例存储于cache中  Module._cache[filename] = module; // 开始获取模块的内容 module.load(filename); // 对外提供接口 return module.exports;}

接下来问题的关键就变成了module.load,该方法用于获取module的内容,然后进行解析:Module.prototype.load = function(filename) {  // 解析出文件的后缀, 存在['.js', '.json', 'node']三种后缀  var extension = path.extname(filename) || '.js';  // 根据后缀,获取相关的模块  Module._extensions[extension](this, filename);}

node会匹配按照.js、.json、.node三种格式进行模块匹配,根据文件类型的不同采取不同的加载策略,但是以实际开发中以加载.js最多,该种策略最后需要调用Module.prototype._compile进行编译处理:Module._extensions['.js'] = function(module, filename) { var content = fs.readFileSync(filename, 'utf8'); module._compile(stripBOM(content), filename);};Module.prototype._compile = function(content, filename) {  //将内容放入到(function() { content }),形成闭包,创建私有作用域  var wrapper = Module.wrap(content);  // bind新的执行上下文  var compiledWrapper = runInThisContext(wrapper, { filename: filename });  // 向外暴漏接口:module.exports, require, module,__filename, __dirname,   var args = [self.exports, require, self, filename, dirname];   return compiledWrapper.apply(self.exports, args);}

这样,我们就可以在require来获取相应地module。

结论

node现在这么火,各种优势铺天盖地涌来,会让刚刚入行的人觉得深不可测,因而往往会让人望而却步。但是只要我们敢于突破第一步,深入下来仔细分析,就会发现其实没有那么晦涩难懂,踏出第一步真的很关键!

参考资料

http://thenodeway.io/posts/get-fancy/how-require-actually-works/

https://github.com/joyent/node/blob/master/lib/module.js

http://nodejs.org/api/modules.html

https://github.com/substack/node-resolve

本文网址:http://www.shaoqun.com/a/107112.html

*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们:admin@shaoqun.com。

0

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值