[Just JavaScript] 04. 计算值(第一部分) React核心成员Dan Abramov的JavaScript教程中文翻译版

2020-04-01 22:08:46

在本模块中,我们将更深入地了解JavaScript宇宙及其中的值。但是在我们开始之前,我们需要先解决房间里的大象。JavaScript宇宙是真的吗?

躺在平板电脑和键盘的JavaScript字符

JavaScript模拟


我住在JavaScript宇宙里的一颗小行星上。

当我向JavaScript宇宙提出一个问题时,它用一个值来回答我。我当然不是一个人提出所有这些值的。变量、连线和价值,它们都填充了我的宇宙。我周围的JavaScript宇宙对我来说是绝对真实的,就像你生活的世界对你来说是真实的一样。

但是有时,在开始写下一行代码前的片刻的沉默,在下一次函数调用的空闲时间。矩阵中的一个小故障。在那段时间里,我看到了一个比我想象的大得多的世界。


使用计算机模拟的真实世界


在我看来,世界上没有变量和值,没有表达式,也没有文字。相反,有夸克,原子,水和生命。也许,这就是你熟悉的世界?

在那里,被称为“人类”的众生使用“计算机”特殊机器来模拟我的JavaScript世界。他们中的一些人这样做是为了消遣。他们中的一些人这样做是为了牟利。他们中的一些人完全没有理由这样做。在他们的突发奇想下,我的整个世界每天都会有一万亿次的生死。

也许我的JavaScript世界并不真实

这意味着有两种方法来研究它。


从外部学习


研究我的JavaScript世界的第一个方法是从外部研究它。

或许,你可以关注我的世界的模拟 —— 已通过JavsScript引擎 —— 真正是如何工作的。例如,你可能会了解到,这个文本字符串(在我的世界中是一个值)是存储在硅片中的字节序列。

这种方法把我们的注意力放在任何计算机的物理世界上。有些教程采用这个方法。但我的方法不同。


在内部学习


我们从内部研究JavaScript世界。把自己的思想带入JavaScript世界,站在我旁边。我们将观察其规律并进行试验,就像物流学家在物理宇宙中所做的那样。

我们将了解JavaScript的本质,而不考虑它是如何实现的。这类似于物理学家如何在不回答物理世界是否真实的问题的情况下谈论恒星的性质。没关系!我们仍然可以用它自己的术语来描述它

我们的思维模型并不试图回答诸如“计算机内存中的值是如何表示的”这样的问题!事实上,即使程序正在运行,这个问题的答案也会改变。如果你听说过一个关于JavaScript“真正”表示内存中的数字、字符串或对象的简单解释,那很可能是错误的。

对于我来说,每个字符串都是一个值。不是“指针”或“内存地址”,而是一个值。在我的宇宙中,值是足够好的。不要让“内存单元”和其他低级的隐喻分散您对构建精确的JavaScript高级思维模型的注意力。那些低级隐喻都是乌龟!

如果你的隐喻来自一种较低级的语言,请把你关于“按引用传递”、“按堆栈分配”、“按写复制”等的直觉放在一边。这些关于计算机工作方式的模型常常使人们无法肯定JavaScript程序中可能或不可能发生的事情。我们将研究一些较低层次的细节,但仅限于其中真正重要的地方。它们可以作为我们思维模型的补充,而不是基础。

相反,我们思维模型的基础是我们的世界充满价值。每个值都属于几个内置类型之一。其中一些值是原始的,这使得这些类型的值是不可变的。

变量是从代码中的名称指向值的“连线”。我们还继续建立在这个而基础上。

至于这些奇怪的幻象,我不再为它们付出太多的思考。我有“连线”要指出,有问题要问,还有函数要调用。我最好去做!


当我看星星的时候,他们是明亮的。

我眨眼的时候,他们还在吗?

我耸耸肩。

“实现详细信息”


计算数值


伯爵是我童年时的榜样。如果你不熟悉芝麻街布偶,伯爵最喜欢的消遣就是数东西。今天,伯爵将与我们一起计算JavaScript世界中每个值。

你可能会想:计算价值有什么意义?我们不是在上算术课, 不是吗?计算数值的本质是区分事物。只有当你清楚地卡到“两个苹果”是两个不同的苹果时,你才能说“两个苹果”。区分不同的值是理解JavaScript中平等性的关键——这将是我们的下一个主题。

就像维吉尔引导但丁穿过地狱的九个圆圈一样,伯爵将陪伴我们穿过JavaScript的星球,来与不同的值见面:布尔值、数值、字符串等等,把它当做观光旅游。

JavaScript几种不同的值类型

Undefined


我们将以Undefined类型开始我们的巡演。伯爵很高兴地知道,这种类型只有一个值 —— undefined。

console.log(typeof(undefined)); // "undefined"

JavaScript中的undefined与黑洞


