首先我在目录下创建一个age.txt
var age = 18
创建一个y1.js
const fs = require('fs')
let content = fs.readFileSync('age.txt', 'utf-8')
console.log(content)
eval(content)
console.log(age)
这里因为我没有配置Node.js,所以我将代码改为.txt在浏览器中为大家调试(结果一样)
fetch('age.txt')
.then(response => response.text())
.then(content => {
console.log(content);
eval(content);
console.log(age);
})
.catch(error => console.error('An error occurred:', error));
可以发现我们通过eval执行了一个字符串,但是这种执行方式如果在当前作用域下已经有了同名的age变量,这个程序就会报错。
在js中每一个模块都有自己独立的作用域,所以用eval执行字符串代码很容易出现上面的这个问题,我们再看另外一种方法。
上面的方法因为模块间的作用域被限制了使用,那么我们考虑一下如果能够自己创建一个作用域是不是就可以更加方便的执行代码呢?new Function的第一个参数是形参名称,第二个参数是函数体。
我们都知道函数内和函数外是两个作用域,不过当在函数中的作用域想要使用函数外的变量时,要通过形参来传递,当参数过多时这种方法就变的麻烦起来了。
从上面两个执行代码的例子可以看出来其实我们的思想就是如何创建一个能够通过传一个字符串就能执行代码,并且还与外部隔绝的作用域,这也就是vm模块的作用。
说到作用域,我们就要说一下Node中的作用域是怎么分配的(在Node中一般把作用域叫上下文)。
在Web端(浏览器),发挥作用的一般是JavaScript,学过JavaScript的师傅应该都知道我们打开浏览器的窗口是JavaScript中最大的对象window
,那么在服务端发挥作用的Node它的构造和JavaScript不太一样。
我们在写一个Node项目时往往要在一个文件里ruquire其他的js文件,这些文件我们都给它们叫做“包”。每一个包都有一个自己的上下文,包之间的作用域是互相隔离不互通的,也就是说就算我在y1.js中require了y2.js,那么我在y1.js中也无法直接调用y2.js中的变量和函数,举个例子。
在同一级目录下有y1.js
和y2.js
两个文件
y1.js:
var age = 20
y2.js:
const a = require("./y1")
console.log(a.age)
运行y2.js发现报错:
那么我们想y2中引入并使用y1中的元素应该怎么办呢,Node给我们提供了一个将js文件中元素输出的接口exports
,把y1修改成下面这样:
y1.js:
var age = 20
exports.age = age
我们再运行y2就可以拿到age的值了
图解:
这个时候就有人会问左上角的global是什么?这里就要说到Nodejs中的全局对象了。
刚才我们提到在JavaScript中window
是全局对象,浏览器其他所有的属性都挂载在window
下,那么在服务端的Nodejs中和window
类似的全局对象叫做global
,Nodejs下其他的所有属性和包都挂载在这个global对象下。在global下挂载了一些全局变量,我们在访问这些全局变量时不需要用global.xxx
的方式来访问,直接用xxx
就可以调用这个变量。举个例子,console
就是挂载在global下的一个全局变量,我们在用console.log
输出时并不需要写成global.console.log
,其他常见全局变量还有process(一会逃逸要用到)。
我们也可以手动声明一个全局变量,但全局变量在每个包中都是共享的,所以尽量不要声明全局变量,不然容易导致变量污染。用上面的代码举个例子: