Node概述

强在大吞吐量,可以接受大量的网络请求,不用切换进程/线程,因为他是异步单线程,也导致不适合处理高并发的大量计算。适合作为中间层(腾讯视频采用的nodejs作为中间层(搜索引擎的优化、首屏的优化))
优点:node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,异步编程,使其轻量又高效。
缺点:单进程,单线程,只支持单核cpu,不能充分的利用多核cpu服务器。一旦这个进程崩掉,那么整个web服务就崩掉了。
image.png

全局对象—global

类似window,global.global, window.window, 主要是为了访问自身。global里常用属性如下:

  • setTimeout
  • setInterval

同浏览器环境下机制一样,但这俩的返回结果不同于浏览器环境里的数字,它俩都是返回一个对象

  • setImmediate
    • 类似于setTimeout, 0; 肯定有区别啊!到时在node生命周期里再谈
  • console
  • __dirname
    • 它不是global的属性,那它从哪儿来呢?后续再论
    • 表示获取当前模块所在的目录的绝对路径!!很常用
  • __filename
    • 它也不是global的属性
    • 获取当前模块(文件)的绝对路径,常用
  • Buffer:类型化数组,继承自Uint8Array。常用
    • 它里面的每个数字都是无符号整数8位(8bit),即一个字节,即0-255
    • 计算机中存储的基本单位:字节(8bit/byte)
    • 使用时、输出时可能要用16进制表示每个数字,即两个0-e的表示一个数字
    • 例:Buffer.from(“abcdegf”, “utf-8”),打印得到:
  • process:它的常用属性/方法如下

    • cwd(): 返回当前nodejs进程的工作目录的绝对路径。就是在哪儿运行的node命令的目录路径
    • exit(状态码: 默认0): 强制退出node进程,状态码为0时正常退出,为1是错误退出
    • argv: 一个数组,[node进程绝对路径,运行文件绝对路径,…命令行参数]
    • platform: 获取当前的操作系统信息
      • 返回win32,代表编程的平台版本支持32位及以上,不代表我们的电脑是32位的
    • kill(pid): 根据进程id杀死一个进程,如果无论它是什么id都要杀死它,该怎么做呢
      • 要用到内置库:child process
    • env: 系统的环境变量; 是一个对象

      Node的模块化细节

      目前node默认是commonjs(因为那会还没ES模块化)模块化,但因为ES是标准,所以日后一定会支持好

      关于模块的查找:

  • 绝对路径:根据绝对路径直接夹在模块/文件

  • 相对路径第一种:./或../开头
    • 相对于当前模块所在目录,最终也是会转为绝对路径自行查找
  • 相对路径第二种,查找规则如下:
      1. 检查是否是内置模块,如:fs、path等
      1. 检查当前目录中的node_modules
      1. 依次检查上层目录中的node_modules
      1. 找到后转为绝对路径,找不到就报错
      1. 加载模块
  • 关于后缀名,规则如下:
    • 如果不提供后缀名,则自动补全(按顺序):js、json、node、mjs
  • 关于文件名:
    • 如果仅提供目录,不提供文件名,则先 在当前目录自动补全后缀找文件,如果找不到,则找对应目录下的index.js(这个可以配置,默认是index.js)
    • package.json中的main字段:
      • 表示包(就是第三方库,不包括我们自己的src)的默认入口
      • 导入或执行包时,若仅提供目录,则使用main补全入口
      • main的默认值为index.js

module对象:

记录了当前模块的信息,它里面的属性有:

  • id:这个模块的绝对路径。如果是根入口模块,则为一个点,并且其parent为null
  • path:这个模块所在目录的绝对路径 —> __dirname
  • exports:导出的对象
  • parent:module对象,是该模块的父模块,即该模块被谁引用了
  • filename:这个模块的绝对路径 —> __filename
  • loaded:该模块是否加载完
  • children:module数组,该模块的子模块有哪些
  • paths:绝对路径数组,每条记录就是该模块要去哪个node_modules下查

require函数

它的属性如下:

  • resolve:一个函数,返回一个绝对路径
  • main:module对象,根入口起点模块的信息
  • extensions:如何处理四种不同的后缀名文件,.js .json .node .mjs
  • cache:对象,存储着哪些模块被缓存了,键值对,键名就是模块绝对路径,值就是modul对象信息

    require函数原理

    require怎么实现模块化的呢?
    当执行一个模块或使用require时,会将模块放置在一个函数环境中