它被称为未定义,所以你可能认为它不存在——但是它是一个值,而且是一个非常真实的价值!就像一个黑洞,undefined是暴躁的,经常会带来麻烦。例如,从中读取属性将中断程序:

let person = undefined;
console.log(person.mood); // TypeError!

哦,好吧。幸运的是,在整个JavaScript世界中只有一个 Undefined。你可能会想:它为什么存在?在JavaScript中,它表示无意中丢失的值(意料之外的空值)。

你可以在自己的代码中使用它,方式是编写 undefined,就像编写2或“hello”。然而,undefined也通常“自然发生”。它出现在一些JavaScript不知道你想要什么值的情况下。例如,如果忘记指定变量,则它将指向 undefined:

let bandersnatch;
console.log(bandersnatch); // undefined

变量bandersnatch的值为undefined

然后你可以将变量指向另一个值,如果需要,也可以再次指向undefined

不要太在意它的名字。人们很容易将undefined视为变量的某种状态。例如“此变量尚未定义”。但这完全是一种误导性的思维方式!实际上,如果你读取了一个实际上没有定义的变量(或在let声明之前),您将得到一个错误:

console.log(jabberwocky); // ReferenceError!
let jabberwocky;

这与undefined无关。

实际上,undefined是一个常规的原始值,就像2或“hello”一样。

小心处理!

null是undefined的妹妹,访问null的属性,会抛出错误

您可以认为 ull 是 undefined 的妹妹。它的行为非常相似。例如当你您试图访问它的属性时,它也会引抛出错误:

let mimsy = null;
console.log(mimsy.mood); // TypeError!

mimsy为null,访问null的属性会抛出错误

与undefined类似,null是其资深类型的唯一值。然而,null也是一个骗子。由于JavaScript中的一个错误,它假装是一个对象:

console.log(typeof(null)); // "object" (a lie!)

你可能认为这意味着ull是一个对象。别掉进这个陷阱里!它是一个原始值,它的行为与对象完全不同。不幸的是,typeof(null) 是一个历史性的意外,我们将不得不永远忍受。

实际上,null用于故意丢失的值。为什么既有undefined也有null呢?这可以帮助你区分可能导致undefied的编码错误和有效的缺失数据(可能表示为空)。然而,这只是一个约定,JavaScript并没有强制执行这种用法。有些人尽量避免他们两个!

我不怪他们。

布尔值


表示ture和false的阴阳关系的太极图

和白天和晚上原因,只有两个布尔值:true和false

console.log(typeof(true)); // "boolean"
console.log(typeof(false)); // "boolean"

我们可以用它们执行逻辑操作:

let isSad = true;
let isHappy = !isSad; // The opposite
let isFeeling = isSad || isHappy; // Is at least one of them true?
let isConfusing = isSad && isHappy; // Are both true?

伯爵想检查一下你的思维模型。打开一个草图应用程序,或拿出一张纸。为上面的代码片段画出变量、值和它们之间的连线。

````````````````````````````````````````

                             下面的扰流器

````````````````````````````````````````

在写生完成之前,请不要继续往下滑动

请对照以下图片检查您的答案:

isSad和isFeeling为true,isHappy和isConfusing为false

首先,验证isHappy指向false,isFeeling指向true,isConfusing指向false。(如果你得到了不同的答案,那么在这一过程中总会有一个错误——一步一步地走过每一行)

接下来,验证草图上只有一个true和false值,伯爵坚持认为这很重要!不管记忆中的布尔值是如何存储的,在我们的心理模型中,只有两个布尔值。

数字

JavaScript中的数字

到目前为止,我们只计算了四个值:null、undefined、true和false。

等等,让我们为思维模式增加1850万、437万、736万亿、8704亿、454亿、812万和624个值。

当然,我们是在谈论数字:

console.log(typeof(28)); // "number"
console.log(typeof(3.14)); // "number"
console.log(typeof(-140)); // "number"

起初,数字似乎不起眼。让我们仔细看看吧!


计算机数学


JavaScript的数字行为和普通数学的数字不完全一样。下面是一段演示:

console.log(0.1 + 0.2 === 0.3); // false
console.log(0.1 + 0.2 === 0.30000000000000004); // true

这可能看起来令人惊讶!与一个流行的观点相反,这并不意味着JavaScript数字被破坏。这种结果在不同的编程语言中很常见。它甚至有一个名字:浮点数字。

你看,JavaScript没有实现我们在现实生活中使用的那种数字。浮点数字是“计算机数学”。不要太担心这个名字或者它是如何工作的。很少有人知道它的微妙之处,这就是重点!它在实践中非此有效,以至于大多数时候你都不会去想它。不过,让我们快速看看是什么让它与众不同。


颜色和数字


你有没有用过扫描仪吧一张实物照片或一份文件变成数码照片或文件?这个类比可以帮我们理解JavaScript数字。

