邂逅node.js

常见的全局对象

node中是没有window这样一个全局对象的,有的是global

全局对象有很多,不需要全部都学,因为有的不常用

有的不用特别学是因为用着用着就会了

比较重要的有:module、export、require(模块化)、buffer

特殊的全局对象

这个对象特殊在哪?

这些全局对象特殊就特殊在是模块中的变量,只是每个模块都有,像是全局变量

在命令行中是不可以使用的

包括:_dirname、_filename、exports、module、require()

//类似于window的全局对象
console.log(global)

// 特殊的全局对象
// 当前文件所在的目录结构
console.log(__dirname)
// 当前目录+文件名
console.log(__filename)

// 模块化
console.log(module)
console.log(exports)
console.log(require)

// 进程
console.log(process)

// 定时器方法
setTimeout(()=>{
    console.log("setTimeout")
},3000);
setInterval(()=>{
    console.log("setInterval")
},3000);
// 立即执行的
setImmediate(()=>{
    console.log("setImmediate")
});
// 额外执行函数
process.nextTick(()=>{
    console.log("nextTick")
});

global是一个全局对象,process、console、setTimeout等都有被放到global中

在新的 标准中还有一个globalThis也是指向全局对象的

类似于浏览器中的window

但是又有区别

如果你用var定义变量是会被自动添加到window中的

但是不会被添加到global中

由于两个不一样容易记混,所以在新标准中他们有一个共同的名字:globalThis

什么是模块化

模块化开发最终的目的是将程序划分成一个个小的结构

在这个结构中编写属于自己的逻辑代码,有自己的作用域,定义变量名词的时候不会影响到其他的结构

这个结构可以将自己希望暴露的变量函数对象等导出给其结构使用

也可以通过某种方式导入另外结构中的变量、函数、对象等

按照结构划分开发程序的过程就是模块化开发的过程

举个例子:

const moduleA = (function(){
    let name="why"
    let age=18
    let height=1.88
    console.log(name)
    return {
        name,
        age,
        height
    }
}())
console.log(moduleA.name)

这就是设计的一种模块化的方案

但是如果有新人来了不知道你怎么做的就很难受

即没有一个统一的标准

直至ES6官方推出了一个自己的模块化方案:ESModule

CommonJS规范和Node关系

CommonJS是一个规范,最初提出来是在浏览器以外的地方使用,当时被命名为ServerJS,后来为了体现它的广泛性,修改为了CommonJS,平时我们也会简称为CJS

Node是CommonJS在服务器端一个具有代表性的实现

Browerserify是 CommonJS在浏览器中的一种实现

webpack打包工具具备对CommonJS的支持和转换

Node中对CommonJS进行了支持和实现,让我们在开发node的过程中可以更方便的进行模块化开发

在Node中每一个js文件都是一个单独的模块

包括CommonJS规范的 核心变量:exports、module.exports、require

可以用这些变量方便的进行模块化开发

exports和module.exports可以负责对模块中的内容进行导出

require函数可以帮助我们导入其他模块(自定义模块、系统模块、第三方库模块)中的内容

举个例子:

const UTIL_NAME = "util_name"

function formatCount(){
    return "200"
}
function formatDate(){
    return "2022-01-01"
}

exports.UTIL_NAME = UTIL_NAME
exports.formatCount = formatCount
exports.formatDate = formatDate

这样就可以成功的导出

const util = require("./util.js")
console.log(util.UTIL_NAME)
console.log(util.formatCount())

导入是这样的

exports导出

exports是一个对象,可以在这个对象中添加很多个属性,添加的属性可以导出

const util = require("./util.js")

这行代码完成的操作是main中的bar变量就等于exports对象

require通过各种查找方式最终找到了exports这个对象

并且将这个exports对象赋值给了bar变量

所以bar变量就是exports对象了

举个例子,这是bar.js:

let name = "bar"
exports.name = name

setTimeout(()=>{
    exports.name = "why"
},3000)
// const util = require("./util.js")
// console.log(util.UTIL_NAME)
// console.log(util.formatCount())

const bar = require("./bar.js")
console.log(bar.name)

//4s后重新获取home
setTimeout(()=>{
    console.log(bar.name)
},4000)

