《Just JavaScript》04. 计算数值

在本模块中,我们将更深入地了解JavaScript世界和其中的值。但在我们开始之前,我们需要先正视这个问题,JavaScript世界是真的吗?

JavaScript模式

我住在JavaScript宇宙中的小行星上。

当我问JavaScript世界一个问题时,它用一个值来回答我。这所有的值当然不是我一个人提出来的。变量,导线,值——它们都居住在我的世界。我周围的JavaScript世界对我来说是绝对真实的,就像你生活的世界对你来说是真实的一样。

但有时,在下一行代码之前会有片刻的沉默。在下一个函数调用前的空闲时间,

1
2
3
let reaction = 'yikes';
reaction[0] = 'l';
console.log(reaction);

你希望它做什么?我们还没有讲到这个,所以如果你不确定的话没关系。试着用你目前的JavaScript知识来回答这个问题。

现在我想让你花点时间,一步一步地为这段代码的每一行写下你确切的思考过程。注意你现有的思维模型中的任何欠缺或不确定性,并把它们也写下来。如果你对此有任何疑问,尽可能清楚地将它表达出来。

在你写完之前你的思考前不要往下滚动。

...

...

...

...

...

...

...

...

...

...

...

...

...

...

...

...

...

...

...

...

...

...

答案是这样的。此代码将打印“yikes”或抛出错误,具体取决于你是否处于严格模式。它永远不会印“likes”。

原始值是不可改变的

你答对了吗?这可能看起来像一个很小的问题,就像大家在JavaScript面试中问的那种问题,但在实际编码中并没有太多出现。即使如此,它也说明了关于原始值的一个重要特点。

我不能改变原始值。

我将使用一个很小的例子来解释这点。字符串(原始值)和数组(非原始值,对象)有一些表面上的相似之处。数组是项的序列,字符串是字符的序列。

1
2
let arr = [212, 8, 506];
let str = 'hello';

你可以访问字符串的第一个字符就像访问数组项那样,几乎感觉字符串就是数组(但它们并不是!):

1
2
console.log(arr[0]); // 212
console.log(str[0]); // "h"

你可以改变数组的第一项:

1
2
arr[0] = 420;
console.log(arr); // [420, 8, 506]

所以凭直觉来说,很容易假设可以对字符串执行相同的操作:

1
str[0] = 'j'; // ???

但是你不能这样做。

这有一点很重要,我们需要添加到我们的思维模型。字符串是原始值,这是十分重要的。

所有的原始值都是不可改变的。“Immutable”是拉丁语中一种奇特的说法“unchangeable”。只读的,你根本不能改变原始值。

如果你尝试在一个原始值上设置属性,不管是字符串、数字还是其他什么,JavaScript不会允许你这样做。它是否会默默拒绝你的请求或报错取决于你的代码运行在哪个模式

但我向你保证,这永远行不通:

1
2
let fifty = 50;
fifty.shades = 'gray'; // No!

就像一些数组,50是一个原始值,你不能给它设置属性。

在我的JavaScript宇宙里面,所有的原始值存在于离我的代码很远的外圆中——就像遥远的恒星。这提醒我,即使我可以从代码中引用它们,也无法更改它们。他们保持原样。

我觉得这异常地令人安慰:

一个矛盾?

我刚刚演示了原始值是只读的,或者用我们这个时代的话说,是不可变的。这里有一个片段来测试你的思维模型:

1
2
3
let pet = 'Narwhal';
pet = 'The Kraken';
console.log(pet); // ?

像以前一样,用几句话写下你的思考过程。别急着往前走。一步一步地关注你对每一行的想法。字符串的不变性在这里起作用吗?它起什么作用?



...

...

...

...

...

...

...

...

...

...

...

...

...

...

...

...

...

...

...

...

...

...

如果你认为我是在捣乱你的大脑,那你完全是对的!答案是“The Kraken”——字符串的不变性不起作用。

如果你错了,不要气馁!最后这两个例子看起来肯定是相互矛盾的。

这是一个很重要的领悟!

当你对一门语言不熟悉时,你可能会倾向于忽略矛盾。毕竟,如果你追逐每一个矛盾,你会深陷入一个兔子洞深而学不到任何东西。

但既然你决定建立思维模型,你就需要质疑矛盾,它们展示出了思维模型的缺口。

变量是导线

再看看这些例子:

1
2
3
let pet = 'Narwhal';
pet = 'The Kraken';
console.log(pet); // "The Kraken"

我们知道字符串不能改变因为它们是原始值。但是pet变量的确变成了“The Kraken”。发生了什么呢?

