您的位置:新葡亰496net > 新葡亰官网 > 新葡亰496net:在 Node.js 中看 JavaScript 的引用

新葡亰496net:在 Node.js 中看 JavaScript 的引用

发布时间:2019-08-10 19:31编辑:新葡亰官网浏览(149)

    在 Node.js 中看 JavaScript 的引用

    2017/05/05 · JavaScript · NodeJS

    原稿出处: lellansin   

    早先时代学习 Node.js 的时候 (二〇一三-二〇一二),有挺多是从 PHP 转过来的,当时有部分人对于 Node.js 编辑完代码要求重启一下象征麻烦(PHP无需以此进程),于是社区里的朋友就初阶发起使用 node-supervisor 这几个模块来运维项目,能够编写制定完代码之后自动重启。然而相对于 PHP 来讲依旧远远不足方便,因为 Node.js 在重启未来,以前的上下文都不见了。

    即使能够通过将 session 数据保存在数据库或然缓存中来收缩重启进度中的数据错失,不过若是是在生养的图景下,更新代码的重启间隙是无可奈何管理乞请的(PHP能够,另外极度时候 Node.js 还未有 cluster)。由于那上边的主题素材,加上自身是从 PHP 转到 Node.js 的,于是从那儿开端思虑,有未有法子能够在不重启的地方下热更新 Node.js 的代码。

    最初阶把目光瞄向了 require 这几个模块。主张很简单,因为 Node.js 中引进三个模块都是经过 require 这一个措施加载的。于是就起始思考 require 能否在立异代码之后再也 require 一下。尝试如下:

    a.js

    var express = require('express'); var b = require('./b.js'); var app = express(); app.get('/', function (req, res) { b = require('./b.js'); res.send(b.num); }); app.listen(3000);

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var express = require('express');
    var b = require('./b.js');
     
    var app = express();
     
    app.get('/', function (req, res) {
      b = require('./b.js');
      res.send(b.num);
    });
     
    app.listen(3000);

    b.js

    exports.num = 1024;

    1
    exports.num = 1024;

    八个 JS 文件写好之后,从 a.js 运维,刷新页面会输出 b.js 中的 1024,然后修改 b.js 文件中程导弹出的值,比方修改为 2048。再度刷新页面依旧是原来的 1024。

    再次实践一遍 require 并不曾刷新代码。require 在施行的历程中加载完代码之后会把模块导出的多少放在 require.cache 中。require.cache 是八个 { } 对象,以模块的相对路线为 key,该模块的详实数据为 value。于是便开端做如下尝试:

    a.js

    var path = require('path'); var express = require('express'); var b = require('./b.js'); var app = express(); app.get('/', function (req, res) { if (true) { // 检查文件是还是不是修改 flush(); } res.send(b.num); }); function flush() { delete require.cache[path.join(__dirname, './b.js')]; b = require('./b.js'); } app.listen(3000);

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    var path = require('path');
    var express = require('express');
    var b = require('./b.js');
     
    var app = express();
     
    app.get('/', function (req, res) {
      if (true) { // 检查文件是否修改
        flush();
      }
      res.send(b.num);
    });
     
    function flush() {
      delete require.cache[path.join(__dirname, './b.js')];
      b = require('./b.js');
    }
     
    app.listen(3000);

    重复 require 在此之前,将 require 之上关于该模块的 cache 清理掉后,用事先的点子重新测量检验。结果发掘,能够成功的基础代谢 b.js 的代码,输出新修改的值。

    问询到这几个点后,就想透过该原理达成四个无重启热更新版本的 node-supervisor。在包装模块的长河中,出于情怀的案由,思量提供三个像样 PHP 中 include 的函数来取代 require 去引进二个模块。实际内部仍旧是利用 require 去加载。以b.js为例,原来的写法改为 var b = include(‘./b’),在文书 b.js 更新之后 include 内部能够自行刷新,让外部获得新型的代码。

    只是实际上的支付进度中,那样神速就超越了难点。大家期望的代码也许是这么:

    web.js

    var include = require('./include'); var express = require('express'); var b = include('./b.js'); var app = express(); app.get('/', function (req, res) { res.send(b.num); }); app.listen(3000);

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var include = require('./include');
    var express = require('express');
    var b = include('./b.js');
    var app = express();
     
    app.get('/', function (req, res) {
      res.send(b.num);
    });
     
    app.listen(3000);

    但遵照那些指标封装include的时候,我们开采了难题。无论大家在include.js内部中怎样兑现,都不可能像开始那样获得新的 b.num。

    相对来说起来的代码,大家发掘难点出在少了 b = xx。也便是说这样写才得以:

    web.js

    var include = require('./include'); var express = require('express'); var app = express(); app.get('/', function (req, res) { var b = include('./b.js'); res.send(b.num); }); app.listen(3000);

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var include = require('./include');
    var express = require('express');
    var app = express();
     
    app.get('/', function (req, res) {
      var b = include('./b.js');
      res.send(b.num);
    });
     
    app.listen(3000);

    修改成这么,就足以确定保证每一遍能得以正确的刷新到最新的代码,并且永不重启实例了。读者有意思味的可以商量这一个include是怎么落到实处的,本文就不深远钻探了,因为那些本领使用度不高,写起起来不是很优雅[1],反而那当中有三个更重要的标题——JavaScript的援引。

    开始时代学习Node.js的时候,有挺多是从PHP转过来的,当时有一对人对于Node.js编辑完代码须求重启一下意味麻烦(PHP不须要以此历程),于是社区里的仇人就初阶发起使用node-supervisor那一个模块来运行项目,能够编写制定完代码之后自动重启。可是相对于PHP来讲依旧相当不足方便,因为Node.js在重启现在在此之前的上下文都不胫而走了。

    Node.js中看JavaScript的引用,node.jsjavascript

    开始时代学习 Node.js 的时候 (二〇一二-二〇一二),有挺多是从 PHP 转过来的,当时有局地人对此 Node.js 编辑完代码需求重启一下代表麻烦(PHP不必要那一个进度),于是社区里的意中人就起来发起使用 node-supervisor 那些模块来运维项目,能够编写制定完代码之后自动重启。然则相对于 PHP 来说依旧远远不够方便,因为 Node.js 在重启现在,此前的上下文都遗落了。

    尽管能够透过将 session 数据保存在数据库大概缓存中来减弱重启进度中的数据遗失,可是即使是在生养的情事下,更新代码的重启间隙是没办法处理央浼的(PHP能够,别的特别时候 Node.js 还不曾 cluster)。由于那地点的难点,加上本身是从 PHP 转到 Node.js 的,于是从那时候初步商量,有未有艺术能够在不重启的情状下热更新 Node.js 的代码。

    起头导把目光瞄向了 require 那几个模块。主张异常的粗略,因为 Node.js 中引进二个模块都以透过 require 那个点子加载的。于是就从头盘算 require 能否在立异代码之后再行 require 一下。尝试如下:

    a.js

    var express = require('express');
    var b = require('./b.js'); 
    var app = express();
    app.get('/', function (req, res) {
     b = require('./b.js');
     res.send(b.num);
     });
    app.listen(3000);
    

    b.js

    exports.num = 1024;
    

    多个 JS 文件写好之后,从 a.js 运维,刷新页面会输出 b.js 中的 1024,然后修改 b.js 文件中程导弹出的值,举例修改为 2048。再度刷新页面还是是本来的 1024。

    再一次实施二回 require 并从未刷新代码。require 在执行的长河中加载完代码之后会把模块导出的数额放在 require.cache 中。require.cache 是三个 { } 对象,以模块的相对路径为 key,该模块的详尽数据为 value。于是便起首做如下尝试:

    a.js

    var path = require('path');
    var express = require('express');
    var b = require('./b.js'); 
    var app = express();
    app.get('/', function (req, res) {
     if (true) { // 检查文件是否修改
     flush();
     }
     res.send(b.num);
     });
    function flush() {
     delete require.cache[path.join(__dirname, './b.js')];
     b = require('./b.js');
     }
    app.listen(3000);
    

    双重 require 此前,将 require 之上关于该模块的 cache 清理掉后,用事先的不二等秘书技重复测验。结果开采,能够成功的刷新 b.js 的代码,输出新修改的值。

    打听到这一个点后,就想通过该原理达成三个无重启热更新版本的 node-supervisor。在卷入模块的进程中,出于情怀的原由,思虑提供八个看似 PHP 中 include 的函数来代替 require 去引进三个模块。实际内部照旧是利用 require 去加载。以b.js为例,原来的写法改为 var b = include(‘./b'),在文书 b.js 更新之后 include 内部可以自行刷新,让外界拿到新型的代码。

    唯独实际的花费进程中,那样急忙就遇上了难题。大家希望的代码也许是这么:

    web.js

    var include = require('./include');
    var express = require('express');
    var b = include('./b.js');
    var app = express(); 
    app.get('/', function (req, res) {
     res.send(b.num);
     });
    app.listen(3000);
    

    但根据这几个目的封装include的时候,大家开掘了难点。无论大家在include.js内部中哪些实现,都无法像先导那样得到新的 b.num。

    相比较之下起来的代码,大家开掘难点出在少了 b = xx。也即是说这样写才得以:

    web.js

    var include = require('./include');
    var express = require('express');
    var app = express(); 
    app.get('/', function (req, res) {
     var b = include('./b.js');
     res.send(b.num);
     });
    app.listen(3000);
    

    修改成那样,就足以确定保证每一遍能得以准确的刷新到最新的代码,何况不要重启实例了。读者有意思味的能够切磋那一个include是怎么落到实处的,本文就不深切商量了,因为那些技巧使开支不高,写起起来不是很优雅[1],反而这里面有一个更重要的标题——JavaScript的引用。

    JavaScript 的援用与历史观引用的界别

    要研究这几个主题素材,大家第一要询问 JavaScript 的援用于任何语言中的叁个区分,在 C 中引用能够从来改换外界的值:

    #include 
    using namespace std;
    void test(int &p) // 引用传递 {
     p = 2048;
     }
    int main() {
     int a = 1024;
     int &p = a; // 设置引用p指向a
     test(p); // 调用函数
     cout << "p: " << p << endl; // 2048
     cout << "a: " << a << endl; // 2048
     return 0;
     }
    

    而在 JavaScript 中:

    var obj = { name: 'Alan' };
    function test1(obj) {
     obj = { hello: 'world' }; // 试图修改外部obj
     }
    test1(obj);
     console.log(obj); // { name: 'Alan' } // 并没有修改①
    function test2(obj) {
     obj.name = 'world'; // 根据该对象修改其上的属性
     }
    test2(obj);
     console.log(obj); // { name: 'world' } // 修改成功②
    

    大家开掘与 C 分化,根据下边代码 ① 可见 JavaScript 中并未传递一个援用,而是拷贝了二个新的变量,即值传递。依照 ② 可见拷贝的那个变量是三个能够访谈到指标属性的“援引”(与古板的 C 的引用分化,下文中涉及的 JavaScript 的引用都是这种特地的援引)。这里需求总计贰个绕口的结论:Javascript 中均是值传递,对象在传递的历程中是拷贝了一份新的引用。

    为了知道这几个相比生硬的下结论,让大家来看一段代码:

    var obj = { name: 'Alan' };
    function test1(obj) {
     obj = { hello: 'world' }; // 试图修改外部obj
     }
    test1(obj);
     console.log(obj); // { name: 'Alan' } // 并没有修改①
    function test2(obj) {
     obj.name = 'world'; // 根据该对象修改其上的属性
     }
    test2(obj);
     console.log(obj); // { name: 'world' } // 修改成功②
    

    由此那些例子大家能够见见,data 固然像三个引用同样指向了 obj.data,并且经过 data 能够访谈到 obj.data 上的脾气。可是出于 JavaScript 值传递的风味直接修改 data = xxx 并不会使得 obj.data = xxx。

    打个比如最初安装 var data = obj.data 的时候,内部存款和储蓄器中的情形大意是:

    |   Addr   |  内容  | |----------|-------- | obj.data |  内存1 |
    | data | 内存1 |

    为此通过 data.xx 能够修改 obj.data 的内部存款和储蓄器1。

    下一场设置 data = xxx,由于 data 是拷贝的一个新的值,只是那一个值是三个援用(指向内部存储器1)罢了。让它相当于别的多个指标就好比:

    |   Addr   |  内容  | |----------|-------- | obj.data |  内存1 |
    | data | 内存2 |

    让 data 指向了新的一块内存2。

    比如是古板的援用(如上文中涉及的 C 的引用),那么 obj.data 本人会化为新的内部存款和储蓄器2,但 JavaScript 中均是值传递,对象在传递的进度中拷贝了一份新的援用。所以那一个新拷贝的变量被改动并不影响原来的对象。

    Node.js 中的 module.exports 与 exports

    上述例子中的 obj.data 与 data 的关系,正是 Node.js 中的 module.exports 与 exports 之间的关系。让我们来看看 Node.js 中 require 三个文书时的实在组织:

    function require(...) {
     var module = { exports: {} };
     ((module, exports) => { // Node.js 中文件外部其实被包了一层自执行的函数
     // 这中间是你模块内部的代码.
     function some_func() {};
     exports = some_func;
     // 这样赋值,exports便不再指向module.exports
     // 而module.exports依旧是{} 
     module.exports = some_func;
     // 这样设置才能修改到原本的exports
     })(module, module.exports);
     return module.exports;
     }
    

    之所以很当然的:

    console.log(module.exports === exports); // true
    // 所以 exports 所操作的就是 module.exports
    

    Node.js 中的 exports 正是拷贝的一份 module.exports 的引用。通过 exports 能够修改Node.js 当前文件导出的习性,可是不能够改改当前模块本人。通过 module.exports 才足以修改到其本身。表现上来讲:

    exports = 1; // 无效
    module.exports = 1; // 有效
    

    那是两个展现上的分别,其余地点用起来都没迥然不一样。所以您未来应当清楚写module.exports.xx = xxx; 的人其实是多写了三个module.。

    更头晕目眩的例证

    为了再练习一下,大家在来看一个比较复杂的事例:

    var a = {n: 1}; 
    var b = a; 
    a.x = a = {n: 2}; 
    console.log(a.x);
    console.log(b.x);
    

    依据开头的定论大家得以一步步的来看这几个主题材料:

    var a = {n: 1};  // 引用a指向内存1{n:1}
    var b = a; // 引用b => a => { n:1 }
    

    内部结构:

    |   Addr  |     内容     | |---------|-------------|
    | a | 内存1 {n:1} | | b | 内存1 |

    后续往下看:

    a.x = a = {n: 2}; // (内存1 而不是 a ).x = 引用 a = 内存2 {n:2}
    

    a 纵然是援引,不过 JavaScript 是值传的这些援用,所以被退换不影响原来的地方。

    | Addr | 内容 | |-----------|-----------------------|
    | 1) a | 内存2({n:2}) | | 2) 内存1.x | 内存2({n:2}) |
    | 3) b | 内存1({n:1, x:内存2}) |

    进而末了的结果

    a.x 即(内存2).x ==> {n: 2}.x ==> undefined
    b.x 即(内存1).x ==> 内存2 ==> {n: 2}

    总结

    JavaScrip t中从未引用传递,唯有值传递。对象(援用类型)的传递只是拷贝一个新的援引,那么些新的援引能够访谈原来对象上的习性,然则那几个新的引用笔者是投身别的三个格子上的值,直接往那些格子赋新的值,并不会潜濡默化原来的对象。本文开首所研商的 Node.js 热更新时碰着的也是那几个主题材料,分裂是目标自己改动了,而原来拷贝出来的援引还指向旧的内部存款和储蓄器,所以通过旧的援用调用不到新的办法。

    Node.js 并不曾对 JavaScript 施加黑法力,当中的引用难题还是是 JavaScript 的故事情节。如 module.exports 与 exports 那样暗藏了部分细节轻便使人误解,本质照旧 JavaScript 的主题材料。

    注[1]:

    老实巴交说,模块在函数内证明有一些谭浩强的感到到。

    把 b = include(xxx) 写在调用内部,仍可以透过设置成人中学间件绑定在国有地点来写。

    除了写在调用内部,也能够导出二个工厂函数,每一遍使用时 b().num 一下调用也能够。

    还足以由其中间件的花样绑定在框架的公用对象上(如:ctx.b = include(xxx))。

    要贯彻如此的热更新必须在架设上将在执法必严防止旧代码被引述的大概性,不然很轻便写出内部存款和储蓄器泄漏的代码。

    如上所述是小编给我们介绍的Node.js中看JavaScript的引用,希望对大家具备帮助,假若大家有任何疑问请给自身留言,小编会及时复苏我们的。在此也特别多谢大家对帮客之家网址的协理!

    开始时期学习 Node.js 的时候 (二零一三-二零一三),有挺多是从 PHP 转过来的,当时有一点点人对此 Node.js 编辑完代码需...

    最初学习 Node.js 的时候 (二零一一-2011),有挺多是从 PHP 转过来的,当时有一对人对于 Node.js 编辑完代码需求重启一下意味麻烦(PHP没有须要以此进度),于是社区里的仇敌就起始发起使用 node-supervisor 那么些模块来运转项目,能够编写制定完代码之后自动重启。但是相对于 PHP 来说如故远远不够便利,因为 Node.js 在重启未来,在此之前的上下文都扬弃了。

    【转】

    JavaScript 的引用与价值观引用的分化

    要商讨那一个难点,大家首先要打听 JavaScript 的援用于别的语言中的二个分别,在 C 中引用能够一贯改换外部的值:

    #include using namespace std; void test(int &p) // 援用传递 { p = 2048; } int main() { int a = 1024; int &p = a; // 设置引用p指向a test(p); // 调用函数 cout

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #include
     
    using namespace std;
     
    void test(int &p) // 引用传递
    {
        p = 2048;
    }
     
    int main()
    {
        int a = 1024;
        int &p = a; // 设置引用p指向a
     
        test(p); // 调用函数
     
        cout

    而在 JavaScript 中:

    var obj = { name: 'Alan' }; function test1(obj) { obj = { hello: 'world' }; // 试图修改外界obj } test1(obj); console.log(obj); // { name: '艾伦' } // 并不曾退换① function test2(obj) { obj.name = 'world'; // 根据该目的修改其上的属性 } test2(obj); console.log(obj); // { name: 'world' } // 修改成功②

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    var obj = { name: 'Alan' };
     
    function test1(obj) {
      obj = { hello: 'world' }; // 试图修改外部obj
    }
     
    test1(obj);
    console.log(obj); // { name: 'Alan' } // 并没有修改①
     
    function test2(obj) {
      obj.name = 'world'; // 根据该对象修改其上的属性
    }
     
    test2(obj);
    console.log(obj); // { name: 'world' } // 修改成功②

    我们发掘与 C 不一致,遵照上边代码 ① 可见 JavaScript 中并未传递二个引用,而是拷贝了二个新的变量,即值传递。根据 ② 可见拷贝的那些变量是二个能够访问到目的属性的“引用”(与守旧的 C 的引用差别,下文中涉及的 JavaScript 的援用都以这种特意的援引)。这里要求计算三个绕口的结论:Javascript 中均是值传递,对象在传递的长河中是拷贝了一份新的引用。

    为了明白那个相比生硬的结论,让大家来看一段代码:

    var obj = { data: {} }; // data 指向 obj.data var data = obj.data; console.log(data === obj.data); // true-->data所操作的正是obj.data data.name = 'Alan'; data.test = function () { console.log('hi') }; // 通过data能够直接修改到data的值 console.log(obj) // { data: { name: 'Alan', test: [Function] } } data = { name: '鲍勃', add: function (a, b) { return a b; } }; // data是一个援引,直接赋值给它,只是让这么些变量等于别的一个援用,并不会修改到obj本身console.log(data); // { name: 'Bob', add: [Function] } console.log(obj); // { data: { name: 'Alan', test: [Function] } } obj.data = { name: '鲍伯', add: function (a, b) { return a b; } }; // 而通过obj.data技巧真的修改到data自个儿 console.log(obj); // { data: { name: '鲍勃', add: [Function] } }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    var obj = {
      data: {}
    };
     
    // data 指向 obj.data
    var data = obj.data;
     
    console.log(data === obj.data); // true-->data所操作的就是obj.data
     
    data.name = 'Alan';
    data.test = function () {
      console.log('hi')
    };
     
    // 通过data可以直接修改到data的值
    console.log(obj) // { data: { name: 'Alan', test: [Function] } }
     
    data = {
      name: 'Bob',
      add: function (a, b) {
        return a b;
      }
    };
     
    // data是一个引用,直接赋值给它,只是让这个变量等于另外一个引用,并不会修改到obj本身
    console.log(data); // { name: 'Bob', add: [Function] }
    console.log(obj); // { data: { name: 'Alan', test: [Function] } }
     
    obj.data = {
      name: 'Bob',
      add: function (a, b) {
        return a b;
      }
    };
     
    // 而通过obj.data才能真正修改到data本身
    console.log(obj); // { data: { name: 'Bob', add: [Function] } }

    透过那些例子大家得以看出,data 即使像二个引用同样指向了 obj.data,况且通过 data 可以访谈到 obj.data 上的性情。可是出于 JavaScript 值传递的个性直接修改 data = xxx 并不会使得 obj.data = xxx。

    打个就算最初安装 var data = obj.data 的时候,内部存款和储蓄器中的情形大约是:

    | Addr | 内容 | |----------|-------- | obj.data | 内存1 | | data | 内存1 |

    1
    2
    3
    4
    |   Addr   |  内容  |
    |----------|--------
    | obj.data |  内存1 |
    |   data   |  内存1 |

    因而通过 data.xx 能够修改 obj.data 的内部存款和储蓄器1。

    接下来设置 data = xxx,由于 data 是拷贝的二个新的值,只是那几个值是一个援引(指向内部存款和储蓄器1)罢了。让它特别别的一个对象就好比:

    | Addr | 内容 | |----------|-------- | obj.data | 内存1 | | data | 内存2 |

    1
    2
    3
    4
    |   Addr   |  内容  |
    |----------|--------
    | obj.data |  内存1 |
    |   data   |  内存2 |

    让 data 指向了新的一块内部存款和储蓄器2。

    倘假设古板的援引(如上文中提到的 C 的援用),那么 obj.data 本人会化为新的内部存款和储蓄器2,但 JavaScript 中均是值传递,对象在传递的经过中拷贝了一份新的引用。所以这一个新拷贝的变量被改换并不影响原来的目的。

    纵然能够经过将session数据保存在数据库恐怕缓存中来压缩重启进程中的数据遗失,不过纵然是在生产的气象下,更新代码的重启间隙是可望而不可及管理乞求的(PHP能够,别的足够时候还未曾cluster)。由于那上头的主题材料,加上自己是从PHP转到Node.js的,于是从当时初阶企图有未有措施能够在不重启的境况下热更新Node.js的代码。

    虽说能够透过将 session 数据保存在数据库恐怕缓存中来压缩重启进程中的数据遗失,可是假设是在生养的情形下,更新代码的重启间隙是不得已处理诉求的(PHP能够,别的二〇一六年Node.js 还并未 cluster)。由于那上面的标题,加上自己是从 PHP 转到 Node.js 的,于是从那时初叶思虑,有未有法子能够在不重启的情形下热更新 Node.js 的代码。

    奉公守法的模块化典型不一样样

    Node.js 中的 module.exports 与 exports

    上述例子中的 obj.data 与 data 的关联,正是 Node.js 中的 module.exports 与 exports 之间的涉嫌。让大家来探问 Node.js 中 require 一个文本时的实际上协会:

    function require(...) { var module = { exports: {} }; ((module, exports) => { // Node.js 中文件外界其实被包了一层自推行的函数 // 那中间是你模块内部的代码. function some_func() {}; exports = some_func; // 那样赋值,exports便不再指向module.exports // 而module.exports依旧是{} module.exports = some_func; // 那样设置才具修改到原本的exports })(module, module.exports); return module.exports; }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    function require(...) {
      var module = { exports: {} };
      ((module, exports) => { // Node.js 中文件外部其实被包了一层自执行的函数
        // 这中间是你模块内部的代码.
        function some_func() {};
        exports = some_func;
        // 这样赋值,exports便不再指向module.exports
        // 而module.exports依旧是{}
     
        module.exports = some_func;
        // 这样设置才能修改到原本的exports
      })(module, module.exports);
      return module.exports;
    }

    故而很当然的:

    console.log(module.exports === exports); // true // 所以 exports 所操作的正是 module.exports

    1
    2
    console.log(module.exports === exports); // true
    // 所以 exports 所操作的就是 module.exports

    Node.js 中的 exports 正是拷贝的一份 module.exports 的援用。通过 exports 能够修改Node.js 当前文件导出的质量,但是不可能改改当前模块本人。通过 module.exports 才具够修改到其本身。展现上来讲:

    exports = 1; // 无效 module.exports = 1; // 有效

    1
    2
    exports = 1; // 无效
    module.exports = 1; // 有效

    那是两个呈现上的分别,其余地点用起来都并没不一模一样。所以你未来应当清楚写module.exports.xx = xxx; 的人实在是多写了二个module.。

    最发轫把眼光瞄向了require那一个模块。主张很轻易,因为Node.js中引进三个模块都以透过require那些格局来加载的。于是就起首盘算require能还是不能在创新代码之后再次require一下。尝试如下:

    最开端把目光瞄向了 require 这些模块。主张非常粗略,因为 Node.js 中引进一个模块都以经过 require 那些法子加载的。于是就从头探讨 require 能或不能够在创新代码之后再次 require 一下。尝试如下:

    模块化标准:即为 JavaScript 提供一种模块编写、模块依赖和模块运营的方案。哪个人让最初的 JavaScript 是那么的裸奔呢——全局变量正是它的模块化标准。

    更目不暇接的事例

    为了再练习一下,大家在来看一个比较复杂的例证:

    var a = {n: 1}; var b = a; a.x = a = {n: 2}; console.log(a.x); console.log(b.x);

    1
    2
    3
    4
    5
    var a = {n: 1};  
    var b = a;
    a.x = a = {n: 2};  
    console.log(a.x);
    console.log(b.x);

    依照开首的结论大家能够一步步的来看这么些标题:

    var a = {n: 1}; // 援用a指向内部存款和储蓄器1{n:1} var b = a; // 援引b => a => { n:1 }

    1
    2
    var a = {n: 1};   // 引用a指向内存1{n:1}
    var b = a;        // 引用b => a => { n:1 }

    内部结构:

    | Addr | 内容 | |---------|-------------| | a | 内存1 {n:1} | | b | 内存1 |

    1
    2
    3
    4
    |   Addr  |     内容     |
    |---------|-------------|
    |    a    |  内存1 {n:1} |
    |    b    |  内存1       |

    三番五次往下看:

    a.x = a = {n: 2}; // (内存1 而不是 a ).x = 引用 a = 内存2 {n:2}

    1
    a.x = a = {n: 2};  //  (内存1 而不是 a ).x = 引用 a = 内存2 {n:2}

    a 就算是引用,可是 JavaScript 是值传的这么些引用,所以被涂改不影响原来的地点。

    | Addr | 内容 | |-----------|-----------------------| | 1) a | 内存2({n:2}) | | 2) 内存1.x | 内存2({n:2}) | | 3) b | 内存1({n:1, x:内存2}) |

    1
    2
    3
    4
    5
    |    Addr   |          内容         |
    |-----------|-----------------------|
    | 1) a     |  内存2({n:2})         |
    | 2) 内存1.x |  内存2({n:2})         |
    | 3) b     |  内存1({n:1, x:内存2}) |

    由此最后的结果

    • a.x 即(内存2).x ==> {n: 2}.x ==> undefined
    • b.x 即(内存1).x ==> 内存2 ==> {n: 2}

    A.js代码

    a.js

    require/exports 出生在野生规范个中,什么叫做野生规范?即那一个标准是 JavaScript 社区中的开辟者本身起草的条条框框,获得了大家的认可只怕大面积的施用。比如CommonJS、英特尔、CMD 等等。import/export 则是豪门正派。TC39 制订的新的 ECMAScript 版本,即 ES6(ES二〇一四)中满含进来。

    总结

    JavaScrip t中从不援引传递,只有值传递。对象(援用类型)的传递只是拷贝叁个新的援用,那些新的援引能够访问原来对象上的质量,不过这几个新的援引作者是坐落别的二个格子上的值,直接往这么些格子赋新的值,并不会耳濡目染原本的对象。本文开端所研讨的 Node.js 热更新时遇到的也是那么些主题材料,差别是目的自己改换了,而原来拷贝出来的引用还指向旧的内部存储器,所以通过旧的援引调用不到新的措施。

    Node.js 并不曾对 JavaScript 施加黑法力,个中的援用难题依然是 JavaScript 的剧情。如 module.exports 与 exports 那样暗藏了一些细节轻易使人误会,本质依旧 JavaScript 的难点。别的推荐一个有关 Node.js 的进级教程 《Node.js 面试》。

    注[1]:

    1. 老实说,模块在函数内阐明有一些谭浩强的以为。
    2. 把 b = include(xxx) 写在调用内部,还足以经过设置成人中学间件绑定在公私地点来写。
    3. 除却写在调用内部,也足以导出三个厂子函数,每回使用时 b().num 一下调用也足以。
    4. 还足以通过中间件的款式绑定在框架的公用对象上(如:ctx.b = include(xxx))。
    5. 要落到实处如此的热更新必须在架设上就要执法必严防止旧代码被引用的恐怕性,不然很轻巧写出内部存款和储蓄器泄漏的代码。

      1 赞 收藏 评论

    新葡亰496net 1

    var express = require('express');

    var express = require('express');
    var b = require('./b.js'); 
    var app = express();
    app.get('/', function (req, res) {
     b = require('./b.js');
     res.send(b.num);
     });
    app.listen(3000);
    

    出现的岁月区别

    var b = require('./b.js');

    b.js

    require/exports 相关的规范由于野生性质,在 二〇〇九 年内外出生。英特尔、CMD 相对命十分的短,到 二〇一五年基本上就摇头欲坠了。一开端大家还比较喜欢在浏览器上应用这种异步小模块的加载格局,但并不是银弹。随着 Node.js 流行和 Browsersify 的勃兴,运转时异步加载慢慢被营造时模块合併分块所替代。Wrapper 函数再也没有供给了。 二零一六 年 Webpack 照旧新东西,以往已经是前边三个必备神器了。

    var app = express();

    exports.num = 1024;
    

    Browsersify、Webpack 一初阶的目标就是包裹 CommonJS 模块。

    app.get('/', function (req, res) {

    五个 JS 文件写好之后,从 a.js 运营,刷新页面会输出 b.js 中的 1024,然后修改 b.js 文件中程导弹出的值,比如修改为 2048。再一次刷新页面仍旧是原本的 1024。

    CommonJS 作为 Node.js 的标准,从来沿用到现在。由于 npm 上 CommonJS 的类库众多,以及 CommonJS 和 ES6 之间的歧异,Node.js(这里不太标准) 不能够直接包容 ES6。所以现阶段 require/exports 还是是少不了且必须的。出自 ES6 的  import/export 相对就晚了比非常多。被大家所熟练和选拔也是 2016年之后的事了。 那实质上要多谢 babel(原本项目名称叫做 6to5,后更名称为 babel) 这一个神一般的品种。由于有了 babel 将还未被宿主遭遇(各浏览器、Node.js)直接支持的 ES6 Module 编写翻译为 ES5 的 CommonJS —— 也正是 require/exports 这种写法 —— Webpack 插上 babel-loader 那么些羽翼才起来高飞,我们也工夫够称 " 小编在行使 ES6! "

    b = require('./b.js');

    重新实践二次 require 并未刷新代码。require 在实行的过程中加载完代码之后会把模块导出的数额放在 require.cache 中。require.cache 是贰个 { } 对象,以模块的绝对路线为 key,该模块的详尽数据为 value。于是便开始做如下尝试:

    那也正是干什么前边说 require/exports 是须要且必须的。因为实际是,方今你编写的 import/export 最后都以编写翻译为 require/exports 来实行的。

    res.send(b.num);

    a.js

    require/exports 和 import/export 方式不等同

    });

    var path = require('path');
    var express = require('express');
    var b = require('./b.js'); 
    var app = express();
    app.get('/', function (req, res) {
     if (true) { // 检查文件是否修改
     flush();
     }
     res.send(b.num);
     });
    function flush() {
     delete require.cache[path.join(__dirname, './b.js')];
     b = require('./b.js');
     }
    app.listen(3000);
    

    require/exports 的用法独有以下三种简易的写法:

    app.listen(3000);

    再一次 require 在此以前,将 require 之上关于该模块的 cache 清理掉后,用事先的方法重新测量试验。结果发掘,能够成功的基础代谢 b.js 的代码,输出新修改的值。

    const fs = require('fs')exports.fs = fsmodule.exports = fs

    B.js代码

    驾驭到这几个点后,就想经过该原理完成三个无重启热更新版本的 node-supervisor。在包装模块的历程中,出于情怀的由来,思虑提供三个近乎 PHP 中 include 的函数来代表 require 去引进三个模块。实际内部依旧是运用 require 去加载。以b.js为例,原本的写法改为 var b = include(‘./b'),在文书 b.js 更新之后 include 内部能够活动刷新,让外部获得新型的代码。

    而 import/export 的写法就错综复杂:

    exports.num = 1024;

    而是其实的花费进度中,这样快速就超越了难题。我们希望的代码也许是那般:

    import fs from 'fs'import {default as fs} from 'fs'import * as fs from 'fs'import {readFile} from 'fs'import {readFile as read} from 'fs'import fs, {readFile} from 'fs'export default fsexport const fsexport function readFileexport {readFile, read}export * from 'fs'

    多个JS文件写好今后,从a.js运行,刷新页面会输出b.js中的1024,然后修改b.js文件中程导弹出的值,譬如修改为2048。再一次刷新页面依然是原本的1024。

    web.js

    require/exports 和 import/export 本质上的异样

    再也实践一遍require并从未刷新代码。require在施行的经过中加载完代码之后会把模块导出的多寡放在require.cache中。require.cache是二个{}对象,以模块的相对路线为key,该模块的详细数据为value。于是便起首做如下尝试:

    var include = require('./include');
    var express = require('express');
    var b = include('./b.js');
    var app = express(); 
    app.get('/', function (req, res) {
     res.send(b.num);
     });
    app.listen(3000);
    

    花样上看起来多姿多彩,但精神上:

    A.js代码

    但依照那一个目的封装include的时候,大家发掘了难题。无论我们在include.js内部中如何实现,都不能够像开端那样获得新的 b.num。

    CommonJS 仍然 ES6 Module 输出都足以作为是一个有着五个属性只怕措施的指标;

    var path = require('path');

    对待起来的代码,我们发现难题出在少了 b = xx。也便是说那样写才方可:

    default 是 ES6 Module 所只有的第一字,export default fs 输出暗许的接口对象,import fs from 'fs' 可径直导入那几个指标;

    var express = require('express');

    web.js

    ES6 Module 中程导弹入模块的习性可能措施是强绑定的,富含基础项目;而 CommonJS 则是平凡的值传递大概援引传递。

    var b = require('./b.js');

    var include = require('./include');
    var express = require('express');
    var app = express(); 
    app.get('/', function (req, res) {
     var b = include('./b.js');
     res.send(b.num);
     });
    app.listen(3000);
    

    1、2 绝对相比好明白,3 要求看个例子:

    var app = express();

    修改成那样,就可以保证每一次能得以精确的基础代谢到新型的代码,並且不要重启实例了。读者风趣味的可以研商这些include是怎么落到实处的,本文就不深切商讨了,因为那个手艺使开销不高,写起起来不是很优雅[1],反而那几个中有贰个更关键的主题素材——JavaScript的援用。

    // counter.jsexports.count=0setTimeout(function(){console.log('increase count to', exports.count,'in counter.js after 500ms')},500)// commonjs.jsconst{count}=require('./counter')setTimeout(function(){console.log('read count after 1000ms in commonjs is',count)},1000)//es6.jsimport{count}from'./counter'setTimeout(function(){console.log('read count after 1000ms in es6 is',count)},1000)

    app.get('/', function (req, res) {

    JavaScript 的引用与观念引用的分歧

    分别运营 commonjs.js 和 es6.js:

    if (true) { // 检查文件是不是修改

    要探讨那么些难题,大家首先要打听 JavaScript 的援引于另外语言中的二个分别,在 C 中引用能够一贯修改外界的值:

    ➜testnode commonjs.jsincrease count to1in counter.js after 500msreadcount after 1000ms in commonjs is 0➜testbabel-node es6.jsincrease count to1in counter.js after 500msreadcount after 1000ms in es6 is 1

    flush();

    #include 
    using namespace std;
    void test(int &p) // 引用传递 {
     p = 2048;
     }
    int main() {
     int a = 1024;
     int &p = a; // 设置引用p指向a
     test(p); // 调用函数
     cout << "p: " << p << endl; // 2048
     cout << "a: " << a << endl; // 2048
     return 0;
     }
    

    作者:寸志

    }

    而在 JavaScript 中:

    链接:

    res.send(b.num);

    var obj = { name: 'Alan' };
    function test1(obj) {
     obj = { hello: 'world' }; // 试图修改外部obj
     }
    test1(obj);
     console.log(obj); // { name: 'Alan' } // 并没有修改①
    function test2(obj) {
     obj.name = 'world'; // 根据该对象修改其上的属性
     }
    test2(obj);
     console.log(obj); // { name: 'world' } // 修改成功②
    

    来源:知乎

    });

    大家开掘与 C 差异,依照上边代码 ① 可见 JavaScript 中并不曾传递贰个引用,而是拷贝了贰个新的变量,即值传递。根据 ② 可见拷贝的那么些变量是一个方可访谈到对象属性的“援引”(与价值观的 C 的引用分歧,下文中涉嫌的 JavaScript 的援用都以这种特别的引用)。这里供给计算多少个绕口的定论:Javascript 中均是值传递,对象在传递的长河中是拷贝了一份新的引用。

    【转】commonjs模块与es6模块的分别

    function flush() {

    为了理解那些相比较刚烈的定论,让我们来看一段代码:

    到方今结束,已经实习了3个月的时间了。前段时间在面试,在面试题里面有有失水准态涉及到模块循环加载的知识。趁着那些机缘,将commonjs模块与es6模块之间有些十分重要的的界别做个总括。语法上有何界别就不具体说了,主要研究引用的界别。

    delete require.cache[path.join(__dirname, './b.js')];

    var obj = { name: 'Alan' };
    function test1(obj) {
     obj = { hello: 'world' }; // 试图修改外部obj
     }
    test1(obj);
     console.log(obj); // { name: 'Alan' } // 并没有修改①
    function test2(obj) {
     obj.name = 'world'; // 根据该对象修改其上的属性
     }
    test2(obj);
     console.log(obj); // { name: 'world' } // 修改成功②
    

    转载请注脚出处:commonjs模块与es6模块的差距

    b = require('./b.js');

    由此那个事例大家能够看来,data 即便像三个援用同样指向了 obj.data,並且经过 data 能够访问到 obj.data 上的属性。但是由于 JavaScript 值传递的风味间接退换 data = xxx 并不会使得 obj.data = xxx。

    commonjs

    }

    打个如若最初安装 var data = obj.data 的时候,内部存款和储蓄器中的境况大致是:

    对此基本数据类型,属于复制。即会被模块缓存。同有的时候间,在另三个模块能够对该模块输出的变量重新赋值。

    新葡亰496net:在 Node.js 中看 JavaScript 的引用。app.listen(3000);

    |   Addr   |  内容  | |----------|-------- | obj.data |  内存1 |
    | data | 内存1 |

    对此复杂数据类型,属于浅拷贝。由于三个模块援引的靶子指向同三个内部存款和储蓄器空间,由此对该模块的值做修改时会影响另二个模块。

    新葡亰496net:在 Node.js 中看 JavaScript 的引用。在再次require之前将require之上关于该模块的cache清理掉之后,用事先的格局重复测量试验。结果开掘,能够成功的刷新b.js的代码,输出新修改的值。

    故而经过 data.xx 能够修改 obj.data 的内部存储器1。

    当使用require命令加载有个别模块时,就能够运转总人体模型块的代码。

    询问那些点,原来认为经过这些规律就足以写二个跟node-supervisor类似的模块,将起重启的一对换到通过该原理刷新就能够写二个越来越好的。可是在事实上的付出进度中及时就遭逢了问题。在包装模块的经过中,出于情怀的原因思量提供三个好像PHP中include的函数来代表require去引进三个模块。实际内部仍旧是应用require去加载。以b.js为例,原来的写法就创作var b = include(‘./b’),在文书b.js更新之后include内部能够活动刷新,让外部获得新型的代码。

    然后设置 data = xxx,由于 data 是拷贝的贰个新的值,只是那个值是贰个引用(指向内部存款和储蓄器1)罢了。让它等于另外三个对象就好比:

    当使用require命令加载同三个模块时,不会再举行该模块,而是取到缓存之中的值。也正是说,commonjs模块无论加载多少次,都只会在第叁回加载时运行二回,未来再加载,就赶回第一次运维的结果,除非手动清除系统缓存。

    可是实际的费用进程中,那样神速就遇到了难题。大家希望的代码恐怕是这么:

    |   Addr   |  内容  | |----------|-------- | obj.data |  内存1 |
    | data | 内存2 |

    循环加载时,属于加载时进行。即脚本代码在require的时候,就能全体施行。一旦出现某些模块被"循环加载",就只输出已经实践的一部分,还未施行的片段不会输出。

    Web.js代码

    让 data 指向了新的一块内部存款和储蓄器2。

    ES6模块

    var include = require('./include');

    若果是传统的援用(如上文中提到的 C 的援引),那么 obj.data 本人会化为新的内存2,但 JavaScript 中均是值传递,对象在传递的长河中拷贝了一份新的援引。所以这几个新拷贝的变量被变更并不影响原来的靶子。

    es6模块中的值属于【动态只读引用】。

    var express = require('express');

    Node.js 中的 module.exports 与 exports

    对于只读来讲,即不容许修改引进变量的值,import的变量是只读的,不论是基本数据类型照旧复杂数据类型。当模块碰到import命令时,就能调换二个只读援引。等到脚本真的实施时,再遵照这几个只读援引,到被加载的可怜模块里面去取值。

    var b = include('./b.js');

    上述例子中的 obj.data 与 data 的涉及,正是 Node.js 中的 module.exports 与 exports 之间的关系。让我们来看看 Node.js 中 require 叁个文件时的其实组织:

    对于动态来讲,原始值产生变化,import加载的值也会发生变化。不论是基本数据类型照旧复杂数据类型。

    var app = express();

    function require(...) {
     var module = { exports: {} };
     ((module, exports) => { // Node.js 中文件外部其实被包了一层自执行的函数
     // 这中间是你模块内部的代码.
     function some_func() {};
     exports = some_func;
     // 这样赋值,exports便不再指向module.exports
     // 而module.exports依旧是{} 
     module.exports = some_func;
     // 这样设置才能修改到原本的exports
     })(module, module.exports);
     return module.exports;
     }
    

    循环加载时,ES6模块是动态援用。只要多少个模块之间存在有些援用,代码就可见执行。

    app.get('/', function (req, res) {

    据此很自然的:

    地点说了有的重视差距。现在举一些事例来申明每一点吗

    res.send(b.num);

    console.log(module.exports === exports); // true
    // 所以 exports 所操作的就是 module.exports
    

    commonjs

    });

    Node.js 中的 exports 正是拷贝的一份 module.exports 的引用。通过 exports 能够修改Node.js 当前文件导出的属性,可是不能够改改当前模块本身。通过 module.exports 才得以修改到其本身。表现上来讲:

    对此着力数据类型,属于复制。即会被模块缓存。同不常间,在另三个模块能够对该模块输出的变量重新赋值。

    app.listen(3000);

    exports = 1; // 无效
    module.exports = 1; // 有效
    

    // b.js

    而是在鲁人持竿那些指标封装include的时候,大家开采了难题。无论我们在include.js内部中什么贯彻,都不能像开头那样让得到新的b.num。

    那是两个显示上的区分,其余方面用起来都尚未距离。所以你今后应有了解写module.exports.xx = xxx; 的人其实是多写了一个module.。

    let count = 1

    对照起来的代码,大家开采难题出在少了b = xx。也等于说那样写才得以:

    更复杂的例证

    let plusCount = () => {

    Web.js代码

    为了再演习一下,我们在来看三个比较复杂的事例:

    count

    var include = require('./include');

    var a = {n: 1}; 
    var b = a; 
    a.x = a = {n: 2}; 
    console.log(a.x);
    console.log(b.x);
    

    }

    var express = require('express');

    依照开始的下结论大家得以一步步的来看那一个主题材料:

    setTimeout(() => {

    var app = express();

    var a = {n: 1};  // 引用a指向内存1{n:1}
    var b = a; // 引用b => a => { n:1 }
    

    console.log('b.js-1', count)

    app.get('/', function (req, res) {

    内部结构:

    }, 1000)

    var b = include('./b.js');

    |   Addr  |     内容     | |---------|-------------|
    | a | 内存1 {n:1} | | b | 内存1 |

    module.exports = {

    res.send(b.num);

    持续往下看:

    count,

    });

    a.x = a = {n: 2}; // (内存1 而不是 a ).x = 引用 a = 内存2 {n:2}
    

    plusCount

    app.listen(3000);

    a 纵然是援用,可是 JavaScript 是值传的这么些援用,所以被更换不影响原来的地方。

    }

    修改成那样就足以确认保障每一趟能得以正确的基础代谢到新型的代码,况兼不要重启实例了。读者有野趣的能够钻探那几个include怎么落到实处,本文就不深入座谈了,因为这些本领使花费不高,写起起来不是很优雅①,反而那之间有八个更主要的主题素材————JavaScript的引用。

    | Addr | 内容 | |-----------|-----------------------|
    | 1) a | 内存2({n:2}) | | 2) 内存1.x | 内存2({n:2}) |
    | 3) b | 内存1({n:1, x:内存2}) |

    // a.js

    JavaScript的援引与历史观援引的界别

    因而最终的结果

    let mod = require('./b.js')

    要钻探这几个难点,大家率先要通晓JavaScript的引用于别的语言中的一个界别,在C 中引用是直接能够修改外界的值:

    a.x 即(内存2).x ==> {n: 2}.x ==> undefined
    b.x 即(内存1).x ==> 内存2 ==> {n: 2}

    console.log('a.js-1', mod.count)

    C 代码

    总结

    mod.plusCount()

    #include 

    JavaScrip t中没有引用传递,独有值传递。对象(引用类型)的传递只是拷贝叁个新的援引,这一个新的援引能够访谈原来对象上的习性,然则那么些新的引用作者是投身别的二个格子上的值,间接往这一个格子赋新的值,并不会耳熏目染原来的对象。本文先河所冲突的 Node.js 热更新时遇见的也是那个主题材料,差距是指标自己改造了,而原本拷贝出来的引用还指向旧的内部存储器,所以经过旧的援引调用不到新的艺术。

    console.log('a.js-2', mod.count)

    using namespace std;

    Node.js 并未对 JavaScript 施加黑法力,在那之中的援引难点依旧是 JavaScript 的从头到尾的经过。如 module.exports 与 exports 那样暗藏了一部分细节轻便使人误解,本质依旧 JavaScript 的主题素材。

    setTimeout(() => {

    void test(int &p) // 引用传递

    注[1]:

    mod.count = 3

    {

    老实说,模块在函数内注解有一点点谭浩强的感到。

    console.log('a.js-3', mod.count)

    p = 2048;

    把 b = include(xxx) 写在调用内部,还足以经过设置成人中学间件绑定在公私地点来写。

    }, 2000)

    }

    除此之外写在调用内部,也足以导出二个厂子函数,每回使用时 b().num 一下调用也足以。

    node a.js

    int main()

    还可以够通过中间件的方式绑定在框架的公用对象上(如:ctx.b = include(xxx))。

    a.js-1 1

    {

    要落实那样的热更新必须在架设上将要严酷幸免旧代码被引述的或然,不然很轻松写出内存泄漏的代码。

    a.js-2 1

    int a = 1024;

    上述所述是笔者给大家介绍的Node.js中看JavaScript的援用,希望对我们享有帮忙,假设大家有其余疑问请给笔者留言,作者会及时还原大家的。在此也特别多谢大家对台本之家网址的支撑!

    b.js-1 2  // 1秒后

    int &p = a; // 设置援引p指向a

    你大概感兴趣的稿子:

    • Node.js查找当前目录下文件夹实例代码
    • 应用node.js搭建简易web服务器的艺术教程
    • 动用n 进级工具升级Node.js版本及在mac遭受下的坑

    a.js-3 3  // 2秒后

    test(p); // 调用函数

    如上代码能够看看,b模块export的count变量,是一个复制行为。在plusCount方法调用之后,a模块中的count不受影响。同不常候,能够在b模块中退换a模块中的值。若是希望能够一起代码,能够export出去四个getter。

    cout << "p: " << p << endl; // 2048

    // 别的代码一样

    cout << "a: " << a << endl; // 2048

    module.exports = {

    return 0;

    get count () {

    }

    return count

    而在JavaScript中:

    },

    Javascript代码

    plusCount

    var obj = { name: 'Alan' };

    }

    function test1(obj) {

    node a.js

    obj = { hello: 'world' }; // 试图修改外界obj

    a.js-1 1

    }

    a.js-2 1

    test1(obj);

    b.js-1 2  // 1秒后

    console.log(obj); // { name: 'Alan' } // 并从未改换②

    a.js-3 2  // 2秒后, 由于尚未定义setter,因而不能对值实行安装。所以如故回到2

    function test2(obj) {

    对此复杂数据类型,属于浅拷贝。由于四个模块引用的指标指向同二个内部存款和储蓄器空间,由此对该模块的值做修改时会影响另二个模块。

    obj.name = 'world'; // 根据该指标修改其上的习性

    // b.js

    }

    let obj = {

    test2(obj);

    count: 1

    console.log(obj); // { name: 'world' } // 修改成功③

    }

    咱俩发掘与C 不一样,依据②可见JavaScript中并不曾传递贰个援用,而是拷贝了三个新的变量,即值传递。依据③可知拷贝的那几个变量是一个足以访谈到对象属性的“援用”(与历史观的C 的引用分裂,下文中关系的JavaScript的援引都是这种非常的援引)。这里供给计算四个绕口的结论:Javascript中均是值传递,对象在传递的进度中是拷贝了一份新的援引。

    let plusCount = () => {

    为了精通这些相比生硬的定论,让我们来看一段代码:

    obj.count

    Javascript代码

    }

    var obj = {

    setTimeout(() => {

    data: {}

    console.log('b.js-1', obj.count)

    };

    }, 1000)

    // data 指向 obj.data

    setTimeout(() => {

    var data = obj.data;

    console.log('b.js-2', obj.count)

    console.log(data === obj.data); // true-->data所操作的正是obj.data

    }, 3000)

    data.name = 'Alan';

    module.exports = {

    data.test = function () {

    obj,

    console.log('hi')

    plusCount

    };

    }

    // 通过data能够直接修改到data的值

    // a.js

    console.log(obj) // { data: { name: 'Alan', test: [Function] } }

    var mod = require('./b.js')

    data = {

    console.log('a.js-1', mod.obj.count)

    name: 'Bob',

    mod.plusCount()

    add: function (a, b) {

    console.log('a.js-2', mod.obj.count)

    return a   b;

    setTimeout(() => {

    }

    mod.obj.count = 3

    };

    console.log('a.js-3', mod.obj.count)

    // data是三个援用,直接赋值给它,只是让那些变量等于别的三个引用,并不会修改到obj自个儿

    }, 2000)

    console.log(data); // { name: 'Bob', add: [Function] }

    node a.js

    console.log(obj); // { data: { name: 'Alan', test: [Function] } }

    a.js-1 1

    obj.data = {

    a.js-2 2

    name: 'Bob',

    b.js-1 2

    add: function (a, b) {

    a.js-3 3

    return a   b;

    b.js-2 3

    }

    上述代码可以见到,对于目的的话属于浅拷贝。当实施a模块时,首先打字与印刷obj.count的值为1,然后通过plusCount方法,再一次打印时为2。接着在a模块修改count的值为3,此时在b模块的值也为3。

    };

    3.当用到require命令加载某些模块时,就能够运作总人体模型块的代码。

    // 而经过obj.data技术确实修改到data本人

    4.当施用require命令加载同多个模块时,不会再实施该模块,而是取到缓存之中的值。也正是说,commonjs模块无论加载多少次,都只会在第叁遍加载时运营二遍,以往再加载,就回去第二遍运营的结果,除非手动清除系统缓存。

    console.log(obj); // { data: { name: 'Bob', add: [Function] } }

    5.循环加载时,属于加载时施行。即脚本代码在require的时候,就能够整整实行。一旦出现有个别模块被"循环加载",就只输出已经实施的部分,还未推行的有个别不会输出。

    经过那些例子大家能够见到,data纵然像一个引用同样指向了obj.data,况且经过data能够访问到obj.data上的天性。可是出于JavaScript值传递的性状直接修改data = xxx并不会使得obj.data = xxx。

    3, 4, 5能够运用同一个事例表明

    // b.js

    打个假若最初安装var data = obj.data的时候,内部存储器中的意况大约是:

    exports.done = false

    新葡亰496net 2

    let a = require('./a.js')

    为此通过data.xx能够修改到obj.data的内部存款和储蓄器1。

    console.log('b.js-1', a.done)

    接下来设置data = xxx,由于data是拷贝的三个新的值,只是那么些值是贰个援用(指向内部存储器1)罢了。让它等于其他三个目的就好比:

    exports.done = true

    新葡亰496net 3

    console.log('b.js-2', '执行完结')

    让data指向了新的一块内部存款和储蓄器2。

    // a.js

    只假如古板的援引(如上文中的C 的情状),那么obj.data本身会形成新的内部存款和储蓄器2,但JavaScript中均是值传递,对象在传递的进度中拷贝了一份新的援引。从而那么些新拷贝的变量被转移并不影响原来的靶子。

    exports.done = false

    Node.js中的module.exports与exports

    let b = require('./b.js')

    上述例子中的obj.data与data的关联,正是Node.js中的module.exports与exports之间的涉嫌。让我们来拜谒Node.js中require一个文本时候的实际上组织:

    console.log('a.js-1', b.done)

    Node.js代码

    exports.done = true

    function require(...) {

    console.log('a.js-2', '试行实现')

    var module = { exports: {} };

    // c.js

    ((module, exports) => { // Node.js 汉语件外界其实被包了一层自进行的函数

    let a = require('./a.js')

    // 那当中是您模块内部的代码.

    let b = require('./b.js')

    function some_func() {};

    console.log('c.js-1', '实践完成', a.done, b.done)

    exports = some_func;

    node c.js

    // 那样赋值,exports便不再指向module.exports

    b.js-1 false

    // 而module.exports依旧是{}

    b.js-2 奉行完成

    module.exports = some_func;

    a.js-1 true

    // 那样设置本事改改到原本的exports

    a.js-2 实行完成

    })(module, module.exports);

    c.js-1 实施达成 true true

    return module.exports;

    全面说圣元(Synutra)下全体进程。

    }

    在Node.js中实行c模块。此时境遇require关键字,实施a.js中保有代码。

    就此很自然的:

    在a模块中exports之后,通过require引进了b模块,试行b模块的代码。

    Node.js代码

    在b模块中exports之后,又require引进了a模块,此时实施a模块的代码。

    console.log(module.exports === exports); // true --> exports所操作的便是module.exports

    a模块只施行exports.done = false那条语句。

    Node.js中的exports正是拷贝的一份module.exports的援引。通过exports能够修改Node.js当前文件导出的属性,可是不可能改改到当下模块本人。通过module.exports才得以修改到其自个儿。表现上来说:

    再次回到b模块,打字与印刷b.js-1, exports, b.js-2。b模块推行实现。

    Module.exports代码

    归来a模块,接着打字与印刷a.js-1, exports, b.js-2。a模块实践完结

    exports = 1; // 无效

    回到c模块,接着试行require,供给引进b模块。由于在a模块中曾经引进过了,所以直接就足以输出值了。

    module.exports = 1; // 有效

    结束。

    那是二者显示上的分别,别的地点用起来都未曾差距。所以你未来应当清楚写module.exports.xx = xxx;的人实际上是多写了二个module.。

    从上述结果和剖判进度能够看看,当蒙受require命令时,会试行相应的模块代码。当循环引用时,有望只输出某模块代码的一片段。当援用同七个模块时,不会再度加载,而是获取缓存。

    更目眩神摇的例子

    ES6模块

    为了再演练一下,我们在来看二个比较复杂的例子:

    es6模块中的值属于【动态只读引用】。只说雅培(Beingmate)(Karicare)下头眼昏花数据类型。

    Js代码

    对此只读来讲,即不容许修改引进变量的值,import的变量是只读的,不论是焦点数据类型照旧复杂数据类型。当模块碰到import命令时,就能够变卦几个只读援引。等到脚本真的进行时,再凭借那个只读引用,到被加载的可怜模块里面去取值。

    var a = {n: 1};

    对此动态来讲,原始值爆发变化,import加载的值也会产生变化。不论是骨干数据类型照旧复杂数据类型。

    var b = a;

    // b.js

    a.x = a = {n: 2};

    export let counter = {

    console.log(a.x);

    count: 1

    console.log(b.x);

    }

    依照早先的下结论我们得以一步步的来看这几个标题:

    setTimeout(() => {

    Js代码

    console.log('b.js-1', counter.count)

    var a = {n: 1};    // 援用a指向内部存款和储蓄器1{n:1}

    }, 1000)

    var b = a;  // 引用b => a => { n:1 }

    // a.js

    Js代码

    import { counter } from './b.js'

    a.x = a = {n: 2};  //  (内存1 而不是 a ).x = 引用 a = 内存2 {n:2}

    counter = {}

    a 就算是援用,可是JavaScript是值传的这些援用,所以被修改不影响原来的地点。

    console.log('a.js-1', counter)

    因此最终的结果

    // Syntax Error: "counter" is read-only

    a.x 即(内存2).x ==> {n: 2}.x ==> undefined

    纵然不能够将counter重新赋值三个新的对象,不过可以给指标加多属性和办法。此时不会报错。这种表现类型与根本字const的用法。

    b.x 即(内存1).x ==> 内存2 ==> {n: 2}

    // a.js

    总结

    import { counter } from './b.js'

    Javascript中尚无援引传递,独有值传递。对象(援用类型)的传递只是拷贝三个新的援引,那些新的引用能够访谈原来对象上的习性,然而那一个新的引用作者是放在其他一个格子上的值,直接往那几个格子赋新的值,并不会潜濡默化原来的对象。本文初始所议论的Node.js热更新时相遇的也是以此主题素材,差别是指标自己改换了,而原先拷贝出来的引用还指向旧的内部存款和储蓄器。

    counter.count

    Node.js并未对JavaScript施加黑法力,在那之中的援引难题仍然是JavaScript的源委。如module.exports与exports那样暗藏了一部分细节轻便使人误会,本质依旧JavaScript的标题。别的推荐一个关于 Node.js 的晋级教程 《Node.js 面试》。

    console.log(counter)

    更加的多美丽内容请关切微教徒人号:visoon_weixin

    // 2

    循环加载时,ES6模块是动态援引。只要多个模块之间存在某些援用,代码就可见实行。

    // b.js

    import {foo} from './a.js';

    export function bar() {

    console.log('bar');

    if (Math.random() > 0.5) {

    foo();

    }

    }

    // a.js

    import {bar} from './b.js';

    export function foo() {

    console.log('foo');

    bar();

    console.log('推行实现');

    }

    foo();

    babel-node a.js

    foo

    bar

    推行实现

    // 实践结果也会有望是

    foo

    bar

    foo

    bar

    试行完毕

    实践达成

    鉴于在五个模块之间都设有援用。由此能够健康试行。

    以上以上。对es6 module和commonjs module有不掌握的同桌能够参见一下以下的稿子

    ES6 module

    module的语法

    module的加载完成

    来源:

    本文由新葡亰496net发布于新葡亰官网,转载请注明出处:新葡亰496net:在 Node.js 中看 JavaScript 的引用

    关键词:

上一篇:前端开拓基础,jQuery技术总计

下一篇:没有了