FrankKai / FrankKai.github.io

FE blog
https://frankkai.github.io/
363 stars 39 forks source link

node 标准库常用语法 #77

Open FrankKai opened 6 years ago

FrankKai commented 6 years ago

标准库可以说是node的基础,非常重要。

FrankKai commented 6 years ago

path.join()与path.resolve()什么区别?

一个demonstration就可以说明主要问题:

const path = require('path');
const urlJoin = path.join(__dirname, '../../');
const urlResolve = path.resolve(__dirname, '../../');

console.log(urlJoin,urlResolve);
//urlJoin: /Users/frank/Desktop/ 
//urlResolve: /Users/frank/Desktop

细心的你一定发现了,join返会的路径以分隔符"/"结尾,而resolve以目录名结尾。

这是在传入"../../"的情况下,那如果直接传入目录名呢?

const urlJoinPersonal = path.join(urlJion,"./personal/");
const urlResolvePersonal = path.resolve(urlResolve,"./personal/");

console.log(urlJoinPersonal,urlResolvePersonal);
//urlJoinPersonal: /Users/frank/Desktop/personal/
//urlResolvePersonal: /Users/frank/Desktop/personal

path.join会始终保留路径原来的模样,预留一个空间。path.resolve会自动把多余的/去掉,从而保证是一个有效的目录。

因此我们可以做出总结:

path.join()

主要用于规范化路径

path.resolve('/foo/bar', './baz');
// Returns: '/foo/bar/baz'

path.resolve('/foo/bar', '/tmp/file/');
// Returns: '/tmp/file'

path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif');
// if the current working directory is /home/myself/node,
// this returns '/home/myself/node/wwwroot/static_files/gif/image.gif'

关于path.join与path.resolve,部门老大刚才从源码角度带着我理解了一波,源码地址在这里:https://github.com/nodejs/node/blob/master/lib/path.js

FrankKai commented 6 years ago

process.cwd()

由上面的path.js源码拓展出一个process.cwd()的问题。

老大说:

由于工科男的执着,我们对上面的结论做两次实验:

普通实验

-path
  -dir
      foo.js
      bar.js
  index.js

index.js

const foo = require('./dir/foo');
const bar = require('./dir/bar');

console.log("dir/foo.js:",foo);
console.log("dir/bar.js:",bar);
console.log("index.js",process.cwd())

foo.js

function foo (){
    return process.cwd();
}
module.exports = foo();

bar.js

function bar (){
    return process.cwd();
}
module.exports = bar();
node index.js

输出结果:

dir/foo.js: /Users/frank/Desktop/path
dir/bar.js: /Users/frank/Desktop/path
index.js /Users/frank/Desktop/path

实验表明,正常情况下,process.cwd()返回的是当前的工作目录。也就是最顶级的node index.js的路径。不一定是文件所在路径。正确。


2019.7.31更新

// cwd.js
console.log(process.cwd());

这个cwd()指的是node进程的工作目录: 假如是在/Users/frank/foo/bar目录,node cwd.js 返回/Users/frank/foo/bar。 假如是在/Users/frank/foo/bar/src目录,node ../cwd.js,返回的是/Users/frank/foo/bar/src。 假如是在/Users/frank/foo目录,node ./bar/cwd.js,返回的是/Users/frank/foo。

PM2实验

能力有限,未完待续。

FrankKai commented 6 years ago

fs.readFile()与fs.readFileSync()有什么区别?

二者返回的结果都是目录下文件名数组,最为关键的地方在于Sync关键字。在nodejs中,有大量的*Sync类型的标准库api,就拿fs来说,就有下面这么多。

without sync with sync
fs.access fs.accessSync
fs.appendFile fs.appendFileSync
fs.chmod fs.chmodSync
fs.chown fs.chownSync
fs.close fs.closeSync
fs.copyFile fs.copyFileSync
fs.exists fs.existsSync
fs.fchmod fs.fchmod Sync
fs.fchown fs.fchownSync
fs.fdatasync fs.fdatasyncSync
fs.fsync fs.fsyncSync
fs.ftruncate fs.ftruncateSync
fs.futimes fs.futimesSync
fs.lchmod fs.lchmod Sync
fs.lchown fs.lchownSync
fs.link fs.linkSync
fs.lstat fs.lstaSync
fs.mkdir fs.mkdirSync
fs.mkdtemp fs.mkdtempSync
fs.open fs.openSync
fs.readdir fs.readdirSync
fs.readFile fs.readFileSync
fs.readlink fs.readlinkSync
fs.realpath fs.realpathSync
fs.realpath.native fs.realpath.nativeSync
fs.rename fs.renameSync
fs.rmdir fs.rmdir Sync
fs.stat fs.statSync
fs.symlink fs.symlinkSync
fs.truncate fs.truncateSync
fs.unlink fs.unlinkSync
fs.utimes fs.utimesSync
fs.writeFile fs.writeFileSync