这似乎是一个矛盾,但事实并非如此。我们只说原始值不能改变。我们对于变量什么都没说!

当我们完善我们的思维模型时候,我们可能需要整理一下相关的概念。

变量不是值。

变量指向值。

在我的宇宙,一个变量代表一根导线。它有两端和方向:它从我代码中的一个名字开始,最后指向我宇宙中的某个值。

举个例子,我可以将pet变量指向“Narwhal”值:

1
let pet = 'Narwhal';



之后可以对变量执行两项操作:

给变量赋值

我可以做的一件事是给我的变量赋值:

1
pet = 'The Kraken';



我在这里所做的只是告知JavaScript将左侧的“wire”(我的pet变量)指向右侧的值(“The Kraken”)。它将一直指向那个值,除非我之后重新分配它。

注意,我不能在左边放任何东西:

1
'war' = 'peace'; // Nope.(Try it in the console.)

赋值的左侧必须是导线。目前,我们只知道变量是“导线”。但是还有另一种“导线”我们将在后面的模块中讨论。也许,你能猜出是什么?(提示:它包含方括号或点,我们已经见过几次了)。

还有另外一条规则。

赋值的右侧必须是表达式。它可以是简单的值,比如2或“hello”,也可以是更复杂的表达式:

1
pet = count + ' Dalmatians';

在这里,count+“Dalmatians”是一个表达式——对JavaScript来说是一个问题。JavaScript将用一个值(例如,“101 Dalmatians”)来回答这个问题。从现在起,pet“导线”将开始指向这个值。

如果右边必须是表达式,这是否意味着像2这样的数字或像用代码编写的’the Kraken’这样的字符串也是表达式?对!这样的表达式称为字面量——因为我们逐字记录它们的值。

读取变量的值

我还可以读取变量的值——例如,要记录它:

1
2
javascript
console.log(pet);

这并不奇怪。

但是请注意,我们传递给console.log的不是pet变量。我们可以通俗地说,但是我们不能真的把变量传递给函数。我们传递的是pet变量的当前值。这是怎么工作的呢?

原来,像pet这样的变量名也可以用作表达式!当我们编写pet时,我们在问JavaScript一个问题:“pet的当前价值是什么?”为了回答我们的问题,JavaScript跟随pet的“导线”,并在“导线”的末尾返回值。

所以同一个表达式可以在不同的时间给我们不同的值!

名词和动词

谁在乎你说的是“传递变量”还是“传递值”?深究这点差别不是过于卖弄学问吗?我当然不会打断你的同事来纠正他们,甚至是你自己。那将会浪费大家的时间。

但在你的头脑中,你需要清楚地知道你可以用每一个概念做什么。你不能骑自行车溜冰。你不能让鳄梨费气力啊。你不能像蚊子一样发嗖嗖声。而且你不能传递一个变量——至少在JavaScript中不能。

这里有一个小例子说明为什么这些细节很重要。

1
2
3
4
5
6
7
8
9
function double(x) {

x = x * 2;

}

let money = 10;
double(money);
console.log(money); // ?

如果我们认为double(money)传递了一个变量,那么我们可以预期x=x*2将使该变量加倍。但事情不是这样的。我们知道double(money)的意思是“计算出货币的价值,然后将其传递给double”。所以答案是10。真是个骗局!

你脑子里有哪些不同的JavaScript名词和动词?他们之间有什么关系?将你最常使用的列一个清单。

把它放在一起

现在让我们重温一下来自思维模型的第一个例子:

1
2
3
let x = 10;
let y = x;
x = 0;

我建议你拿一张纸或一个绘图应用程序,一步一步地画出x和y变量的“连线”的情况图。

第一行的作用不大:



第二行很短,但它做了很多事情:





最后,x变量指向值0,y变量指向值10。注意y=x并不意味着将y指向“x”。我们不能把变量指向彼此!变量总是指向值。当我们看到一个赋值时,我们“询问”右边的值,并将左边的“线”指向它。

我在思维模型中提到,把变量看作盒子是相当常见的。我们要建造的宇宙根本就没有盒子。它只有导线!这看起来有点烦人。为什么我们不能“将0和10放入变量中,而是将变量指向它们?”

使用导线对于解释许多其他概念将是非常重要的,就像严格的等式,对象标识和变动。我们要坚持使用导线,所以你最好现在就开始习惯。

我的宇宙充满了导线。

总结

练习

本单元也有练习题供你练习!

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

不要跳过它们!

即使你可能熟悉变量的概念,这些练习将帮助你巩固我们正在建立的思维模型。我们需要打好基础才能继续更复杂的话题。

上次更新 2020-06-03