模拟require实现原理:

  1. function require(modulePath) {
  2. //1. 将modulePath转换为绝对路径:D:\repository\NodeJS\源码\myModule.js
  3. //其实这个第一步不简单,因为要判断是哪种路径写法,然后采取不同策略,转换为绝对路径
  4. //2. 判断是否该模块已有缓存
  5. // if(require.cache["D:\\repository\\NodeJS\\源码\\myModule.js"]){
  6. // return require.cache["D:\\repository\\NodeJS\\源码\\myModule.js"].result;
  7. // }
  8. //3. 读取文件内容, I/O流
  9. //4. 把文件内容包裹到一个函数中
  10. // 所以说这个__dirname和__filename不是global属性的原因就在这,因为我们在上面得到了绝对路径,就可以传给这个函数
  11. function __temp(module, exports, require, __dirname, __filename) {
  12. console.log("当前模块路径:", __dirname);
  13. console.log("当前模块文件:", __filename);
  14. exports.c = 3;
  15. module.exports = {
  16. a: 1,
  17. b: 2
  18. };
  19. this.m = 5;
  20. }
  21. //6. 创建module对象
  22. module.exports = {};
  23. const exports = module.exports;
  24. //7. 绑定this,并且执行包裹函数
  25. __temp.call(module.exports, module, exports, require, module.path, module.filename)
  26. //8. 最终返回module.exports
  27. return module.exports;
  28. }

所以,在一个文件最初,module.exports === exports === this,但是之后就不一定了,看个人操作

node中this指向

  1. 全局中的this,综上,默认指向module.exports,在无导出时,就是一个空对象 {}
  2. 函数中的this,默认指向 global
  3. 构造函数中的this,默认指向其实例

    【扩展】Node中的ES模块化

    目前,在Node中,的ES模块化仍然处于试验阶段。模块要么是commonjs,要么是ES
    因为ES不是包裹在函数环境中,虽然v8能这么做,但是ES模块化不是这样,

commonjs:默认情况下,都是commonjs

转变为ES模块:

  • 文件后缀名为.mjs
  • 离得最近的package.json中type设为module,但这样那个目录都成.mjs不太好
  • 使用ES模块化运行时,启动命令必须添加—experimental-modules标记。
    1. "scripts": {
    2. "start": "node --experimental-modules index.mjs"
    3. },
    使用了ES模块,因为它不是在函数环境内运行,而是在引擎内部搞定的。
    部分commonjs部分ES模块化,则大部分情况下,是要出问题的!!
    webpack是解决了,它把所有东西都放到了函数环境里

ES模块的异步加载:

  1. import ("./a.mjs").then(r => console.log(r));

基本内置模块

具体看官网

os

os常用的属性/方法:

  • EOL(end-of-line):mac、unix、Linux下是 \n,windows下是\r\n
  • arch(): 获取cpu的架构名
  • cpus(): 获取cpu每个内核的信息
  • freemem(): 得到当前剩余空闲内存大小, 单位:字节
  • homedir(): 获取用户目录的绝对路径
  • hostname(): 获取主机名
  • tmpdir(): 获取操作系统的临时目录

    path—常用

    path模块常用属性/方法如下:注意:path里的方法都不会管传入路径对应模块是否真实存在!就当成字符串!

  • basename(): 获取整个路径中的最后一部分,比如:index.html

  • delimiter: 分隔符,不同路径间的分割。windows上是 ; linux, mac上就是 :
  • sep: 分隔符,一个路径内部的分割。windows上是 \ linux, mac上就是 /
  • dirname(): 获取目录的绝对路径
  • extname(): 得到传入路径的最后一个.的后缀名,比如:.js.mjs —>> .mjs
  • join(): 可以传入几段路径,它会将其拼接为完整路径(不同操作系统值不一样)
  • normalize(): 帮我们规范化一个路径
  • relative(): 得到第二个路径相对于第一个路径 的 相对路径
  • resolve(): 返回一个绝对路径

    1. // 这样的话无论在哪儿启动的node命令,都会正确得到该a.js的绝对路径。因为它也会在内部拼接
    2. const absPath = path.resolve(__dirname, "./a.js");

    关于./

    路径只有在require里面才是相对于当前目录。在其他任何地方,都是相对于启动node命令的目录的路径!即process.cwd()

    url

    1. const URL = require("url");
    2. const url = new URL.URL("https://nodejs.org:80/a/b/c?t=3&u=5#abc");
    3. // const url = URL.parse("https://nodejs.org:80/a/b/c?t=3&u=5#abc");//它内部帮我们调用URL构造函数
    4. console.log(url);
    5. console.log(url.searchParams.has("a"));//searchParams是一个类似Map的东西,因为那会还没ES6+
    6. const url = URL.format(obj);//将url对象格式的普通对象转为url链接

    util

    util的常用方法如下,主要当时还没有ES6^:

  • callbackify(original): 传入一个异步函数,返回一个回调模式的新函数 ```javascript async function delay(duration = 1000) { return new Promise(resolve => { setTimeout(() => {

    1. resolve(duration);

    }, duration); }); }

const delayCallback = util.callbackify(delay);

delayCallback(500, (err, d) => { console.log(d);//node习惯把error定义在第一个参数 });

  1. - **promisify():** 传入一个回调模式的函数,返回一个新的异步函数
  2. ```javascript
  3. function delayCallBack(duration, callback) {
  4. setTimeout(() => {
  5. callback(null, duration);
  6. }, duration);
  7. }
  8. const delay = util.promisify(delayCallBack);
  9. //delay(500).then(d => console.log(d));
  10. (async () => {
  11. const r = await delay(500);
  12. console.log(r);
  13. })();
  • isDeepStrictEqual(obj1, obj2): 将两个对象深度严格比较
  • inherits(子类,父类): ES6出来之前还比较常用