模块化、对象和状态

我们关于世界的常规观点之一,就是将它看作聚集在一起的许多独立对象,每个对象都有自己随着时间变化的状态。所谓一个对象有状态,也就是说它的行为受到它的历史的影响

赋值与局部状态

假定开始账户里有100元钱,在不断使用withdraw的过程中可以得到以下响应序列:

1
2
3
withdraw(25) // 75
withdraw(25) // 50
withdraw(60) //余额不足

可以看到每次执行withdraw的结果不同,我们用一个变量balance去表示账户里的现金余额

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
let balance = 100;
function withdraw(amount) {
if(amount > balance){
console.log("余额不足");
} else {
balance -= amount;
console.log(balance);
}
}
withdraw(50);// 50
withdraw(20);// 30
withdraw(20);// 10
----------------------------------
function new_withdraw(amount) {
let balance = 100;
if (amount > balance) {
console.log("余额不足");
} else {
balance -= amount;
console.log(balance);
}
return balance;
};
w1 = new_withdraw(50);// 50
w2 = new_withdraw(20);// 80
w3 = new_withdraw(20);// 80

可以看到w1、w2、w3是各自独立的,互不影响,与所有状态都必须显式地操作和传递额外参数的方式相比,通过引进赋值和将状态隐藏在局部变量中的技术,我们能以一种更模块化的方式构造系统。

按值传递和按引用传递

1
2
3
4
5
6
7
function() {
let apple = { fruit: "apple" };
console.log(apple.fruit);//apple
let banana = apple;
banana.fruit = "banana";
console.log(apple.fruit);//banana
}

变量是按引用传递的,apple.fruit的值会被修改,banana 复制了 apple的指向内存,两者共用一个内存,所以apple.fruit的值会被修改,因为变量保存的是指针,所以变量的值有暂时记忆。

用变动的数据做模拟

比如保存上一次的看的书的位置

1
2
3
4
5
6
7
8
9
10
11
let book = {
name: name,
chapter: number,
page: number,
}
function store(name, chapter, page) {
book.name = name,
book.chapter = chapter,
book.page = page,
}
store("CSS世界", 1, 13);

并发:本质上是时间问题

现实世界的对象并不是一次一个地顺序变化,与此相反,它们总是并发地活动,所有东西一起活动。所以用并发执行的计算进程模拟各种系统是正常的。从表面上看,时间似乎是非常简单的东西。它也就是强加在各种事件上的一个顺序。

共享变量的处理

如果小明和小红要借阅同一本书,登录图书系统,则谁会借到这本书?

  1. 如果小明先申请,这时小红也提出申请,服务器先写入小明的名字,当读取时被小红的写入改变,最后返回小红的名字,反而小红借到了这本书。
  2. 如果小明先申请,这时小红也提出申请,由于小明网络延时,服务器先返回小红的请求,反而小红借到了这本书。

如何让小明借到这本书?

方案一: 同时只让一个人提出申请,当一个人处于申请这本书状态时,不让其他人提出申请,严格限制请求,先到先得
方案二: 使用互斥锁,先获取先到的请求,忽略其他请求,竞争性互斥,具有不确定

在并发系统的设计中,需要去控制不同进程访问共享变量的事件发生顺序,可以通过限定修改任意共享变量的两个操作都不允许同时发生,但是这样太低效和保守了;另一种保证并发系统产出的结果与各个进程按照某种方式顺序运行产生的结果完全一样,但是顺序不确定,常用互斥锁。