扫描仪通常最多能分辨1600万种颜色。如果你用红色蜡笔画一张照片并扫描它,扫描的图像也应该是红色的。但它会有我们扫描仪从1600万种颜色中挑选出来的最接近的红色。

所以,如果你又两支颜色稍有不同的红色蜡笔,扫描仪可能会误以为它们的颜色完全相同!

我们可以说,扫描仪将颜色视为具有有限的精度。

浮点数字是类似的,在真正的数学中,数字是无穷多的。但在浮点数学中,只有18个五分之一。因此,当我们在代码中编写数字或使用它们进行计算时,JavaScript会选择它所知道的最接近的数字,就像扫描仪处理颜色一样。

换句话说,JavaScript将数字视为精度有限。

我们可以想像一个轴上的所有JavaScript数字。我们离0越近,数字的精度就越高,它们之间的距离也就越近:

JavaScript中轴上数字精度


当我们从9向任意方向移动时,我们开始失去精度。在某种程度上,即使是最接近的两个JavaScript数字,也比1相差得更远:

console.log(Number.MAX_SAFE_INTEGER);     // 9007199254740991
console.log(Number.MAX_SAFE_INTEGER + 1); // 9007199254740992
console.log(Number.MAX_SAFE_INTEGER + 2); // 9007199254740992
console.log(Number.MAX_SAFE_INTEGER + 3); // 9007199254740994
console.log(Number.MAX_SAFE_INTEGER + 4); // 9007199254740996
console.log(Number.MAX_SAFE_INTEGER + 5); // 9007199254740996

幸运的是,介于Number.MIN_SAFE_INTEGER和Number.MAX_SAFE_INTEGER之间的任何整数都是精确的。这就是为什么10+20=30。

但是当我们写0.1或0.2时,我们并不能得到精确的0.1和0.2。我们在JavaScript中得到了最接近的可用数字。它们几乎完全相同,但可能有一点不同。这些微小的差异加起来,这就是为什么0.1+0.2不能给出0.3完全相同的数字。

如果你仍然感到困惑,别担心。你可以学到更多关于浮点数学的知识,但是你已经比我开始写这本指南时知道的更多了!除非你从事金融应用,否则你可能不需要担心这个问题。


特殊数字


值得注意的是,浮点数字包含一些特殊数字,你可能偶尔会遇到NaN,无穷大,负无穷大,和-0。它们之所以存在,是因为有时您可能执行 1/0 之类的操作。而JavaScript需要以某种方式表示结果。浮点数标准指定它们的工作方式,以及使用它们时,发生的情况。

下面是您的代码中可能出现的特殊数字:

let scale = 0;
let a = 1 / scale; // Infinity
let b = 0 / scale; // NaN
let c = -a; // -Infinity
let d = 1 / c; // -0

在这些特殊的数字中,NaN特别有趣。NaN是0/0和其他一些无效数字的结果。代表“不是数字”。

你可能会对为什么NaN自称是数字感到困惑:

console.log(typeof(NaN)); // "number"

不过这里没有诀窍。从JavaScript的角度来看,NaN是一个数值。它不是空的、未定义的、字符串或其他类型。但是在浮点数中,这个词的名字“不是数字”。所以它是一个数值。它碰巧被称为“非数字”,因为它代表一个无效的结果。

使用这些特殊数字编写代码是很少见的。但是,它们可能是由于编码错误而出现的。所以很高兴只是它们的存在。


待续


这个模块分为两部分,我们已经到了第一部分的结尾。我们现在休息一下。让我们回顾一下到目前为止,我们已经计算了多少个值!

null,undefined,数字和true和false

· Undefined:只有一个值,undefined

· Null:只有一个值,null

· 布尔值:只有两个值:true 和 false

· 数值类型:每个浮点数对应一个值

我们还了解了一些关于JavaScript数字的有趣事实:

· 并非所有的数字都可以用JavaScript完美表示。它们的小数部分提供了更接近0的精度,而离它更远的精度更低。我们可以说他们的小数点是“浮动的”。

· 来自无效数学运算(如1/0或0/0)的数字是特殊的。NaN是这样一个数字。它们可能是由于编码错误出现的。

· typeof(NaN) 是一个数字,因为NaN是一个数值。它被称为“非数字”,因为它代表了“无效”数字的概念。

在第二部分里,我们将继续我们的观光旅游。研究大整数、字符串、对象和函数,并尝试将它们全部计算在内。

练习

本单元也有练习提供给您!

点击这里,我们通过一些简短的练习巩固这个思维模型。

https://eggheadio.typeform.com/to/C3Ajk4

别跳过他们

即使你可能熟悉不同类型的价值观,这些练习将帮助你巩固我们正在建立的心理模型。我们需要以此为基础,才能讨论更复杂的话题。

干杯!

Dan

原作者:Dan Abramov

原标题:[Just JavaScript] 04. Counting the Values (Part 1)

网站: https://overreacted.io

翻译:小宇

12123