资讯专栏INFORMATION COLUMN

Node.js design pattern : module

focusj / 1880人阅读

摘要:本文转发自本人。这个例子通过对进行计算,即通过函数处理该字符串脚本。实现的功能与函数类似,但比函数更强大。返回模块中暴露的接口以供调用。但如果给重新赋值,则会失去的引用此时,意味着通过暴露的接口是无效的,没有添加到中的中。

Node.js design pattern一书中对Node的Module模块机制这一块,我觉得讲的挺透彻和易懂,这里根据自己理解做下总结。本文转发自本人github。

loadModule

自定义一个简单的模块加载方法loadModule,基本思路跟nodejs一致,将加载的模块内容包裹在一个函数里面实现变量的隔离,保证模块内的变量都是私有的。

    function loadModule(filename, module, require) {
        const wrappedSrc = `(function(module, exports, require) {
            ${fs.readFileSync(filename, "utf8")}
        })(module, module.exports, require);`;
    
        eval(wrappedSrc);
    }

这个例子通过evalwrappedSrc进行计算,即通过eval函数处理该字符串脚本。因为这里要把(function(module, exports, require) { 这串东西和 fs.readFileSync(filename, "utf8")加载的模块内容合并在一起作为新的整合代码再运行,所以必须借助eval函数。

作为对比,在nodejs源码中, wrap是这样实现的

    Module.wrap = function(script) {
          return Module.wrapper[0] + script + Module.wrapper[1];
    };

    Module.wrapper = [
      "(function (exports, require, module, __filename, __dirname) { ",
      "
});"
    ];

最终对该warpper的解析实现在Module.prototype._compile方法中,其中用到了vm模块对wrapper脚本进行处理。vm实现的功能与eval函数类似,但比eval函数更强大。

模块引用require()

对模块的引用我们通过require(..)函数进行引用,如var http = require("http"),该方法简单实现如下:

    const require = (moduleName) => {
        console.log(`Require invoked for module: ${moduleName}`);
        const id = require.resolve(moduleName);
        if (require.cache[id]) { return require.cache[id].exports; }
    
        // 1.module metadata
        const module = {
            exports: {},
            id: id
        }
    
        // 2.require.cache
        require.cache[id] = module;
    
        // 3.load the module
        loadModule(id, module, require);
    
        // 4.return exported variables
        return module.exports;
    }
    
    require.cache = {};
    require.resolve = (moduleName) => {
        /* resolve a full module id from the moduleName */
    }

定义了一个module对象用来保存通过loadModule方法中加载模块中暴露出的接口。

将第一次加载的模块保存在内部缓存中。即第二次调用require(..)时不会再调用loadModule方法,直接从cache中返回。

通过loadModule方法通过模块路径加载模块内容。

返回模块中暴露的接口以供调用。

可以通过下图更加直观的了解其中的关系。

module.exports vs exports

通过上述代码和图示可知,我们写的模块中的exports其实是对module.exports的引用。 因此我们可以通过exports添加属性来给module.exports引用的对象添加属性。

    exports.hello = () => { console.log("Hello") };

但如果给exports重新赋值,则会失去module.exports的引用

    exports = () => { console.log("Hello") };

此时exports !== module.exports,意味着通过exports暴露的接口是无效的,没有添加到module metadata中的exports中。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/92473.html

相关文章

  • [譯 + 更新] 參透 Node 中 exports 的 7 種設計模式

    摘要:現在,我們可以開始探討介面的設計模式了。匯出命名空間一個簡單且常用的設計模式就是匯出一個包含數個屬性的物件,這些屬性具體的內容主要是函式,但並不限於函式。如此,我們就能夠透過匯入該模組來取得這個命名空間下一系列相關的功能。 前言 這篇文章試著要整理,翻譯Export This: Interface Design Patterns for Node.js Modules這篇非常值得一讀的...

    wmui 评论0 收藏0
  • 2017-08-10 前端日报

    摘要:前端日报精选译用搭建探索生命周期中的匿名递归浏览器端机器智能框架深入理解笔记和属性中文上海线下活动前端工程化架构实践沪江技术沙龙掘金周二放送追加视频知乎专栏第期聊一聊前端自动化测试上双关语来自前端的小段子,你看得懂吗众成翻 2017-08-10 前端日报 精选 [译] 用 Node.js 搭建 API Gateway探索 Service Worker 「生命周期」JavaScript ...

    wupengyu 评论0 收藏0
  • JavaScript设计模式

    摘要:此处的构造函数使用的形式添加新属性,但实际上新属性的添加有四种方式,除去这一种,还有三种方括号语法,方法方法此处举的是原文中的例子,若要使用,可参考原文。 参考书籍Learning Javascript Design Patterns 一、设计模式概述与应用场景 首先引用原书的一段话: Patterns are proven solutions: They provide solid ...

    dreambei 评论0 收藏0
  • 2017-10-08 前端日报

    摘要:前端日报精选读书思考一的计算属性使用开发调试开发者控制台中,你可能意想不到的功能中字符串转数字的陷阱和示例中文设计模式单例模式个人文章设计模式工厂模式个人文章读书思考二掘金网络基础三传输层的笔记学习笔记中的属性学习 2017-10-08 前端日报 精选 Node.js Design Patterns - Second Edition读书思考(一)Vue的计算属性_Vue使用typesc...

    Cciradih 评论0 收藏0
  • 山地人的2019年前端自学路径Roadmap——v0.0.1

    摘要:为何会有这个自学也需要有章可寻,早上整理了一下这段时间学的内容东西比较多,接下来的一段时间都会围绕这个展开学习,当然这张前端自学图谱并不是一成不变的,随着时间的推移我会调整这张里的内容,总的目标只有一个系统化的学好前端的技术,我会随时调整完 为何会有这个Roadmap 自学也需要有章可寻,早上整理了一下这段时间学的内容东西比较多,接下来的一段时间都会围绕这个Roadmap展开学习,当然...

    Hegel_Gu 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<