*Sync 类型的是同步函数,它们会立即返回一个值,而其它的是异步函数,返回的是undefined,但是可以接收一个callback去处理它们的响应。

拿fs.readFile与fs.readFileSync来说。

# 同步 synchronous
data = fs.readFileSync ( filename )
# 现在我就可以使用data了
console.log ( data )
# 异步 asynchronous
fs.readFile ( filename, (err, data) =>{
  # 现在data变量在回调函数上下文中是可用的
  # **但是在fs.readFile函数的上下文中是不可调用的**
  console.log(data)
})

虽然同步的方式比较直观,但是对于在node进程同步读取文件的时候,进程处于阻塞状态,不能去做其它事情。而采用异步的方式是非常畅通的。

再来想个问题:为什么不把fs.readFile写成fs.readFileAsync,这样更直观啊?

因为在nodejs的场景中,会大量用到异步的场景,这也是node的优势所在,相比fs.readFileAsync,fs.readFile的写法更简洁,少写了5个字母,可以略微提升coding的速度。

FrankKai commented 6 years ago

os.cpus()的times返回数据的user mode,nice mode,sys mode,idle mode,irq mode是什么?

Operating modes

ARM7TDMI操作模式 User Modes是通常的ARM 程序执行状态,可以用于执行大多数应用程序。 Fast Interrupt (FIQ) mode支持数据转换或者渠道进程。 Interrupt (IRQ) mode被用于general-purpose中断处理。 Supervisor mode是操作系统的保护模式。 Abort mode在数据或者指令Prefetch Abort后输入。 System mode是一种操作系统的特权模式。

Mode Mode identifier
User usr
Fast interrupt fiq
Interrupt irq
Supervisor svc
Abort abt
System sys
Undefined und

由此可见:

FrankKai commented 6 years ago

crypto

image

Crypto Module

crypto模块提供了密码学加密函数集,包括OpenSSL的hash,HMAC,cipher(暗号),decipher(破译),sign(签名)以及认证函数。

const crypto = require('crypto');
const secret = 'abcdefg';
const hash = crypto.createHmac('sha256', secret)
                                  .update('I love you')
                                  .digest('hex');
console.log(hash);

核心方法:createHmac(algorithm, key ,[options])

algorithm <string>
key <string> | <Buffer> | <TypedArray> |<DataView>
options <Object> stream.transform options
Returns: <Hmac>

crypto.createHmac()方法会创建一个Hmac实例。Hmac对象使用new操作符是实例不了的。 使用hmac.update和hmac.digest()生成的Hmac码:

const crypto = require('crypto');
const hmac = crypto.createHmac('sha256', 'a secret');//创建一个key为a secret的Hmac实例。

hmac.update('some data to hash');//使用给定的数据更新Hmac实例内容,数据类型与key类型一致。
console.log(hmac.digest('hex'));
// Prints:
// 7fd04df92f636fd450bc841c9418e5825c17f33ad9c87c518115a45971f7f77e

引申基础:

实践:

FrankKai commented 5 years ago

REPL

repl模块的全称是Read-Eval-Print-Loop(REPL),既可以独立使用也可以被其他应用程序使用。

const repl = require('repl');

设计初衷

命令和特殊键

下面的命令被REPL实例支持:

Last login: Mon Apr 29 11:35:27 on ttys001
frankdeiMac:~ frank$ node
> .editor
// Entering editor mode (^D to finish, ^C to cancel)
function welcome(name) {
  return `Hello ${name}!`;
}

welcome('Node.js User');
'Hello Node.js User!'
> 

下面的快捷键功能如下:

默认执行

默认情况下,repl.REPLServer的所有实例会用一个执行函数执行js表达式并且允许调用nodejs内建模块的权限。这个默认行为可以通过传入一个可执行函数去覆盖。

js表达式
frankdeiMac:~ frank$ node
> 1+1
2
> const m =2
undefined
> m+1
3
> 
全局和本地作用域

默认计算器赋权所有全局作用域中的变量。可以通过给REPLServer的context对象复制显式将变量暴露给REPL。 REPL实例上下文的属性:

> .editor
// Entering editor mode (^D to finish, ^C to cancel)
const repl = require('repl');
const msg = 'message';

repl.start('> ').context.m = msg;
> 'message'

可以通过Object.defineProperty()设置属性为只读:

Object.defineProperty(repl.start('> ').context, 'm', { enumerable: true, value: msg });
FrankKai commented 2 years ago

require.cache

在写vscode插件的过程中,发现通过require获取到的文件是旧的,不能获取到最新的。

原因是cjs的require有缓存机制,因此可以通过下面这种方式去获取:

// 避免require缓存
delete require.cache["/client/static/locales/zh/translation.json"];
const data = require("/client/static/locales/zh/translation.json");

Modules are cached in this object when they are required. By deleting a key value from this object, the next require will reload the module.