【问题标题】:What is a module and difference between module.exports vs exports?module.exports 与 export 之间的模块和区别是什么?
【发布时间】:2014-02-23 14:50:33
【问题描述】:

我已经阅读这个主题几个小时了,但没有找到任何可以帮助坚持下去的东西。

模块只是节点中具有一些属性的对象,一个是引用对象的导出属性。

“出口”变量是

var exports = module.exports

它是一个指向 module.exports 引用的对象的 var。

我正在努力想象模块是什么。我知道这是一个对象,但只有一个吗?

我知道这不是节点实现模块的确切方式,但我将其可视化如下:

var module = {}

module.exports = {}

// now module has a property module.exports

var exports = module.exports

现在,根据我所阅读的所有内容,如果您要为 module.export = 'xyz' 分配一些东西

它将保存值“xyz”。它会丢失原始对象吗?最重要的是,如果我在同一个文件中为 module.exports 分配了其他内容,它会被新值替换吗?

EX: 

// file = app.js

module.export = 'hello'
module.export = 'bye'

// file = newApp.js

require(./app);

模块的价值是什么?我是覆盖同一个模块对象还是有多个?

【问题讨论】:

    标签: javascript node.js


    【解决方案1】:

    您正在覆盖模块 - 导出是使用 require 拉入的单个对象。通常,当使用require 执行这种模块化 JavaScript 时,您的导出将是一个构造函数,而不是像示例中的字符串这样的原语。这样,您可以创建模块中定义的功能的新实例。示例:

    // module.js
    var MyConstructor = function(prop) {
        this.prop = prop;
    });
    
    module.exports = MyConstructor;
    
    // app.js
    var MyConstructor = require('module');
    var instance = new MyConstructor('test');
    console.log(instance.prop) // 'test'
    

    【讨论】:

      【解决方案2】:

      Node.js 中的模块只是文件。每个文件都是一个模块,每个模块都是一个文件。如果您有多个文件,则可以有多个模块(每个文件一个)。

      根据module.exportsthe Node.js API documentation 将阐明该主题:

      module.exports 对象由模块系统创建。有时 这是不可接受的;许多人希望他们的模块成为 一些类。为此,将所需的导出对象分配给 module.exports。请注意,将所需对象分配给 exports 将 只需重新绑定本地 exports 变量,这可能不是什么 你想做。

      模块中可用的exports 变量以 参考module.exports。与任何变量一样,如果您分配一个新的 值,它不再绑定到以前的值。 ... 作为指导方针, 如果exports和module.exports的关系 对你来说似乎很神奇,忽略导出,只使用 module.exports。

      那么,这一切意味着什么?在您的特定示例中,module.exports 将等于它的最后分配值 ('bye')。

      【讨论】:

        【解决方案3】:

        在我们继续之前,请务必了解how modules are actually loaded by node

        从节点的模块加载系统中删除的关键是在它实际运行代码之前require(发生在Module#_compile),它creates 一个新的空exports 对象作为属性Module 的。 (换句话说,你的可视化是正确的。)

        节点然后wrapsrequired 文件中带有匿名函数的文本:

        (function (exports, require, module, __filename, __dirname) {
        // here goes what's in your js file
        });
        

        ...基本上是evals 那个字符串。 evaling 该字符串的结果是一个函数(匿名包装器),节点立即调用该函数,传入如下参数:

        evaledFn(module.exports, require, module, filename, dirname);
        

        requirefilenamedirname 是对require 函数(实际上是isn't a global)的引用,以及包含已加载模块路径信息的字符串。 (这就是docs 的意思,当他们说“__filename 实际上不是全局的,而是每个模块的本地。”)

        所以我们可以在模块内部看到,确实是exports === module.exports。但是为什么模块会同时获得moduleexports

        向后兼容性。在节点的早期,模块内部没有module 变量。您只需分配给exports。但是,这意味着您永远不能将构造函数导出为模块本身。

        一个熟悉的模块将构造函数导出为模块的示例:

        var express = require('express');
        var app = express();
        

        这是因为Express exports a function by reassigning module.exports,覆盖了node默认给你的空对象:

        module.exports = function() { ... }
        

        但是,请注意,您不能只写:

        exports = function { ... }
        

        这是因为JavaScript's parameter passing semantics are weird。从技术上讲,JavaScript 可能被认为是“纯按值传递”,但实际上参数是“按值引用”传递的。

        这意味着当您将对象传递给函数时,它会接收对该对象的引用作为值。您可以通过该引用访问原始对象,但您不能改变调用者对引用的看法。换句话说,没有像您在 C/C++/C# 中看到的那样的“out”参数。

        作为一个具体的例子:

        var obj = { x: 1 };
        
        function A(o) {
            o.x = 2;
        }
        function B(o) {
            o = { x: 2 };
        }
        

        调用A(obj); 将导致obj.x == 2,因为我们访问的是作为o 传入的原始对象。但是,B(obj); 不会做任何事情; obj.x == 1。将一个全新的对象分配给B 的本地o 只会更改o 指向的内容。它对调用者的对象obj 没有任何作用,仍然不受影响。

        现在应该很明显为什么有必要将module 对象添加到节点模块的本地范围。为了允许模块完全替换exports 对象,它必须作为传递给模块匿名函数的对象的属性可用。显然没有人想破坏现有的代码,所以 exports 被留下作为对 module.exports 的引用。

        因此,当您只是为导出对象分配属性时,使用exportsmodule.exports 并不重要;它们是相同的,指向完全相同的对象的引用。

        仅当您要将函数导出为顶级导出时,您必须使用module.exports,因为正如我们所见,只需将函数分配给exports在模块范围之外没有任何影响。

        最后一点,当您将函数导出为模块时,最好将exportsmodule.exports 分配给两者。这样一来,两个变量都保持一致,并且与它们在标准模块中的工作方式相同。

        exports = module.exports = function() { ... }
        

        请务必在模块文件顶部附近执行此操作,以免有人意外分配给最终被覆盖的 exports

        另外,如果您觉得这很奇怪(三个 =s?),我正在利用包含assignment operator 的表达式返回分配值的事实,这使得可以一次将单个值分配给多个变量。

        【讨论】:

        • express 同时分配两个变量的唯一原因真的只是“良好做法”或“以防万一”吗?真的没有其他原因,甚至没有更好甚至需要的边缘情况吗?
        猜你喜欢
        • 2018-12-31
        • 2013-06-10
        • 2017-11-11
        • 2021-12-18
        • 2022-11-18
        • 1970-01-01
        • 1970-01-01
        • 2022-11-27
        • 2019-09-13
        相关资源
        最近更新 更多