这是把导出的变量改变,所以输出的结果也会随之而改变

还有另一个实例

这是main.js

const bar = require("./bar.js")
console.log(bar.name)

setTimeout(()=>{
    bar.name = "why"
},3000)

介素bar.js: 

let name = "bar"
exports.name = name

setTimeout(()=>{
    console.log(exports.name)
},4000)

最后打印出来依然是bar、why

这个赋值本质上是引用赋值

module.exports导出

这种导出是用的比较多的一种

具体这样使用:

const name = "foo"
const age = 18
function sayHello(){
    console.log("hello")
}

module.exports.name = name
module.exports.age = age
module.exports.sayHello = sayHello
const foo=require("./foo")

console.log(foo.name)
console.log(foo.age)
console.log(foo.sayHello())

他和普通的exports导出有什么区别捏?

邂逅node.js_第1张图片

这张图可以解释他们的结构

可以看出在做修改的时候是一样的,那么为什么有两种写法呢?这个module对象 又好在哪呢

首先一个重要的结论:Node导出的本质是在导出module.exports对象

在开发中的常见写法是这样的:

module.exports = {
    
}

这样的写法是创建新对象

那么为什么还要有exports呢?

还是看CommonJS的规范

CommonJS中是没有module.exports的概念的

但是为了实现模块的导出,Node中使用的是Module的类,每一个模块都是Module的一个实例,也就是module

在Node中真正用于导出的不是exports,而是module.exports

因为module才是导出的真正实现者

为什么exports也可以导出呢?

是因为module对象的 exports属性是exports对象的 一个引用

所以module.exports = exports = main中的bar

require细节

讲一下require的细节

有些情况下路径是可以省掉的

邂逅node.js_第2张图片

我们在本地的路径下先写一个这样的结构,在main.js中使用这个导出的函数,可以发现:

const utils = require("./utils")

console.log(utils.formatDate())

不用写特别完全的路径

这和require的查找规则有关(require X):

1.当X是一个Node的核心模块,比如path、http:直接返回核心模块并停止查找

2.X以./、../、或者/开头:

第一步:

将X当做一个文件在对应的目录下查找(如果有后缀名则按照后缀名格式查找对应的文件,如果没有则按照这个顺序查找:直接查找文件X-->查找X.js-->查找X.json-->查找X.node)

第二步:

没有找到对应的文件,将X作为一个目录

查找目录下面的index文件

查找X/index.js文件

查找X/index.json文件

查找X/index.node文件

如果还没找到那就报错:not found

3.X既不是路径也不是核心模块:

先在当前目录的 node_modules下找,再逐层向上的去对应的nodemodules上找

直至找到根目录下的node_modules

如果都没找到那就报错:not found

模块的加载过程

模块在第一次被引入的时候模块中的js代码会被运行一次

被多次引入时会缓存,最终只加载(运行)一次

为什么呢?

因为每个模块对象module都有一个属性:loaded

为false表示还没有加载,为true表示已经加载

如果有循环引入的话加载顺序是什么?

邂逅node.js_第3张图片

这是图结构

图结构在遍历的过程中,有深度优先搜索和广度优先搜索

Node采用的是深度优先算法

CommonJS加载缺点

首先它的加载是同步的,同步就意味着只有等到对应的模块加载完毕,当前模块中的内容才能运行

这个在服务器是没问题的(因为服务器加载的js文件都是本地文件,加载速度很快)

如果将它应用于浏览器

浏览器加载js文件需要先从服务器将文件下载下来,之后再加载运行

采用同步就意味着后序的 js代码无法正常运行,即使是简单的DOM操作

所以在浏览器中通常不使用CommonJS规范

在webpack中使用CommonJS是另外一回事

因为它会把代码转成浏览器可以直接执行的 

AMD规范

这是应用于浏览器的一种规范,是异步加载模块

require.js的使用

首先是下载require.js

然后是定义html的script标签引入require和定义入口文件

data-main属性的作用是在加载完src的文件后加载执行该文件

CMD规范

CMD也是应用于浏览器的一种模块化规范

采用的是异步加载模块,将CommonJS的优点吸收了过来

CMD的自己比较优秀的实现方案是SeaJS

也是先下载后使用

你可能感兴趣的:(node.js,node.js)