【# 01】Just Javascript 笔记
文章目录
前言 && 心智模型 Mental Model
心智模型
心智模型/心智模式的理论是基于一个试图对某事做出合理解释的个人会发展可行的方法的假设,在有限的领域知识和有限的信息处理能力上,产生合理的解释。心智模型是对思维的高级建构,心智模型表征了主观的知识。通过不同的理解解释了心智模型的概念、特性、功用。心智模型是个体为了要了解和解释他们的经验,所建构的知识结构,该模型受限于个体关于他们经验的内隐理论(Implicit Theories),这可能有很多或很少的正确性。
简单的说就是对于事物基于自己心智的理解。
作者举了一个例子。
如下的一段代码:
|
|
在看到这段代码是,注意自己脑中发生了什么,比如会产生这样的独白:
let a = 10
- 声明一个变量为
a
,把它设为10
- 声明一个变量为
let b = a
- 声明一个变量
b
,把它设为a
a
是多少来着?是10
,所以b
也就是10
- 声明一个变量
- a = 0
- 把
a
设置0
- 把
- 所以现在
a
为0
,b
为10
。这就是我们的答案
也许我们的独白会不太一样:比如会说 赋予
而不是 设为
。或者可能逻辑顺序也不一样,也可能答案都不一样。
对每个基本的编程概念(比如变量)和基于此的操作(比如赋值)都有一套深根于你脑子中的类比。有一些或许来自于现实,有一些或许演化于你之前学过的其他领域,比如数学中的数字。这些类比可能会重叠并相互矛盾,但它们还是可以帮你理解代码中发生了什么。
这些直觉(比如变量的盒子化)影响了我们这一生阅读代码的方式。但有时,我们的心智模型是错误的。
一个好的心智模型会帮你更快地找出并修复 bug,更好地理解他人的代码,并且更自信于你的代码。
快编慢码(Coding, Fast and Slow)
一旦可以,我们就会依赖于「快」系统。我们这套系统和很多动物一样,它给了我们像一直走路而不摔倒这种神奇的能力。这种「快」系统善于模式匹配(对于生存来说很有必要)和「本能反应」。但不善于筹划。
独特的是,由于额叶的发展,人类也拥有「慢」思维系统。 这种「慢」系统负责复杂的逐步推理。它使我们能够规划未来的事件、进行争论、遵循数学证明。
由于使用「慢」系统非常费力,因此即使在处理诸如编程之类的智力任务时,我们也倾向于默认使用「快」系统。
|
|
比如这么一段代码,我们可能会发现这个函数用于复制电子表格,如果是未保存的原始电子表格,会报错。
我们可能不会注意到这个函数会不慎修改原电子表格的标题。
在「快」模式下,我们基于命名、注释、整体结构来猜想一段代码的用途。在「慢」模式下,我们一步步地追溯代码的运作方式。
JavaScript 宇宙
代码与值(Code and Values)
原始值(Primitive Values)
原始值(Primitive Values) 就是数字(number)或者字符串(string)等等。所有原始值都有一些共同点。我的代码无法影响他们(指原始值是 immutable)。在宇宙星球中坐着比喻为星星一样,无情二遥远,但是当我们需要时,它们总是会在那里。
对象和函数(Objects and Functions)
对象(object)和函数(function)也是值,但它们并不原始。当我们在控制台打印如下代码时:
|
|
显示它们的方式与原始值不同。某些浏览器会在他们之前显示一个箭头,或者在单击它们的时候展现地很特别。与原始值不同的是对象和函数就像是漂浮在我们代码附件的石块(原始值像是星星)。它们更加近 ,我们可以操纵它们。
表达式(Expressions)
我们可以使用表达式来提问,JavaScript 将用值来回答,比如 2+2
将被用 4 来回答。(不知道这里提这个是什么含义?)
值的类型(Types of Values)
可以使用 typeof
来检查一个值的类型。
原始值(Primitive Values)
- 未定义(Undefined) (
undefined
),用于无意中漏掉的值。 - 空值(Null) (
null
),用于有意漏掉的值。 - 布尔值(Booleans) (
true
和false
),用于逻辑操作符。 - 数字(Numbers) (
-100
、3.14
之类的),用于数学计算。 - 字符串(Strings) (
"hello"
、"abracadabra"
之类的),用于文本。 - 符号(Symbols) (不常用),用于隐藏实现细节。
- 大型整数(BigInts) (不常用,是新的),用于数学中的大数字。
对象和函数(Objects and Functions)
- 对象(Objects) (
{}
之类的),用于将相关的数据和代码分组。 - 函数(Functions) (
x => x * 2
之类的),用于引用代码。
没有别的类型了。除了列举过的类型外,没有别的基础类型了。所有剩下的都是对象(object)。比如数组、日期、正则表达式,都是 JavaScript 中的对象:
|
|
值和变量 Values and Variables
是看一个小例子,看看如下代码输出的结果是什么?
|
|
这段代码打印 yikes
。如果使用 strict mode 的话,会报错。
原始值是不可变的(Primitive Values Are Immutable)
我们无法改变原始值。
我会用一个小例子解释这句话。字符串(是原始值)和数组(不是原始值,是对象)在表面上有一些相似之处。一个数组是一串项目(item),一个字符串是一串字符(character)。
|
|
所有的原始值都是不可变的,同样无法在原始值上设置属性。
|
|
50
是作为数字的原始值,你不能它在上面设置属性。
矛盾之处?(A Contradiction?)
|
|
一段看似和上文矛盾的代码。
变量是电线(Variables Are Wires)
再回到上面的例子,pet 输出的是新的值 The Kraken
。这是咋回事呢?
变量不是值,变量指向值。
在我的宇宙中,一个变量是一根电线。它有两个端点,并且有一条方向:从我代码中的一个命名出发,最终指向我宇宙中的某个值。
比如把变量 pet
指向Narwhal
给变量赋值(Assigning a Value to a Variable)
我在这里所做的只是告诉 JavaScript 把左侧的「电线」(即变量
pet
)指向右侧的值(即"The Kraken"
)。除非我之后再重新赋值,否则它将一直指向该值。
一个赋值语句的左侧是电线,右侧是表达式。像 2
的数字或者像 "The Kraken"
的字符串也是表达式呢。这种表达式被称作字面量(literals)——因为我们字面地写出了它们的值。
读取变量的值(Reading a Value of a Variable)
|
|
当我们写下
pet
时,我们是向 JavaScript 问了这么个问题:「pet
的当前值是多少?」为了回答这个问题,JavaScript 沿着pet
的「电线」,找到末端的值后反馈给我们。
名词和动词(Nouns and Verbs)
|
|
如果我们认为
double(money)
传递的是一个变量,我们可以料想到的是x = x \* 2
会使这个变量翻倍。但事实并非如此。我们知道double(money)
的意思是「算出money
的*值*,然后向double
*传递这个值*」。所以答案是10
。
结合起来说(Putting It Together)
|
|
一个图片的动态示例。
小测试
-
仅通过编辑函数
feed
是否可以改变输出,为什么?不可以1 2 3
let pets = 'Tom and Jerry'; feed(pets); console.log(pets[0]);
-
仅通过编辑函数
feed
来改变输出嘛?为什么?可以1 2 3
let pets = ['Tom', 'Jerry']; feed(pets); console.log(pets[0]);
Counting the Values
未定义(Undefined)
undefined
就是个普通的原始值,跟 2
或者 "hello"
是一样的。
空值(Null)
可以把 null
想象成 undefined
的姐妹。它们的表现相似。比如,当你打算访问它的属性时,会抛错。与 undefined
相似,null
是其自身类型的唯一值。
但是 null
也有特殊的一点。
|
|
是个历史 bug,但是为了不影响先有代码,所以这个一直没有被修复。
为什么需要同时有 null
和 undefined
呢?因为这可以帮你把「(可能导致 undefined
的)编码错误」和「(可能被你表示为 null
)的缺失数据」区分开。然而,这只是一个约定,JavaScript 并不会强制这种用法。
布尔值(Booleans)
布尔值只有两个:true
和 false
。
数字(Numbers)
|
|
有限精度
|
|
0.1
和 0.2
都是被「四舍五入」到最接近的可用数字。而四舍五入的错误会不断累积,因此将它们相加并不能得出 0.3
。
浮动小数点(Floating Decimal Point)
浮点数学运算的另一个有趣方面是,数字的精度是「浮动的」,它取决于数字的大小。我们离 0
越近,数字的精度就越大,数字之间「挨」得也越近:
当我们从 0
开始向任一方向移动时,我们便开始丢失精度。在某个时刻,即便是两个紧挨着的数字也会相差得比 1
还要远:
|
|
特殊数字(Special Numbers)
浮点数学运算包含一些特殊数字。你可能偶尔会遇到 NaN
、Infinity
、-Infinity
、-0
。之所以它们会存在,是因为有时你可能会执行诸如 1 / 0
之类的操作,而 JavaScript 需要以某种方式表示其结果。
|
|
NaN
尤其有意思。NaN
是 0 / 0
这种不正确的数学计算的结果,代表「非数(Not a Number)」。
总结
- **JavaScript 实现了一种叫做「浮点数学」的标准。**越靠近
0
,数字越精确,反之越不精确。 - 像
1 / 0
或者0 / 0
这类不正确的数学操作的结果是特殊的数字。NaN
是这些特殊数字中的一员。 - **
typeof(NaN)
是number
,因为它这个值本身确实是数字。**只不过因为代表了「不正确的」数字这个含义,而被叫做「非数」。
大数(BigInts)
对于精度要求很高的金融计算,大数会很有用。
|
|
字符串(Strings)
字符串代表了 JavaScript 中的文本。有三种写字符串的方式(单引号、双引号、反引号),但是结果都一样。空字符串也是字符串。
字符串不是对象
字符串属性是特殊的,并不和对象属性的表现一致。例如,你不能给 cat[0]
赋值。字符串是原始值,而所有的原始值都是不可变的。
|
|
符号(Symbols)
|
|
对象(Objects)
对象包含数组、日期、正则表达式和其他非原始值的值:
|
|
Making Our Own Objects
每当我们使用 {}
这种对象字面量时,我们就「创建」了全新的对象值:
|
|
对象会消失吗?(Do Objects Disappear?)
JS 自带垃圾回收机制,当我们的代码中没有值相关的引用时,这些值可能会消失。
函数(Functions)
|
|
这段代码有几个函数。7 个。
每当我们执行一行包含函数声明的代码时,一个全新的函数值在我们的宇宙中出现了。
每当我们执行像 let dwarf = {}
的代码时,一个全新的对象是如何出现的。我们创建对象,我们也创建函数。
测试
下面代码运行后的变量和值的示意图。图 B 正确
|
|
参考资料
文章作者 Chen Guixian
上次更新 2021-09-14