在本节中,我们将深入研究JavaScript世界及其中的值。但是,在此之前,我们需要解决一个不容忽视、大家却又避而不谈的问题。 JavaScript世界是真实的吗?
JavaScript模拟器
我们生活在JavaScript世界中的小行星上。
当我们向JavaScript世界提问时,它会通过值给我们答案。我们当然不会自己记住所有这些值,变量,线,值-它们都构成了我们的世界。我们周围的JavaScript世界对我们来说绝对是真实的-正如我们所生活的世界对我们而言是真实的。
但是有时候,在下一行代码执行之前会有片刻的寂静,下一个函数调用之前的间隔时间吗,有一些神迷的事情发生。在那一刻,我看到了比我们世界更大的世界。
在我看来,这个世界上没有变量和价值。没有表达式,没有文字。相反,有夸克,原子,电子,水和生命,也许,你对这个世界更加熟悉?
在那里,被称为“人类”的生命体使用称为“计算机”的特殊机器来模拟我们的JavaScript世界。他们中有些人是出于娱乐目的,有些人是为了牟利,有些人根本没有理由这样做,他们只是一时兴起,我们的JavaScript世界每天诞生并死亡一万亿次。
毕竟,也许我们的JavaScript世界不是那么真实。
这意味着有两种方法去学习它。
从外至内的学习
学习我们JavaScript世界的一种方法是从外部开始进行。
也许,你可能会专注于“实实在在”地模拟JavaScript世界(一个JavaScript引擎)的工作方式。例如,你可能会了解到,一个文本字符串(JavaScript世界中的一个值)是存储在存储芯片中的字节序列。
这种方法使我们将精力集中在人和计算机的物理世界上。一些教程采用这种方法。但是我们将使用不同的方法学习。
从内到外的学习
我们将从内部研究JavaScript世界,从思维上将自己带入JavaScript世界,我们将观察其定律并进行实验,就像物理学家在物理宇宙中所做的那样。
我们将了解JavaScript世界的本质,而无需考虑其实现方式。这类似于物理学家如何谈论恒星的性质而无需回答物理世界是否真实的问题,这样也没关系!我们仍然可以用自己的术语来描述它。
我们的思维模式不会尝试回答诸如“内存在计算机内存中如何表示?”之类的问题,因为答案一直在变化!实际上,即使你的程序正在运行,该问题的答案也会改变。如果你听到有关JavaScript如何“真正”表示内存中的数字,字符串或对象的简单说明,则很可能是错误的。
对我们来说,每个字符串都是一个值,不是“指针”或“内存地址”,而是一个值。在我们的JavaScript世界中中,有“值”就足够了,请勿使用“内存单元”和其他低级隐喻来分散你对JavaScript的准确高级思维模型的注意力,反正其他都可忽略!
如果你使用的是较低级的语言,请不要使用“按引用传递”,“按堆栈分配”,“写入时复制”等直觉思维。这些关于计算机工作方式的模型通常使人们更难以确定JavaScript程序中可能发生或不会发生的事情。我们仅在真正重要的地方,研究一些较低级别的细节。它们可以作为我们思维模型的补充,而不是其基础。
相反,我们思维模型的基础是我们的世界充满“值”。每个值是属于几种内置类型之一,其中一些是原始的,这些类型的值不可变。变量是从代码中的名称指向值的“线”,我们将继续在此基础上继续发展。
对于一些奇怪的现象,我们不需要对它们抱有太多的想法,我们要关注的问题是要问的问题和要调用的函数,我最好这样去做!
当我们看着它们时,星星是亮的。
我们眨眼时他们还在吗?
我们耸了耸肩。
“具体表现如下”:
数数”值”
Count von Count(歌曲)是我童年的最喜欢的,如果你不熟悉芝麻街,那么他最喜欢的消遣就是数数。今天,Count von Count将与我们一起对JavaScript世界中的每个值进行计算。
你可能会想:计算值有什么意义?我们不在算术课上,对吗?计算值的本质是将事物彼此区分开。当你清楚地看到它们是两个不同的苹果时,你只能说有“两个苹果”。相互区分值是理解JavaScript相等性的关键-这将是我们的下一个主题。
就像维吉尔(Virgil)指导但丁(Dante)穿越地狱的9个循环一样,Count von Count将陪伴我们穿越JavaScript的“天体”,以满足不同的值:布尔值,数字,字符串等,考虑一下在观光旅游的时候观赏他们。
Undefined
我们将从“Undefined”类型开始观赏,Count von Count很高兴得知该类型只有一个值-Undefined。
console.log(typeof(undefined)); // "undefined"
它被称为未定义,因此你可能会认为它不存在-但它是一种值,而且是真实存在的值!就像黑洞一样,脾气暴躁而不确定,经常会带来麻烦。例如,从属性读取属性将导致你的程序出错:
let person = undefined;
console.log(person.mood); // TypeError!
那好吧。幸运的是,整个JavaScript世界中只有一个未定义。你可能会想:为什么它是未定义但是实际存在?在JavaScript世界中,它表示无意中缺少值的概念。
你可以在自己代码的代码中像使用值2或“ hello”一样使用undefined。但是,undefined通常也“自然而然的存在”。在某些情况下,当JavaScript不知道你想要什么值时,它就会显示出来。例如,如果忘记分配变量,它将指向undefined:
let bandersnatch;
console.log(bandersnatch); // undefined
然后,你可以将其指向另一个值,或者根据需要再次指向未定义。
不要太在意它的名字,将undefined视为某种可变状态,例如“此变量尚未定义”。
但这是一种完全误导的思考方式!实际上,如果你读取一个实际上未定义的变量(或在let声明之前),则会收到错误消息:
console.log(jabberwocky); // ReferenceError!
let jabberwocky;
这与undefined无关。
实际上,undefined是常规原始值,例如2或“ hello”。
小心处理它。
Null
你可以将null视为undefined的姐妹。它的行为非常相似。例如,当你尝试访问其属性时,它也会报错:
let mimsy = null;
console.log(mimsy.mood); // TypeError!
与undefined相似,null是其自身类型的唯一值。但是,null也是骗子,由于JavaScript中的某些错误,它被伪装成一个对象:
console.log(typeof(null)); // "object" (a lie!)
你可能认为这意味着null是一个对象,不要陷入这个陷阱!这是一个原始值,它的行为方式与对象不同吗,不幸的是,typeof(null)是历史性的事故,我们必须永远接受它。
实际上,null用于故意缺失的值,为什么同时存在null和undefined呢?
其实,这只是一个约定俗成的事,JavaScript不会强制使用此用法,但尽可能避免两者的出现!
我不怪他们。
Booleans
像白天和黑夜一样,只有两个布尔值: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?
Count von Count现在想检查你的思维模型,打开画图程序或一张纸,为上面的代码片段绘制出变量,将值和它们用线连起来。
完成书写之前,请勿往下滚动。
…
…
…
…
…
…
…
…
…
对照下图检查你的答案:
首先,验证isHappy指向false,isFeeling指向true,isConfusing指向false。 (如果你得到不同的答案,则一路走来会出现错误-逐步遍历每一行。)
接下来,验证草图上只有一个true和一个false值。 Count von Count坚持认为这很重要!不管布尔值如何存储在内存中,在我们的思维模型中,只有两个。
Numbers
到目前为止,我们准确地计算了四个值:null,undefined,true和false
。
稍等,我们将新增,18亿,437万亿,763万亿,8740亿,4亿5400万,82万1000,800,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万种颜色中选出的最接近红色的颜色。因此,如果你有两支红色蜡笔的颜色略有不同,扫描仪可能会被骗到以为它们的颜色完全一样!所以,如果你有两支红色蜡笔的颜色略有不同,扫描仪可能会以为它们的颜色完全一样。
从上面的例子,我们了解到,扫描仪对颜色的处理精度是有限的。
浮点数也是类似的。在基础数学中,有一个无限的数字集合。但是,在浮点数中是有限的(会因为机器位数限制浮点数的个数)。因此,当我们在代码中写数字或用它们进行计算时,JavaScript会选择它知道的最接近的数字—就像我们的扫描器处理颜色一样。
换句话说,JavaScript将数字视为具有有限精度的数字。
我们可以想象一个轴上的所有JavaScript数字。我们越接近0,数字的精度就越高,它们彼此之间的“坐”得越近。
译者注: 可以简单理解为:整数部分越小,那么小数部分能表示的就越多,数量自然越多,所以0-1的数量是最多的。 下图表示了有限的浮点数的分布。
当我们从0朝任一方向移动时,我们开始失去精度。在某些时候,即使两个最接近的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完全相同的数字。
如果这还是让你感到困惑,别担心,你可以学习更多关于浮点数学的知识,但你肯定在我开始写这本指南的之前知道的更多了! 除非你是在金融APP上工作,否则你很可能不需要担心这个问题。
特殊数字
值得注意的是,浮点数包括一些特殊的数字。你可能偶尔会遇到NaN、Infinity、-Infinity和-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, string或其他一些类型。但在浮点数中,这个词的叫做 “不是一个数字”,所以,它是一个数字类型,但它恰好被称为 “不是一个数字”,因为它代表的是一个无效的计算结果。
使用这些特殊数字编写代码并不常见。但是,它们可能会因为编码错误而出现。所以知道它们的存在是件好事。
未完待续
本节分为两部分。我们已经到了第1部分的结尾。现在我们休息一会儿,让我们回顾一下到目前为止我们已经数了多少个值类型!
- Undefined: 只有一个值
undefined
. - Null:只有一个值 ,
null
. - Booleans: 两个值:
true
andfalse
. - Numbers: 每个浮点数表示一个数字值
我们还了解了一些有关JavaScript数字的有趣事实:
- 不是所有的数字都可以在JavaScript中完美地表示。它们的小数部分在靠近0的地方提供了更高的精度,而在离0更远的地方精度更小。我们可以说,它们的小数点是 “浮动 “的。
- 像1/0或0/0这样的无效数学运算产生的数是特殊的。NaN就是这样的数字之一。它们的出现可能是由于编码错误造成的。
- typeof(NaN)是一个数字,因为NaN是一个数字类型。之所以叫 “不是一个数”,是因为它代表的是 “无效 “数的思想。
练习题
本节还包含题目供你练习!
单击此处,通过一些简短的练习来巩固这种思维模式。
不要跳过它们!
即使你可能很熟悉不同类型的值,这些练习也将帮助你巩固我们正在构建的思维模型。在进入更复杂的主题之前,我们需要这个基础。