首页 存档 技术 查看内容

白话 JS 数值的可变与不可变(另白话 Async 勘误)

2018-3-30 13:00 |来自: 互联网 354 0

摘要: 写作目的 如果你是 JS 的初学者,同时你又在学习前端框架(包括 Angular),你可能会对后面这个描述感到有些不解:“以不可变数值(immutable value)作为程序状态(app state)的载体,会提升程序的性能”。什么是 ...



写作目的

如果你是 JS 的初学者,同时你又在学习前端框架(包括 Angular),你可能会对后面这个描述感到有些不解:“以不可变数值(immutable value)作为程序状态(app state)的载体,会提升程序的性能”。什么是可变与不可变(后文以 mutability 代替 “可变与不可变”)?为什么数值的 mutability 会影响程序的性能?本文将尝试尽量不使用术语来回答这些问题。

读者指引

需要读者大概了解下列知识点:

  • Object.assign 方法。

  • Object 里的 getter 方法。

  • Angular 2 的 @Input 和 ChangeDetectionStrategy。

什么是数值的可变与不可变

首先要明确的一点是:当提到 mutability 的时候,我们是在说数值,而不是变量。比如 var text = 'abc';,这里变量是 text,而 text 所对应的数值是 ‘abc’。mutability 是针对数值 ‘abc’ 而言的。

===== 分隔线 =====

先说什么是不可变(immutable)。
在《JavaScript: The Definitive Guide, 4th Edition》,关于 JS 的数据类型的章节有这么一句:

… because strings are immutable: there is no way to change the contents of a string value.

作者此时并不是在说明 mutability,但他不经意间说出了不可变数值的特点,即:我们无法改变一个不可变数值的内容。为什么不可变?原因有两种:一,人为设定某类数值不可变;二,逻辑不通。

比如(例1):

var text = 'abc';
text[1] = 'd';
text[1] === 'b'; // true

我们可以认为 string 数值(比如“例1”中变量 text 所对应的数值)是由单一字符构成的,但是我们不能修改 string 中的某个单一字符,因为:这是人为设定,JS 就是这么设计的。在其他的编程语言中,存在 mutable string(参见Language-specific details)。
除 string 以外,JS 中的 number, boolean 等其他“原始值(primitive types)”也是不可变的,原因是逻辑不通 数字 1 在变成数字 2 以后就不再是数字 1 了。

===== 分隔线 =====

对照不可变数值,可变(mutable)数值就是:其内容可以改变的数值。在 JS 里,可变数值包含 object(array、function等等是特种 object)、map、set等等。以 object 为例(例2):

var nameObj = {firstName: 'Doe', lastName: 'John'};
nameObj.firstName = 'Foo';
nameObj.firstName === 'Foo'; // true

很明显,我们成功的修改了 nameObj 的 firstName 属性。所以说,object 是可变的。可是,这又何坑之有呢?

可变数值带来的是个什么坑

“object 是可变的”,这一点并不是什么坑,坑的是“JS 中对可变数值的操作是以引用的方式(by reference)进行的”,内容变化了,引用却没变,一不留神,就变成了坑。以 object 类型数值为例(例3):

var inputObj = {firstName: 'Doe', lastName: 'John'};
var inputObj_beforeChange = inputObj; inputObj.firstName = 'Foo'; inputObj === inputObj_beforeChange; // true
  • var inputObj = {firstName: 'Doe', lastName: 'John'};:这一行包含三个步骤,1)创建变量 inputObj,2)创建新的 object 实例,3)把“对 object 实例的引用”分配给 inputObj。

  • var inputObj_beforeChange = inputObj;:这里是拷贝 inputObj 所包含的引用,并分配给 inputObj_beforeChange。

  • inputObj.firstName = 'Foo';:改变 inputObj 引用的 object 的内容。

  • inputObj === inputObj_beforeChange;:对比两个引用,因为引用相同,此处为 true。

这里的坑就是:我们无法通过“拷贝初值并与结果对比”的方式来判断可变数值的内容是否发生过变化。

可变数值的坑的影响

你可能会问:不能通过哇啦哇啦的方式来判断哇啦哇啦又怎么样?我们来看一下 Angular 2 里的 ChangeDetectionStrategy。假设我们在 AppComponent 下有一个这样的 ItemComponent:

@Component({
 selector: 'item',
 templateUrl: '
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系 [邮箱地址] 删除

路过

雷人

握手

鲜花

鸡蛋

相关分类

返回顶部