Skip to content

ch2

目录


摘抄内容小结

在 JS 中,值有两种形式: 原始值和对象值 。

null 和 undefined 是原始值。


js
while (value != undefined) {
  console.log("Still got something!");
}

使用 value != undefined 判断语句可以同时排除nullundefined


JS 中的所有值比较都会考虑被比较值的类型,而不仅仅是 === 运算符。具体来说, === 在其比较中不允许任何类型的类型转换(又称“强制转换”),而其他 JS 比较则允许强制转换。

在关系比较符中,如果含有字符串和数字,则字符串会转换为数字,然后再进行比较。如果都为字符串,则进行字符串比较


CJS(const xx= require("xxxx.js"))是 Nodejs 早期模拟的模块方案,ES 规范中应使用 ESM(import xx from "xxxx") 来实现模块。但是由于向后兼容,使用.mjs 来指定使用 ESM 模式

内容摘抄

The most fundamental unit of information in a program is a value. Values are data. They're how the program maintains state. Values come in two forms in JS: primitive and object. 程序中最基本的信息单位是值。值就是数据,是程序维护状态的方式。在 JS 中,值有两种形式: 原始值和对象值 。


In addition to strings, numbers, and booleans, two other primitive values in JS programs are null and undefined. While there are differences between them (some historic and some contemporary), for the most part both values serve the purpose of indicating emptiness (or absence) of a value. 除了字符串、数字和布尔值之外,JS 程序中另外两个原始值是 null 和 undefined 。虽然它们之间存在差异(一些是历史原因,一些是当代原因),但在大多数情况下,这两个值都用于表示值的空值 (或缺失)。

Many developers prefer to treat them both consistently in this fashion, which is to say that the values are assumed to be indistinguishable. If care is taken, this is often possible. However, it's safest and best to use only undefined as the single empty value, even though null seems attractive in that it's shorter to type! 许多开发人员倾向于以这种方式一致地处理它们,也就是说,假设这些值是无法区分的。如果小心谨慎,这通常是可以实现的。然而,最安全、最好的做法是只使用 undefined 作为单个空值,尽管 null 看起来很有吸引力,因为它的输入更简洁!

js
while (value != undefined) {
  console.log("Still got something!");
}

使用 value != undefined 判断语句可以同时检测nullundefined

js
> undefined != undefined
false
> undefined != null
false
> undefined != 1
true

For distinguishing values, the typeof operator tells you its built-in type, if primitive, or "object" otherwise: 为了区分值, typeof 运算符会告诉您其内置类型(如果是原始类型)或 "object" :

js
typeof 42; // "number"
typeof "abc"; // "string"
typeof true; // "boolean"
typeof undefined; // "undefined"
typeof null; // "object" -- oops, bug!
typeof { a: 1 }; // "object"
typeof [1, 2, 3]; // "object"
typeof function hello() {}; // "function"

WARNING: 警告: typeof null unfortunately returns "object" instead of the expected "null". Also, typeof returns the specific "function" for functions, but not the expected "array" for arrays. 遗憾的是, typeof null 返回的是 "object" 而不是预期的 "null" 。此外,对于函数, typeof 返回的是特定的 "function" ,而对于数组,则不会返回预期的 "array" 。


Another way ==='s equality comparison is often described is, "checking both the value and the type". In several of the examples we've looked at so far, like 42 === "42", the type of both values (number, string, etc.) does seem to be the distinguishing factor. There's more to it than that, though. All value comparisons in JS consider the type of the values being compared, not just the === operator. Specifically, === disallows any sort of type conversion (aka, "coercion") in its comparison, where other JS comparisons do allow coercion. === 的相等性比较的另一种常见描述是“同时检查值和类型”。在我们目前看到的几个例子中,例如 42 === "42" ,两个值的类型 (数字、字符串等)似乎是区分因素。然而,事情远不止于此。JS 中的所有值比较都会考虑被比较值的类型,而不仅仅是 === 运算符。具体来说, === 在其比较中不允许任何类型的类型转换(又称“强制转换”),而其他 JS 比较则允许强制转换。

Consider: 考虑:

42 == "42"; // true 1 == true; // true In both comparisons, the value types are different, so the == causes the non-number values ("42" and true) to be converted to numbers (42 and 1, respectively) before the comparisons are made. 在这两种比较中,值的类型不同,因此 == 会导致非数字值( "42" 和 true )在进行比较之前转换为数字(分别为 42 和 1 )。

Just being aware of this nature of ==—that it prefers primitive numeric comparisons—helps you avoid most of the troublesome corner cases, such as staying away from a gotchas like "" == 0 or 0 == false. 只要意识到 == 的这种性质——它更喜欢原始的数字比较——就可以帮助您避免大多数麻烦的极端情况,例如远离像 "" == 0 或 0 == false 的陷阱。

