我们将以下面的代码片段开始本节。

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

你期望的输出内容是什么?如果你不确定也正常,因为我们还没讲过这些知识。但是可以试着去用你当前所学的知识来回答。
现在,请你花点时间,逐步为该代码的每一行写下你的确切思考过程。注意记录使用当前思维模型中的任何差距或不确定性。如果对此有任何疑问,请尝试尽可能清楚地表述它们。
完成书写之前,请勿进一步滚动。








答案在这:根据你是否处于严格模式,此代码将打印“ yikes”或引发错误,它永远不会打印“likes”。
yikes

原始类型值是不可变的

你得到正确的答案了吗?这似乎是一个很细节的问题,面试官在JavaScript面试中会问这种问题,但在实际工程项目上并没有太多。即使这样,这也说明了有关原始类型值的重要的一点。
我们无法更改原始类型的值。 我将用一个小例子对此进行解释。字符串(原始类型)和数组(对象类型)有一些肤浅的相似之处。数组是项的序列,字符串是字符的序列:

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

你可以像访问字符串的第一个字符一样访问第一个数组项。几乎感觉像字符串是数组(但实际上并不是!):

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

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

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

从表面来看,很容易你可以想到对字符串执行相同操作:

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

但是你不能这样做。
这是我们需要添加到思维模型中的重要一点。字符串是原始类型。这意味着其独有的一些特性!
所有原始类型值都是不可变(Immutable)的。 “Immutable”是一种在拉丁语中“不变(unchangeable)”奇特的说法。它是只读的。你不能修改原始类型值。
如果你尝试在原始类型值上设置属性,无论是数字,字符串还是其他原始类型,JavaScript都不允许你这样做。是否以静默方式拒绝您的请求(不做任何提示,宽松模式)或错误取决于代码是否以严格模式运行。
但是请放心,下面的情况永远不起任何作用:

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

与其他数字一样,50是原始值,你无法在其上设置属性。
在我们的JavaScript世界中,所有原始类型值都位于离我们代码更远的外圈中,就像远处的星星一样。这提醒我们,即使我们可以从代码中引用它们,也无法更改它们,它们会保持原样。
我们是否觉得这种说法有些问题呢?
03.值和变量 - 图1

觉得有问题?

我们刚刚证明了原始类型值是只读的,或者,按我们的说法,是不可变的(immutable)。这是一个测试你的思维模型的代码片段。

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

和以前一样,用几句话写下你的思考过程。不要着急。逐步关注你对每行代码的思考,字符串的不变性在这里起作用吗?它起什么作用?
完成书写之前,请勿进一步滚动。









如果你以为我让你脑子混乱了,那你猜对了!答案是“The Kraken”——字符串的不变性不起作用了!
如果你弄错了它并且还没看出来,请不要失望!这两个例子看起来似乎彼此矛盾。
这是一个重要的认知。 如果你学习JavaScript的新手,可能会想着忽略这种矛盾。毕竟,如果你想清楚所有矛盾,你将陷入死胡同,无法学到任何东西。但是,既然你致力于建立一种思维模型,就需要质疑矛盾,从这些可以找到思维模型的差距。

变量就是线

让我们再次来看这个例子。

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

我们知道字符串值不能更改,因为它们是原始类型值。但是pet变量确实更改为“ The Kraken”。那是怎么回事?
这似乎是矛盾的,但事实并非如此。我们只是说原始类型的值是不变的,我们没有对变量说任何话!
变量不是值。
变量指向值
在我们的JavaScript世界中中,变量就是线。它有两个端点和一个方向:它从我们代码中的名称开始,并指向我们JavaScript世界中的某个值。
例如,我可以将pet变量指向“ Narwhal”值:

  1. let pet = 'Narwhal';

03.值和变量 - 图2
之后,我们可以对变量做两件事。

给变量赋值

我们可以做的一件事是为变量分配其他值:

  1. pet = 'The Kraken';

