在本模块中,我们将更深入地了解JavaScript世界和其中的值。但在我们开始之前,我们需要先正视这个问题,JavaScript世界是真的吗?
JavaScript模式
我住在JavaScript宇宙中的小行星上。
当我问JavaScript世界一个问题时,它用一个值来回答我。这所有的值当然不是我一个人提出来的。变量,导线,值——它们都居住在我的世界。我周围的JavaScript世界对我来说是绝对真实的,就像你生活的世界对你来说是真实的一样。
但有时,在下一行代码之前会有片刻的沉默。在下一个函数调用前的空闲时间,
1 | let reaction = 'yikes'; |
你希望它做什么?我们还没有讲到这个,所以如果你不确定的话没关系。试着用你目前的JavaScript知识来回答这个问题。
现在我想让你花点时间,一步一步地为这段代码的每一行写下你确切的思考过程。注意你现有的思维模型中的任何欠缺或不确定性,并把它们也写下来。如果你对此有任何疑问,尽可能清楚地将它表达出来。
在你写完之前你的思考前不要往下滚动。
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
答案是这样的。此代码将打印“yikes”或抛出错误,具体取决于你是否处于严格模式。它永远不会印“likes”。
原始值是不可改变的
你答对了吗?这可能看起来像一个很小的问题,就像大家在JavaScript面试中问的那种问题,但在实际编码中并没有太多出现。即使如此,它也说明了关于原始值的一个重要特点。
我不能改变原始值。
我将使用一个很小的例子来解释这点。字符串(原始值)和数组(非原始值,对象)有一些表面上的相似之处。数组是项的序列,字符串是字符的序列。
1 | let arr = [212, 8, 506]; |
你可以访问字符串的第一个字符就像访问数组项那样,几乎感觉字符串就是数组(但它们并不是!):
1 | console.log(arr[0]); // 212 |
你可以改变数组的第一项:
1 | arr[0] = 420; |
所以凭直觉来说,很容易假设可以对字符串执行相同的操作:
1 | str[0] = 'j'; // ??? |
但是你不能这样做。
这有一点很重要,我们需要添加到我们的思维模型。字符串是原始值,这是十分重要的。
所有的原始值都是不可改变的。“Immutable”是拉丁语中一种奇特的说法“unchangeable”。只读的,你根本不能改变原始值。
如果你尝试在一个原始值上设置属性,不管是字符串、数字还是其他什么,JavaScript不会允许你这样做。它是否会默默拒绝你的请求或报错取决于你的代码运行在哪个模式!
但我向你保证,这永远行不通:
1 | let fifty = 50; |
就像一些数组,50是一个原始值,你不能给它设置属性。
在我的JavaScript宇宙里面,所有的原始值存在于离我的代码很远的外圆中——就像遥远的恒星。这提醒我,即使我可以从代码中引用它们,也无法更改它们。他们保持原样。
我觉得这异常地令人安慰:
一个矛盾?
我刚刚演示了原始值是只读的,或者用我们这个时代的话说,是不可变的。这里有一个片段来测试你的思维模型:
1 | let pet = 'Narwhal'; |
像以前一样,用几句话写下你的思考过程。别急着往前走。一步一步地关注你对每一行的想法。字符串的不变性在这里起作用吗?它起什么作用?
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
如果你认为我是在捣乱你的大脑,那你完全是对的!答案是“The Kraken”——字符串的不变性不起作用。
如果你错了,不要气馁!最后这两个例子看起来肯定是相互矛盾的。
这是一个很重要的领悟!
当你对一门语言不熟悉时,你可能会倾向于忽略矛盾。毕竟,如果你追逐每一个矛盾,你会深陷入一个兔子洞深而学不到任何东西。
但既然你决定建立思维模型,你就需要质疑矛盾,它们展示出了思维模型的缺口。
变量是导线
再看看这些例子:
1 | let pet = 'Narwhal'; |
我们知道字符串不能改变因为它们是原始值。但是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 | javascript |
这并不奇怪。
但是请注意,我们传递给console.log的不是pet变量。我们可以通俗地说,但是我们不能真的把变量传递给函数。我们传递的是pet变量的当前值。这是怎么工作的呢?
原来,像pet这样的变量名也可以用作表达式!当我们编写pet时,我们在问JavaScript一个问题:“pet的当前价值是什么?”为了回答我们的问题,JavaScript跟随pet的“导线”,并在“导线”的末尾返回值。
所以同一个表达式可以在不同的时间给我们不同的值!
名词和动词
谁在乎你说的是“传递变量”还是“传递值”?深究这点差别不是过于卖弄学问吗?我当然不会打断你的同事来纠正他们,甚至是你自己。那将会浪费大家的时间。
但在你的头脑中,你需要清楚地知道你可以用每一个概念做什么。你不能骑自行车溜冰。你不能让鳄梨费气力啊。你不能像蚊子一样发嗖嗖声。而且你不能传递一个变量——至少在JavaScript中不能。
这里有一个小例子说明为什么这些细节很重要。
1 | function double(x) { |
如果我们认为double(money)传递了一个变量,那么我们可以预期x=x*2将使该变量加倍。但事情不是这样的。我们知道double(money)的意思是“计算出货币的价值,然后将其传递给double”。所以答案是10。真是个骗局!
你脑子里有哪些不同的JavaScript名词和动词?他们之间有什么关系?将你最常使用的列一个清单。
把它放在一起
现在让我们重温一下来自思维模型的第一个例子:
1 | let x = 10; |
我建议你拿一张纸或一个绘图应用程序,一步一步地画出x和y变量的“连线”的情况图。
第一行的作用不大:
- 声明一个x变量
- 为x变量生成导线
- 给x赋值10
- 让x的导线指向10
第二行很短,但它做了很多事情:
声明一个y变量
- 为y变量生成导线
把x的值赋给y
计算表达式:x
- 我们要回答的问题是“x”
- 跟随x的导线——答案是值10
x表达式结果是值10
因此,将10的值赋给y
把y的导线指向值10
最后,我们进入第三行:
- 将0的值赋给x
- 将x的导线指向值0
最后,x变量指向值0,y变量指向值10。注意y=x并不意味着将y指向“x”。我们不能把变量指向彼此!变量总是指向值。当我们看到一个赋值时,我们“询问”右边的值,并将左边的“线”指向它。
我在思维模型中提到,把变量看作盒子是相当常见的。我们要建造的宇宙根本就没有盒子。它只有导线!这看起来有点烦人。为什么我们不能“将0和10放入变量中,而是将变量指向它们?”
使用导线对于解释许多其他概念将是非常重要的,就像严格的等式,对象标识和变动。我们要坚持使用导线,所以你最好现在就开始习惯。
我的宇宙充满了导线。
总结
原始值是不可变的:在我们的代码中,我们无法做任何事情来影响它们或以任何方式更改它们。他们保持原样。例如,不能对字符串值设置属性,因为它是原始值。数组不是原始值,所以我们可以设置它们的属性。
变量不是值:每个变量都指向一个特定的值。我们可以使用=赋值运算符来更改它指向的值。
变量就像导线:“导线”并不是JavaScript的概念——但是它帮助我们理解变量如何指向值。还有一种不同的“电线”,不是变量,但我们还没有讨论过。
留意矛盾:如果你学到的两件事似乎互相矛盾,不要灰心。通常这是一个迹象,表明下面隐藏着更深层次的真相。
名词和动词很重要:我们正在建立一个思维模型,这样我们就能对宇宙中可能发生或不可能发生的事情充满信心。随便马虎的说话是可以的,但我们的思维需要精确。
练习
本单元也有练习题供你练习!
不要跳过它们!
即使你可能熟悉变量的概念,这些练习将帮助你巩固我们正在建立的思维模型。我们需要打好基础才能继续更复杂的话题。