澳门新萄京官方网站-www.8455.com-澳门新萄京赌场网址

澳门新萄京官方网站在 Node.js 中看 JavaScript 的援

2019-08-24 作者:澳门新萄京赌场网址   |   浏览(101)

在 Node.js 中看 JavaScript 的引用

2017/05/05 · JavaScript · NodeJS

原作出处: lellansin   

开始时期学习 Node.js 的时候 (二零一三-2013),有挺多是从 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 一下。尝试如下:

澳门新萄京官方网站在 Node.js 中看 JavaScript 的援引。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 的时候 (2012-二零一三),有挺多是从 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 的时候,内部存款和储蓄器中的景况大致是:

澳门新萄京官方网站在 Node.js 中看 JavaScript 的援引。|   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 的时候 (2012-二零一三),有挺多是从 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: 'Alan' } // 并未退换① 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: '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] } }

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 赞 收藏 评论

澳门新萄京官方网站 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 相关的正儿八经由于野生性质,在 二零一零 年内外出生。AMD、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 相对就晚了许多。被大家所通晓和行使也是 二〇一四年之后的事了。 那实际要感激 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() {

为了通晓那几个相比刚强的定论,让我们来看一段代码:

到前段时间截至,已经实习了7个月的年华了。前段时间在面试,在面试题里面有标题涉及到模块循环加载的学识。趁着那些空子,将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 的时候,内部存款和储蓄器中的意况大若是:

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

app.listen(3000);

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

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

在重复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

澳门新萄京官方网站 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

澳门新萄京官方网站 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;

留意说可瑞康(Karicare)下全勤进度。

}

在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模块中的值属于【动态只读援用】。只说澳优下眼花缭乱数据类型。

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的加载达成

来源:

本文由澳门新萄京官方网站发布于澳门新萄京赌场网址,转载请注明出处:澳门新萄京官方网站在 Node.js 中看 JavaScript 的援

关键词: