程序在运行的时候,它的指令和变量是存放在计算机内存里的。我们可以把计算机内存想象成一本巨大的笔记本,因而当我们通过 var
声明了一个变量,就相当于告诉 JavaScript 让它想办法从这本笔记本中找到一处可以写作的地方,并且在那个地方放置一个书签(变量名),以便我们在需要的时候可以迅速的通过书签找到那个地方,而 JavaScript 的垃圾回收机制会自动的在我们不需要这个书签的时候默默的把那个地方还原为可以写作的状态,防止笔记本被写满。
例如执行如下代码时,内存的变化如图所示。
var a; // 声明一个变量
a = 123; // 给这个变量赋值为 123
console.log(a);
a = 456;
=
的作用会在本章稍后介绍。
上边的代码执行之前,内存如下图所示。
graph BT
A[...]
B[...]
C[...]
D[...]
第一行的var a
就是告知 JavaScript 让它在内存中寻找一处空白,并给这里放置名字为a
的书签,执行完后,内存就成了下图所示的模样。图中的undefined
英文意思为「未定义」,是变量的默认值。
graph BT
A[...]
B[...]
C[undefined] -.-> c[a]
D[...]
然后程序执行到了a = 123
,这里就是告知 JavaScript 向变量名a
所标记的内存地址写入123
,执行完后,原来的默认值undefined
就被覆盖了,于是内存就成了如下图所示的样子,这个时候我们称变量a
持有数字123
。
graph BT
A[...]
B[...]
C[123] -.-> c[a]
D[...]
然后执行console.log(a)
,其中的变量名a
,就是告知 JavaScript 通过a
找到到其所标记的内存中的值,也就是数字123
,然后console.log(...)
将这个123
输出到控制台给我们查看。这一步仅仅是读取操作,并没有改变内存中的值,所以内存的样子跟上图一致。
小练习:最后,程序执行了a = 456
,执行完成后内存变成了什么样子呢?变量a
持有的值又是什么呢?
编程语言中的语句和表达式,就像自然语言中的主谓宾,是语法的一部分,当我们熟练掌握了 JavaScript 的时候,我们并不需要在乎我们写的这串代码是语句、表达式还是其他,就像我们在说中文的时候,并不需要关注哪些是主语哪些是宾语一样。不过,在学习编程的时候了解相关概念,可以在学习的时候更容易的的理解某些知识。
表达式是一组可以计算出一个结果的有效的代码的集合,表达式的计算结果,叫做表达式的返回值,最简单的表达式是一个常量或变量。
123 // 这是一个最简单的表达式,它是一个常量,返回值是 123
a // 这是一个最简单的表达式,它是一个量,返回值是变量 a 所持有的值
同时表达式还可以由操作符和操作数构成,操作数可以是常量也可以是变量,操作符和操作数的组合就构成了千奇百怪的表达式,例如下边是合法的的表达式:
123 + a * 123 / 222 + -555 + b // 这是一个合法的表达式
同时表达式还可以是由子表达式构成,也就是说,表达式可以由,操作符、操作数、子表达式共同构成,例如:
var a = 123 / 222 + -555 + b
var a = 123 / 222 + -555 + b
是一个表达式123 / 222 + -555 + b
也一个表达式,它是上述表达式的子表达式
JavaScript 通过分号;
区分一个语句,当我们在表达式后边添加一个;
的时候,它就成了一个语句。
var a = 123 / 222 + -555 // 表达式
var a = 123 / 222 + -555; // 语句
123 // 表达式
123; // 语句
a // 表达式
a; // 语句
注意语句末尾的
;
不是所有语句都是由表达式加;
构成的,我们以后还会学到许多有用的语句,它不是由表达式加分号构成的,而是有自己的语法和特定的用途。同时,比如空语句,它只有一个;
没别的:
; // 空语句
JavaScript 是种很「聪明」的语言,它「聪明」的一个地方就是如果我们忘了在某些表达式后加分号,那么它会在程序运行之前它会自动的帮我们把分号加上,保证运行时能够正确的识别表达式和语句。JavaScript 自行加上的分号在绝大多数情况下都能够清楚区分语句和表达式,因此有些团队的 JavaScript 编码风格就要求省略掉分号。例如下边的表达式,我们就算不加分号程序也能正常运行,因为 JavaScript 在运行之前默默的的帮我们将分号加上了。
var a = 123 / 222 + -555 // 表达式
123 // 表达式
a // 表达式
省略分号的行为在 C/C++ 或 Java 等语言中会直接出错。
但毕竟是程序自行加上的,所以总是避免不了在极端情况下出现一些奇怪的问题,除非你清楚了解 JavaScript 加分号的机制,否则还是不要省略分号为佳,为了让大家更好的分辩表达式和语句,本书的示例代码不省略分号。
在 JavaScript 中可以使用 =
操作符对变量进行赋值。在 JavaScript 中 =
(赋值操作符)的含义和数学中的 $$ = $$ (等于号)是不同的。赋值操作符 =
的作用是将其右边的数据赋值给左边,即将 =
操作符右边的数据,写入左边变量名所标记的那一处内存中,例如:
var a, b, c;
a = 3; // 3 在右边, = 把右边的 3 给左边的 a ,所以这时候 a 的值为 3
b = 5; // 5 在右边, = 把右边的 5 给左边的 b ,所以这时候 b 的值为 5
c = a = b; // 先把最右边 b 的值赋给 a,这个时候 a 和 b 的值都为 5,再把 a 的值赋给 c,此时 c 也为 5
console.log(a, b ,c); // 现在 a b c 都为 5
当我们将某个值赋值给了某个变量的时候。我们就说该变量持有了该值,例如,下边代码中,变量a
持有了数字3
,变量b
持有了数字5
。
var a, b;
a = 3;
b = 5;
=
操作符也可以在声明变量的时候使用,例如:
var a = 3,
b = 5,
c = a = b;
console.log(a, b ,c); // 现在 a b c 都为 5
严格来说,=
操作符的最右边是表达式,之前说的都是简单的表达式,也就是一个常量或者变量的情况。其实在无论表达式多么复杂,赋值的时候都是先计算最右边先计算表达式的返回值,再将返回值逐个往左赋值。
var a, b, c;
a = 1;
b = 2;
c = 3;
a = a + b + c; // 这时先计算 a + b + c,也就是 1 + 2 + 3 再把结果赋值给 a
console.log(a); // 执行完上句代码后 a 的结果为 6
a = a + a; // 这时先计算 a + a,也就是 6 + 6, 再将结果赋值给 a
console.log(a); // 现在 a 的值是 12
// 这时先计算 a + b + c, 也就是 12 + 2 + 3
// 然后将结果赋值给 c ,再将 c 的值赋值给 b ,再将 b 的值赋值给 a
a = b = c = a + b + c;
console.log(a, b, c); // 现在 a b c 都为 17
JavaScript 规定标识符只能由下划线( _ ),美元符号,数字,以及字符(字母或文字)组成,并且不能以数字开头。而变量名就是一种标识符,因而必须按照标识符的要求命名,例如:
var _name; // 对的
var 1name; // 错的,以数字开头
var na1me; // 对的
var name1; // 对的
var _name_; // 对的
var $; // 对的
var 唐宋元明清; // 对的,但不推荐使用英文之外的语言给变量起名字
var お願いだから; // 对的,但不推荐使用英文之外的语言给变量起名字
var ^&*; // 错的
JavaScript 中属于语言语法本身的单词,例如 var
function
等,这类单词叫做关键字。还有一些留给语言以后用做关键字的单词,叫做保留字,它们都不能用做变量名。
var var; // 错的
var function; // 错的
var class; // 错的
var return; // 错的
目前关键字和保留字一共几十个,随着对 JavaScript 的不断深入,自然而然就知道哪些可以用作变量哪些不可以,如果不小心误用, JavaScript 会报错并提示我们修改变量名,因而不用死记硬背。当然如果有时间自行查阅相关文档了解一下也不是什么坏事。
现实开发中,我们经常要对某个值加一,比如某篇文章的阅读量看一次加一,某个赞一次加一。也经常要对某个数值减一,比如发送验证码后的倒计时每秒减一,商品的库存每卖出一次减一。于是 JavaScript 为了方便我们进行这类操作,提供了自增操作符++
和自减操作符--
。它们的作用,就是给持有数字类型的变量的值自增一或是自减一。
var a = 10;
a++;
console.log(a); // 11
var b = 10;
b--;
console.log(b); // 9
自增/自减操作符属于是一种赋值操作,所以它只能对变量使用,而不能对值使用;
var a = 10;
a++; // 对的
9++; // 错的
自增/自减操作符可以用在变量后面,也可以用在变量前面。用在变量前面的时候,作为表达式返回时,得到的就是自增/自减之后的值,用在变量后面的时候,得到的就是自增/自减之前的值。
var a = 10;
console.log(a++); // 10,自增之前的值
console.log(a); // 11
var b = 10;
console.log(--b); // 9,自减之后的值
console.log(b); // 9
有时候我们希望变量自增/自减不只能是1
,而可以是特定的值,我们就可以使用+=
(加后赋值)和-=
(减后赋值),它们的用法如下所示:
var a = 10;
a += 5; // 自增 5
console.log(a); // 15
a += 6; // 自增 6
console.log(a); // 21
var b = 20;
b -= 5; // 自减 5
console.log(b); // 15
b -= 10; // 自减 10
console.log(b); // 5
*=
(乘后赋值)和/=
(除后赋值)操作符的用法类似。
var a = 20;
a *= 2;
console.log(a); // 40
a /= 2;
console.log(a); // 20
- 我们运行如下代码的时候, JavaScript 在背后默默的为我们做了什么?
var username;
- 下列代码改了哪里的内存?对内存做了什么改动?
id = 1234567890123;
- 阅读下列代码并回答问题。
var a;
a = 20;
var b, c;
c = 1 + 2 + 3;
b = 4;
var d = c + b;
var e, f, g;
e = f = g = a - c; // #1
- a. 代码运行完后各个变量的值是什么?
- b.
#1
处赋值的先后顺序是什么?
- 小明修改了小红编辑过的 JavaScript 文件,阅读下面代码并回答问题。
/* 小红之前的代码 */
var x, y, z, u;
y = 5;
x = 3;
z = y % x;
/* 小明新加入的代码 */
var x, dydx;
x = 0;
dydx = 2 * x + 5;
/* 小红之前的代码 */
y = 19;
u = (y + z) / x;
- a. 然后,小红的代码出了 bug 为什么?
- b. 应该如何修改代码,修复小红的 bug。
- 回答下列问题。
- a. ++a 和 a++ 的区别是什么?
- b. --a 和 a-- 的区别是是什么?
- 阅读下列代码, 当代码运行完后
#1~#8
分别输出什么。
var a = 5;
a += a;
console.log(a); // #1
console.log(a++); // #2
console.log(++a); // #3
var b = 4;
b -= 1;
console.log(b); // #4
console.log(--b); // #5
console.log(b--); // #6
var c = 3;
c *= 2;
console.log(c); // #7
c /= 2;
console.log(c); // #8
- 用 JavaScript 描述下列数学式子,其中的未知数用变量替代。其中形如 $$ \dfrac{\mathrm{d}y}{\mathrm{d}x} $$ 的用形如
dydx
的变量代替。
- a. $$ \dfrac{1}{2} \times y \times \dfrac{\mathrm{d}x}{\mathrm{d}t} + \dfrac{1}{2} \times x \times \dfrac{\mathrm{d}y}{\mathrm{d}t} $$
- b. $$ \dfrac{x \times -1 + 12 \times y}{2} \times \dfrac {z} {13} $$
- c. $$ \dfrac{13 \times 5 }{ \dfrac{\mathrm{d}y}{\mathrm{d}t} } \times 4 + \dfrac{13}{2} \times 2 $$