03.值和变量 - 图3
我们在这里所做的只是命令JavaScript将左侧的“线”(“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. console.log(pet);

这不足为奇。
但是请注意,传递给console.log的不是pet变量。我们可能通常这么认为,但是我们不能真正将变量传递给函数,我们其实是传递pet变量的值。这是如何运作的?
事实证明,像pet这样的变量名也可以当做作表达式!编写pet时,我们会问JavaScript一个问题:“ pet的当前值是多少?”为了回答我们的问题,JavaScript遵循了“pet”的“线”,并在该“线”末尾给了我们值。
因此,同一表达式可以在不同时间为我们提供不同的值!

名词和动词

谁在乎你说的是“传递变量”还是“传递值”?这种差异或矛盾难道是是无可救药的吗?我们当然不鼓励打扰你的同事来纠正他们——甚至是你自己,那会浪费每个人的时间。
但是在你的脑海中,你需要弄清楚每个概念可以做什么。你不能把自行车用来滑。你不能像放风筝那样放飞牛油果,你不会像蚊子那样嗡嗡叫,而且你无法传递变量-至少在JavaScript中是这样的。
下面通过一个小例子解释为何这么重要:

  1. function double(x) {
  2. x = x * 2;
  3. }
  4. let money = 10;
  5. double(money);
  6. console.log(money); // ?

如果我们认为double(money)正在传递变量,我们可以预料到x = x * 2将使该变量增加一倍。但它不是这样工作的,我们知道double(money)的意思是“得到money的值,然后将该值传递给double”,答案是10。这很容易让人矛盾!
关于JavaScript名词和动词你脑中想的有什么不同?它们如何相互联系?写下最常用的简短列表。

放在一起思考

现在,让我们回顾一下思维模型中的第一个示例:

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

建议你拿一张纸或一个绘图应用,一步一步地画出x和y变量的“线”发生什么的示意图。
第一行无关紧要:
assign-step1.gif

  • 声明一个名为x的变量。
    • 为x变量制作“线”。
  • 给x赋值10。
    • 将x的连线指向值10。

第二行很短,但是它做了很多事情:
assign-step2.gif

  • 声明一个名为y的变量。
    • 为y变量制作“线”。
  • 向y分配x的值。
    • 计算表达式:x。
      • 我们要回答的“问题”是x。
      • 跟随x的连线——答案是10。
    • x表达式的值为10。
    • 因此,将y的值指定为10。
    • 将y的连线指向值10。

最后,我们进入第三行:
assign-step3.gif

  • 给x赋值0。
    • 将x的连线指向值0。

最后,变量x指向值0,变量y指向值10。请注意,y = x并不意味着y指向x”。我们不能互相指向变量!变量始终指向的是“值”。看到分配时,我们“询问”右侧的值,并左侧的“线”指向该值。
我们在自己的思维模型中提到将变量视为盒子是相当普遍的。我们正在建立的JavaScript世界根本没有任何盒子,它只有“线”!这似乎有点烦人。为什么我们不能“将0和10的值放入变量中,而是将变量指向它们”?
使用“线”对于解释许多其他概念(例如严格的相等性,对象标识和改变)将非常重要。我们将坚持使用“线”,因此你不妨现在就开始习惯它们!

总结

  • 原始类型值是不可变的。我们无法在代码中做任何改变或改变它们的事情。他们会一直保持原样。例如,我们无法在字符串值上设置属性,因为它是原始类型值。数组不是原始类型,因此我们可以设置它们的属性。
  • 变量不是值。每个变量都指向一个特定的值。我们可以使用=赋值运算符更改其指向的值。
  • 变量就像“线”。 “连线”不是JavaScript的概念,而是帮助我们想象变量如何指向值。还有另一种“线”不是变量,但我们尚未讨论。
  • 注意矛盾的存在。如果你学到的两件事似乎相互矛盾,不要灰心。通常,这表明下面隐藏着一个更深的真相。
  • 名词和动词很重要。我们正在建立一种思维模型,以便对JavaScript世界中可能发生或不会发生的事情充满信心。随便的演讲草率一点也可以,但是我们的思维必须准确。

    练习

    本节还包含练习供你练习!
    单击此处,通过一些简短的练习来巩固这种思维模型。
    不要跳过练习!
    即使你可能熟悉变量的概念,这些练习也可以帮助你巩固我们正在构建的思维模型。在进入更复杂的主题之前,我们需要这个基础。