我们将以下面的代码片段开始本节。
let reaction = 'yikes';
reaction[0] = 'l';
console.log(reaction);
你期望的输出内容是什么?如果你不确定也正常,因为我们还没讲过这些知识。但是可以试着去用你当前所学的知识来回答。
现在,请你花点时间,逐步为该代码的每一行写下你的确切思考过程。注意记录使用当前思维模型中的任何差距或不确定性。如果对此有任何疑问,请尝试尽可能清楚地表述它们。
完成书写之前,请勿进一步滚动。
…
…
…
…
…
…
…
…
…
答案在这:根据你是否处于严格模式,此代码将打印“ yikes”或引发错误,它永远不会打印“likes”。
yikes
原始类型值是不可变的
你得到正确的答案了吗?这似乎是一个很细节的问题,面试官在JavaScript面试中会问这种问题,但在实际工程项目上并没有太多。即使这样,这也说明了有关原始类型值的重要的一点。
我们无法更改原始类型的值。 我将用一个小例子对此进行解释。字符串(原始类型)和数组(对象类型)有一些肤浅的相似之处。数组是项的序列,字符串是字符的序列:
let arr = [212, 8, 506];
let str = 'hello';
你可以像访问字符串的第一个字符一样访问第一个数组项。几乎感觉像字符串是数组(但实际上并不是!):
console.log(arr[0]); // 212
console.log(str[0]); // "h"
你可以更改数组的第一项:
arr[0] = 420;
console.log(arr); // [420, 8, 506]
从表面来看,很容易你可以想到对字符串执行相同操作:
str[0] = 'j'; // ???
但是你不能这样做。
这是我们需要添加到思维模型中的重要一点。字符串是原始类型。这意味着其独有的一些特性!
所有原始类型值都是不可变(Immutable)的。 “Immutable”是一种在拉丁语中“不变(unchangeable)”奇特的说法。它是只读的。你不能修改原始类型值。
如果你尝试在原始类型值上设置属性,无论是数字,字符串还是其他原始类型,JavaScript都不允许你这样做。是否以静默方式拒绝您的请求(不做任何提示,宽松模式)或错误取决于代码是否以严格模式运行。
但是请放心,下面的情况永远不起任何作用:
let fifty = 50;
fifty.shades = 'gray'; // No!
与其他数字一样,50是原始值,你无法在其上设置属性。
在我们的JavaScript世界中,所有原始类型值都位于离我们代码更远的外圈中,就像远处的星星一样。这提醒我们,即使我们可以从代码中引用它们,也无法更改它们,它们会保持原样。
我们是否觉得这种说法有些问题呢?
觉得有问题?
我们刚刚证明了原始类型值是只读的,或者,按我们的说法,是不可变的(immutable)。这是一个测试你的思维模型的代码片段。
let pet = 'Narwhal';
pet = 'The Kraken';
console.log(pet); // ?
和以前一样,用几句话写下你的思考过程。不要着急。逐步关注你对每行代码的思考,字符串的不变性在这里起作用吗?它起什么作用?
完成书写之前,请勿进一步滚动。
…
…
…
…
…
…
…
…
…
如果你以为我让你脑子混乱了,那你猜对了!答案是“The Kraken”——字符串的不变性不起作用了!
如果你弄错了它并且还没看出来,请不要失望!这两个例子看起来似乎彼此矛盾。
这是一个重要的认知。 如果你学习JavaScript的新手,可能会想着忽略这种矛盾。毕竟,如果你想清楚所有矛盾,你将陷入死胡同,无法学到任何东西。但是,既然你致力于建立一种思维模型,就需要质疑矛盾,从这些可以找到思维模型的差距。
变量就是线
让我们再次来看这个例子。
let pet = 'Narwhal';
pet = 'The Kraken';
console.log(pet); // "The Kraken"
我们知道字符串值不能更改,因为它们是原始类型值。但是pet变量确实更改为“ The Kraken”。那是怎么回事?
这似乎是矛盾的,但事实并非如此。我们只是说原始类型的值是不变的,我们没有对变量说任何话!
变量不是值。
变量指向值。
在我们的JavaScript世界中中,变量就是线。它有两个端点和一个方向:它从我们代码中的名称开始,并指向我们JavaScript世界中的某个值。
例如,我可以将pet变量指向“ Narwhal”值:
let pet = 'Narwhal';
给变量赋值
我们可以做的一件事是为变量分配其他值:
pet = 'The Kraken';
我们在这里所做的只是命令JavaScript将左侧的“线”(“pet”变量)指向右侧的值(“The Kraken”),除非我们稍后再次分配它,否则它将一直指向该值。
请注意,我不能把值放在左侧:
'war' = 'peace'; // Nope. (Try it in the console.)
左侧必须是“线”。目前,我们只知道变量是“线”。但是,我们将在以后的章节中讨论另一种“线”。也许,您能猜出它是什么? (提示:它涉及方括号或圆点,我们已经看过几次了。)
还有另一条规则。
右侧必须是表达式。它可以是简单的值,例如2或“ hello”,也可以是更复杂的表达式-例如:
pet = count + ' Dalmatians';
在这里,count +'Dalmatians'
是一个表达式——给JavaScript提的的问题。 JavaScript会用一个值(例如“ 101 Dalmatians”)回答它。从现在开始,pet“线”将开始指向该值。
如果右边必须是表达式,这是否意味着用代码编写的数字(如2)或字符串(如“ The Kraken”)也是表达式?是!这样的表达式称为字面量——因为我们使用字面量的方式写下它们的值。
读取变量的值
我们可以读取变量的值——例如,将其下打印出来:
console.log(pet);
这不足为奇。
但是请注意,传递给console.log的不是pet变量。我们可能通常这么认为,但是我们不能真正将变量传递给函数,我们其实是传递pet变量的值。这是如何运作的?
事实证明,像pet这样的变量名也可以当做作表达式!编写pet时,我们会问JavaScript一个问题:“ pet的当前值是多少?”为了回答我们的问题,JavaScript遵循了“pet”的“线”,并在该“线”末尾给了我们值。
因此,同一表达式可以在不同时间为我们提供不同的值!
名词和动词
谁在乎你说的是“传递变量”还是“传递值”?这种差异或矛盾难道是是无可救药的吗?我们当然不鼓励打扰你的同事来纠正他们——甚至是你自己,那会浪费每个人的时间。
但是在你的脑海中,你需要弄清楚每个概念可以做什么。你不能把自行车用来滑。你不能像放风筝那样放飞牛油果,你不会像蚊子那样嗡嗡叫,而且你无法传递变量-至少在JavaScript中是这样的。
下面通过一个小例子解释为何这么重要:
function double(x) {
x = x * 2;
}
let money = 10;
double(money);
console.log(money); // ?
如果我们认为double(money)
正在传递变量,我们可以预料到x = x * 2
将使该变量增加一倍。但它不是这样工作的,我们知道double(money)
的意思是“得到money的值,然后将该值传递给double”,答案是10。这很容易让人矛盾!
关于JavaScript名词和动词你脑中想的有什么不同?它们如何相互联系?写下最常用的简短列表。
放在一起思考
现在,让我们回顾一下思维模型中的第一个示例:
let x = 10;
let y = x;
x = 0;
建议你拿一张纸或一个绘图应用,一步一步地画出x和y变量的“线”发生什么的示意图。
第一行无关紧要:
- 声明一个名为x的变量。
- 为x变量制作“线”。
- 给x赋值10。
- 将x的连线指向值10。
第二行很短,但是它做了很多事情:
- 声明一个名为y的变量。
- 为y变量制作“线”。
- 向y分配x的值。
- 计算表达式:x。
- 我们要回答的“问题”是x。
- 跟随x的连线——答案是10。
- x表达式的值为10。
- 因此,将y的值指定为10。
- 将y的连线指向值10。
- 计算表达式:x。
最后,我们进入第三行:
- 给x赋值0。
- 将x的连线指向值0。
最后,变量x指向值0,变量y指向值10。请注意,y = x
并不意味着y指向x”。我们不能互相指向变量!变量始终指向的是“值”。看到分配时,我们“询问”右侧的值,并左侧的“线”指向该值。
我们在自己的思维模型中提到将变量视为盒子是相当普遍的。我们正在建立的JavaScript世界根本没有任何盒子,它只有“线”!这似乎有点烦人。为什么我们不能“将0和10的值放入变量中,而是将变量指向它们”?
使用“线”对于解释许多其他概念(例如严格的相等性,对象标识和改变)将非常重要。我们将坚持使用“线”,因此你不妨现在就开始习惯它们!
总结
- 原始类型值是不可变的。我们无法在代码中做任何改变或改变它们的事情。他们会一直保持原样。例如,我们无法在字符串值上设置属性,因为它是原始类型值。数组不是原始类型,因此我们可以设置它们的属性。
- 变量不是值。每个变量都指向一个特定的值。我们可以使用=赋值运算符更改其指向的值。
- 变量就像“线”。 “连线”不是JavaScript的概念,而是帮助我们想象变量如何指向值。还有另一种“线”不是变量,但我们尚未讨论。
- 注意矛盾的存在。如果你学到的两件事似乎相互矛盾,不要灰心。通常,这表明下面隐藏着一个更深的真相。
- 名词和动词很重要。我们正在建立一种思维模型,以便对JavaScript世界中可能发生或不会发生的事情充满信心。随便的演讲草率一点也可以,但是我们的思维必须准确。
练习
本节还包含练习供你练习!
单击此处,通过一些简短的练习来巩固这种思维模型。
不要跳过练习!
即使你可能熟悉变量的概念,这些练习也可以帮助你巩固我们正在构建的思维模型。在进入更复杂的主题之前,我们需要这个基础。