node 中 __dirname 、process.cwd()、. 的区别分析

1,008 阅读4分钟

前言

如果想用 node 写一些涉及文件处理的程序时,经常会碰到文件读写的步骤,而文件读写操作首先先需要确定路径, node 中则提供了很多变量和方法帮助我们完成这一工作,例如 __filenameprocess.pwd 等。

为了让我们写程序时,快速灵活使用各种方式拼接路径,本文将相关方法辨析如下。

获取文件路径的方法及辨析

  1. __filename

返回当前【执行文件】所在的绝对路径。

值得注意的是,__filename 返回路径是当前【执行文件】所在的路径,例如有: blog/dir.jsblog/sub-dir/sub-dir.js 文件,其代码为:

// blog/dir.js 文件
require('./sub-dir/sub-dir')
console.log('__filename :>> ', __filename);

// blog/sub-dir/sub-dir.js 文件
console.log('__filename :>> ', __filename);

C:\Users\isaaczhou\Desktop\博客项目\dir-test\ 执行 node .\blog\dir.js ,结果为:

__filename :>>  C:\Users\isaaczhou\Desktop\博客项目\dir-test\blog\sub-dir\sub-dir.js
__filename :>>  C:\Users\isaaczhou\Desktop\博客项目\dir-test\blog\dir.js

__filename 是由当前正在执行的模块文件所确定,我们可以通过另外一个例子来确定:

require('./sub-dir/sub-dir')
console.log('__filename :>> ', __filename);


// blog/sub-dir/sub-dir.js 文件
function printFileName() {
    console.log('__filename :>> ', __filename);
}

可见,不管如何被其他文件调用,__filename 返回的值都是执行文件所在的目录。

  1. __dirname

返回当前【执行文件】所在目录的绝对路径,参考官网,它的效果类似 path.dirname(__filename)

// blob/sub-dir/dir.js

const path = require('path');
console.log(__dirname);
// 打印: C:\Users\isaaczhou\Desktop\博客项目\dir-test\blog\sub-dir
console.log(path.dirname(__filename));
// 打印: C:\Users\isaaczhou\Desktop\博客项目\dir-test\blog\sub-dir

那么其实它与 __dirname 区别,就是是否包含当前文件的路径,其他方面的特征应该完全相同。

  1. process.cwd()

process 即为当前 node 的 node 进程, process.cwd() 方法则是获取该进程的工作目录,也就是触发的 node 命令的路径。

# 在 C:\Users\isaaczhou\Desktop\博客项目\dir-test 执行 node ./blog/cwd.js
$ node ./blog/cwd.js
process.cwd() :>>  C:\Users\isaaczhou\Desktop\博客项目\dir-test

那么和上文的 __filename 区别就是:process.cwd() 的返回值在 node 进程一启动时就决定了,不会像 __filename 那样执行不同文件就是不同的。我们可以把 cwd 当作 node global 属性,而 __filename__dirname 则是每个模块的局部属性,这点可以在 node require 的源码中看到:

// eslint-disable-next-line func-style
let wrap = function(script) {
  return Module.wrapper[0] + script + Module.wrapper[1];
};

const wrapper = [
  '(function (exports, require, module, __filename, __dirname) { ',
  '\n});',
];

可以看出所有被 require 加载的模块内容都会被注入 __filename __dirname 的变量。

  1. path.resolve('./') 和 path.resolve('/')
  • ./ 表示从工作区目录开始,所以返回的是当前工作目录的绝对路径。
  • / 表示从系统的根目录开始,所以返回的是系统根目录的绝对路径。
  1. 与 require 的辨析

基于当前文件的相对路径寻找其他文件需要有一个前提,就是你有方法知道【当前文件】的信息,此时唯一的方法就是上文的 __dirname(或者__filename)。

这时会有疑问,require 都可以直接基于当前文件相对路径,寻找其他文件的位置时,为何我们我们还需要用借助 __dirname 拼接路径呢?

参考 StackOverflow 上一则回答:除了 require 的几乎所有的函数都会把【文件路径】当作 fs 的参数,而 fs 是 node 下通用模块,不会拿到当前文件信息,所以当想基于当前 js 文件使用相对路径去访问其他文件时,除了 require,都需要结合 __dirname 去拼接。

之所以 require 能够拿到当前文件信息,是因为 require 针对每个 js 文件都是唯一的(而不是像 fs 那么通用)。

可以将 require 看成为一个闭包返回的函数,并为里边注入了当前 js 文件的路径信息,每个文件下的 require 都是不一样的引用。

function createRequire() {
  
  return (relPath) => {
    const realpath = path.resolve(curPath, request);
    // 加载模块内容,并注入 __filename 和 __dirname
    // ...loadModule(realpath)
  }
}
const myRequire = createRequire();
myRequire('./xxx');

require 是从入口模块开始逐级寻找子模块的,所以当加载子模块时,已经拥有了 parent 模块的路径,require 初始是从 package.jsonmain 或者 process.cwd() 出发寻找,所以我们才能在每个子模块下拿到当前模块的路径信息,并进而传给 __dirname __filename,具体可以参考node/loader.js at main · nodejs/node (github.com)

参考

www.geeksforgeeks.org/difference-…

blog.csdn.net/weixin_4397…

node/loader.js at main · nodejs/node (github.com)

What's the difference between process.cwd() vs __dirname? (newbedev.com)