You may be thinking, "Oh, well, I will just always avoid any coercive equality comparison (using === instead) to avoid those corner cases"! Eh, sorry, that's not quite as likely as you would hope. 你可能会想:“好吧,那我就尽量避免强制相等比较(用 === 代替),这样就能避免那些极端情况了。” 呃,抱歉,这种情况不太可能像你希望的那样发生。

There's a pretty good chance that you'll use relational comparison operators like <, > (and even <= and >=). 您很有可能会使用关系比较运算符,例如 < 、 > (甚至 <= 和 >= )。

Just like ==, these operators will perform as if they're "strict" if the types being relationally compared already match, but they'll allow coercion first (generally, to numbers) if the types differ. 就像 == 一样,如果进行关系比较的类型已经匹配,这些运算符将表现得像“严格”的一样,但如果类型不同,它们将首先允许强制转换(通常是数字)。

Consider: 考虑:

var arr = [ "1", "10", "100", "1000" ]; for (let i = 0; i < arr.length && arr[i] < 500; i++) { // will run 3 times } The i < arr.length comparison is "safe" from coercion because i and arr.length are always numbers. The arr[i] < 500 invokes coercion, though, because the arr[i] values are all strings. Those comparisons thus become 1 < 500, 10 < 500, 100 < 500, and 1000 < 500. Since that fourth one is false, the loop stops after its third iteration. i < arr.length 比较是“安全的”,不会被强制转换,因为 i 和 arr.length 始终是数字。然而, arr[i] < 500 会引发强制转换,因为 arr[i] 值都是字符串。因此,这些比较结果变成了 1 < 500 、 10 < 500 、 100 < 500 和 1000 < 500 。由于第四个比较结果为 false,循环在第三次迭代后停止。

These relational operators typically use numeric comparisons, except in the case where both values being compared are already strings; in this case, they use alphabetical (dictionary-like) comparison of the strings: 这些关系运算符通常使用数字比较,除非比较的两个值已经是字符串的情况;在这种情况下,它们使用字符串的字母(类似字典)比较:


使用 ESM 模块

虽然 Node.js 从诞生起就支持模块,但 JavaScript 语言本身长期以来却一直没有模块功能,只能由 CommonJS 或其他 AMD 等模块系统来“模拟”。

CommonJS(Node.js 使用)

js
// math.js
function add(a, b) {
  return a + b;
}
module.exports = { add };

// app.js
const { add } = require("./math");
console.log(add(1, 2));

现在我们有 ESM 和 CJS 的使用权限,但是这导致了一些本不该有的混乱

为啥使用 ESM 需要用 mjs?

Node.js 最初是基于 CommonJS 构建的(也就是说 .js 默认被当作 CJS)。后来标准化的 ESM 出现后,Node.js 想支持它,但不能直接让所有 .js 文件都变成 ESM,否则会破坏老代码的兼容性。

于是引入了这两个方案:

文件名或配置默认模块系统
.js 文件默认是 CommonJS
.mjs 文件强制视为 ESM
package.json 中声明 "type": "module"让 .js 视为 ESM
.cjs 文件强制视为 CommonJS

ESM 模式中可以导入 CommonJS 模块但有限制:CJS 的 module.exports = {...} 会变成 ESM 的默认导出

为什么?

特性ESM (import)CJS (require)
加载方式静态加载:编译阶段就知道依赖动态加载:运行时执行 require()
同步/异步异步加载(底层基于 Promise)同步加载(阻塞式)
导出机制只读绑定(Live Binding)值拷贝(值是快照)
顶级作用域严格模式、支持 await(顶级 await)普通 JS
执行顺序须等依赖全部加载后执行加载即执行

具体其实也没弄懂,暂时理解为

CJS 是一种模拟的行为,在 require 时会进行计算并产生结果

假设一个非常恶心的导出:

js
// cjs.js (CJS 模块)
module.exports = JSON.parse('{"foo":1,"bar":2}');

const { foo } = require("./cjs"); 变为 const { foo } = {"foo":1,"bar":2}

但是 ESM 需要在未运行的时候就确定依赖关系,但是不运行就不知道

同时 ESM 对 CJS 的兼容是 Nodejs 实现的,本来不是 ES 规范的一部分

于是, ESM 只能将 CJS 模块中所有导出的东西打成一个 default,供后续使用

ESM 中不支持 export JSON.parse('{"foo":1,"bar":2}'); 语法,ES 导出必须为变量/对象/函数等确定的东西

类似的代码只能这么实现:

js
const obj = JSON.parse('{"foo":1,"bar":2}');
export { obj };

同时,在 ESM 中,使用 export default 导出的模块,导入时 不能用 {} 结构(命名导入),原因应该类似

————————————————
版权声明:本文为 田园幻想乡 的原创文章,遵循 CC 4.0 BY-NC-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接: http://truraly.fun/学习笔记/JavaScript/《你并不了解 JavaScript》/Get Started/ch2.html


发布时间:

最后更新时间:

Copyright © 2022 田园幻想乡 浙ICP备2021038778号-1