zoukankan      html  css  js  c++  java
  • 深入理解node.js的module.export 和 export方法的区别

    你肯定非常熟悉nodejs模块中的exports对象,你可以用它创建你的模块。例如:(假设这是rocker.js文件)

    exports.name = function() {
        console.log('My name is Lemmy Kilmister');
    };

    在另一个文件中你这样引用

    var rocker = require('./rocker.js');
    rocker.name(); // 'My name is Lemmy Kilmister'

    那到底Module.exports是什么呢?它是否合法呢?

    其实,Module.exports才是真正的接口,exports只不过是它的一个辅助工具。 最终返回给调用的是Module.exports而不是exports。

    所有的exports收集到的属性和方法,都赋值给了Module.exports。当然,这有个前提,就是Module.exports本身不具备任何属性和方法。如果,Module.exports已经具备一些属性和方法,那么exports收集来的信息将被忽略。

    修改rocker.js如下:

    module.exports = 'ROCK IT!';
    exports.name = function() {
        console.log('My name is Lemmy Kilmister');
    };

     再次引用执行rocker.js

    var rocker = require('./rocker.js');
    rocker.name(); // TypeError: Object ROCK IT! has no method 'name'

    发现报错:对象“ROCK IT!”没有name方法

    rocker模块忽略了exports收集的name方法,返回了一个字符串“ROCK IT!”。由此可知,你的模块并不一定非得返回“实例化对象”。你的模块可以是任何合法的javascript对象--boolean, number, date, JSON, string, function, array等等。

    你的模块可以是任何你设置给它的东西。如果你没有显式的给Module.exports设置任何属性和方法,那么你的模块就是exports设置给Module.exports的属性。

    下面例子中,你的模块是一个类:

    复制代码
    复制代码
    module.exports = function(name, age) {
        this.name = name;
        this.age = age;
        this.about = function() {
            console.log(this.name +' is '+ this.age +' years old');
        };
    };
    复制代码
    复制代码

    可以这样应用它:

    var Rocker = require('./rocker.js');
    var r = new Rocker('Ozzy', 62);
    r.about(); // Ozzy is 62 years old

    下面例子中,你的模块是一个数组:

    module.exports = ['Lemmy Kilmister', 'Ozzy Osbourne', 'Ronnie James Dio', 'Steven Tyler', 'Mick Jagger'];

    可以这样应用它:

    var rocker = require('./rocker.js');
    console.log('Rockin in heaven: ' + rocker[2]); //Rockin in heaven: Ronnie James Dio

    现在你明白了,如果你想你的模块是一个特定的类型就用Module.exports。如果你想的模块是一个典型的“实例化对象”就用exports

    给Module.exports添加属性类似于给exports添加属性。例如:

    module.exports.name = function() {
        console.log('My name is Lemmy Kilmister');
    };

    同样,exports是这样的

    exports.name = function() {
        console.log('My name is Lemmy Kilmister');
    };

    请注意,这两种结果并不想同。前面已经提到module.exports是真正的接口,exports只不过是它的辅助工具。推荐使用exports导出,除非你打算从原来的“实例化对象”改变成一个类型

    Node.js在模块编译的过程中会对模块进行包装,最终会返回类似下面的代码:

    (function (exports, require, module, __filename, __dirname) {
        // module code...
    });
    

    其中,module就是这个模块本身,require是对Node.js实现查找模块的模块Module._load实例的引用,__filename__dirname是Node.js在查找该模块后找到的模块名称和模块绝对路径,这就是官方API里头这两个全局变量的来历。

    关于module.exports与exorts的区别,了解了下面几点之后应该就完全明白:

    模块内部大概是这样:

    exports = module.exports = {};  
    
    • exports是module.exports的一个引用

    • require引用模块后,返回给调用者的是module.exports而不是exports

    • exports.xxx,相当于在导出对象上挂属性,该属性对调用模块直接可见

    • exports =相当于给exports对象重新赋值,调用模块不能访问exports对象及其属性

    • 如果此模块是一个类,就应该直接赋值module.exports,这样调用者就是一个类构造器,可以直接new实例

    客官如果看明白咋回事儿了下面的内容可以忽略:)

    假如有模块a.js代码如下:

    exports.str = 'a';  
    exports.fn = function() {};  
    

    对a模块的调用:

    var a = require('./a');  
    console.log(a.str);  
    console.log(a.fn());  
    

    这样用是对的,如果改造a如下:

    exports.str = 'a';  
    exports = function fn() {};  
    

    在调用a模块时自然没用fn属性了。

    再改造下a模块:

    exports.str = 'a';  
    module.exports = function fn() {};  
    

    这时a模块其实就是fn函数的引用,也就是说可以require('./a')()这样使用,而同时不再有str属性了。

    下面直接导出一个类:

    module.exports = function A() {};  
    

    调用:

    var A = require('./a');  
    var a = new A();  
    

    总结下,有两点:

    1. 对于要导出的属性,可以简单直接挂到exports对象上

    2. 对于类,为了直接使导出的内容作为类的构造器可以让调用者使用new操作符创建实例对象,应该把构造函数挂到module.exports对象上,不要和导出属性值混在一起

    很多新手可能会迷惑于 exports 和 module.exports 的区别,为了更好的理解 exports 和 module.exports 的关系,我们先来巩固下 js 的基础。示例:

    test.js

    var a = {name: 1};
    var b = a;
    
    console.log(a);
    console.log(b);
    
    b.name = 2;
    console.log(a);
    console.log(b);
    
    var b = {name: 3};
    console.log(a);
    console.log(b);
    

    运行 test.js 结果为:

    { name: 1 }
    { name: 1 }
    { name: 2 }
    { name: 2 }
    { name: 2 }
    { name: 3 }
    

    解释:a 是一个对象,b 是对 a 的引用,即 a 和 b 指向同一块内存,所以前两个输出一样。当对 b 作修改时,即 a 和 b 指向同一块内存地址的内容发生了改变,所以 a 也会体现出来,所以第三四个输出一样。当 b 被覆盖时,b 指向了一块新的内存,a 还是指向原来的内存,所以最后两个输出不一样。

    明白了上述例子后,我们只需知道三点就知道 exports 和 module.exports 的区别了:

    1. module.exports 初始值为一个空对象 {}
    2. exports 是指向的 module.exports 的引用
    3. require() 返回的是 module.exports 而不是 exports

    Node.js 官方文档的截图证实了我们的观点:

    exports = module.exports = {...}

    我们经常看到这样的写法:

    exports = module.exports = {...}
    

    上面的代码等价于:

    module.exports = {...}
    exports = module.exports
    

    原理很简单:module.exports 指向新的对象时,exports 断开了与 module.exports 的引用,那么通过 exports = module.exports 让 exports 重新指向 module.exports。

  • 相关阅读:
    临时表和表变量
    北航发生一场悲剧:计算机学子复习过程中突然倒地
    2w+长文带你剖析ConcurrentHashMap~!
    2020年最新ZooKeeper面试题(附答案)
    MQ消息中间件,面试能问些什么?
    设计模式之初体验
    IIS7 下使用ExtJs出现Ext未定义的问题
    修改apache+php上传文件大小限制
    memcache support requires ZLIB解决办法
    Windows下Memcache的安装方法
  • 原文地址:https://www.cnblogs.com/axl234/p/6405370.html
Copyright © 2011-2022 走看看