4.2.2.3 JS 速成:顺序结构,变量与值,数组与对象
INFO
本文按照 Mozilla 贡献者基于 CC-BY-SA 2.5 协议发布的以下文章改编:
- https://developer.mozilla.org/zh-CN/docs/Learn/JS/First_steps/What_is_JavaScript
- https://developer.mozilla.org/zh-CN/docs/Web/JS/Guide/Grammar_and_types
- https://developer.mozilla.org/zh-CN/docs/Web/JS/Reference/Global_Objects/Number
- https://developer.mozilla.org/zh-CN/docs/Learn/JS/First_steps/Arrays
- https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Objects/Basics
本文假设你已经至少掌握一门编程语言,所以介绍非常简略,并且假设读者已经学会编程的各种概念。
如果你没有接触过编程,那么就需要完整学习 JS。可以完整阅读 MDN: JavaScript—— 动态客户端脚本语言中的所有教程,确保掌握 JavaScript。或者你也可以阅读 The Modern JavaScript Tutorial (英文) 。这些教程完整的涵盖了我本节提到的内容,所以看了就可以替代本节,当然时间就会拉长,也就没法速成 JS 了,对于编程语言而言,如果你没有学会一门编程语言,没有掌握编程语言的思维方式,还是有必要系统学习的。
JS 速成教程的内容大多来自本人以前写的 JS 入门教程,成文时间较早。如果发现有什么难懂的地方可以反馈。
环境要求
拥有一个可以交互式运行 JavaScript 的环境。可以是 Node.js 命令行,也可以是浏览器的控制台 (Console),在浏览器按 F12 打开开发者工具就可以找到。
我们的教程要求大家同步键入 JS 代码来交互式学习,提高效率。
对于完整的 JS 程序作业,如果不涉及浏览器提供的 API 的话,建议使用 Node.js 来运行 .js
文件。
node <name>.js
对于浏览器,使用我们前面提到的运行环境,加上 <script>
标签,即可加载 JS 文本并运行。具体可以查阅 MDN: 怎样向页面添加 JavaScript。
变量,值和顺序结构
语句和注释
在开始变量之前我们讲一下语句。JS 的语句是以英文分号 ;
结尾的,而不是换行,因为同一行内可以用分号隔开多条语句。虽然 JS 对于一个没有分号直接换行的语句会自动补上分号,但是最好不要这么做。为了防止自己出现这些问题,你可以给自己的 VSCode 装上相关的 Lint 插件。
注释可以写在代码里面,解释器会自动忽略它们。你可以用这些说明你的程序干了什么。JS 里面有着两种注释,一种是用 /* */
包裹的注释,一种是用 //
开头到一行结尾的注释。下面是两种注释的使用。后面我们会用到的。
/* 我是一行注释 */
// 我也是一行注释
/*
我可以跨行
*/
// 我只能
// 这么跨行
2
3
4
5
6
7
8
数据类型与基本运算
JS 里有七种基本数据类型,但是很多类型现在都用不到。所以我们就先讲下最常用的数字和字符串,以及一些基础的运算。
数字
在 JS 里面只有一个数字类型。不论这些数字是像 30(也叫整数)这样,或者像 2.456 这样的小数(也叫做浮点数),在内部都是用浮点数来表示的。这个和别的编程语言(比如 python)不同。这意味着 1
和 1.0
是同一个数字(而其他语言就是两个类型的数字)。
数字可以进行四则运算,优先级和数学里一样,你也可以加上括号改变优先级,就像在用计算器。乘号使用星号 ( *
) 代替,除号使用斜杠 ( /
) 代替。运算符两边可以加上空格,这样排版会更美观一点 (也许是)。
你可以打开浏览器的控制台运行你的 JS,也可以在本地安装 Nodejs ,使用 Node 来运行。我演示的时候使用的是 Node,因为可以直接在 VSCode 的控制台内输入 node
指令来打开。
下面是在 Node 中运行的结果,其中 >
表示这行是输入,输入紧跟着的下一行就是控制台的提示输出。注意实际的 JS 脚本在浏览器运行的时候是没有给每行加上一个输出的,你要用 console.log()
来进行。
> 1; // 1 就是 1
1
> -1; // -1 就是 -1
-1
> 1+1; // 就是一加一
2
> 1 + 1; // 加上空格之后运行结果也一样
2
> 1 * 2 + 2 * 3; // 运算符优先级和数学一样
8
> (1 + 2) * 3; // 使用了括号
9
> 6 / 3; // 除法
2
> 1.1 + 1.5; // 小数加法
2.6
> 0.1 + 0.2; // 这是浮点数误差导致的
0.30000000000000004
>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
最后一个 0.1 + 0.2
算出来结果是 0.30000000000000004
(15 个 0 ,不用数了),这是怎么回事?这不是 bug,是语言的特性,叫做浮点数误差,是因为计算机在转换小数到二进制的时候丢失精度导致的 (可以看 https://0.30000000000000004.com/ 了解)。所以以后判断小数相等的时候可能不能直接用等号来进行。
除了四则运算之外,还有一种取余运算,使用百分号 ( %
)。在两者都是正数的时候,可以取得前一个数字除以后一个数字的余数。如果出现负数可能就不太一样,不过一般不会遇到,所以就先略过。
> 5 % 2;
1
> 5 % 3;
2
> 25 % 5;
0
>
2
3
4
5
6
7
有时候我们需要舍弃小数部分,这可以用 Math
的一些方法来进行。其中 Math.round()
四舍五入, Math.floor()
向下取整, Math.ceil()
向上取整。
> Math.round(3.1415926)
3
> Math.round(3.999)
4
> Math.ceil(3.1415926)
4
> Math.floor(3.999)
3
>
2
3
4
5
6
7
8
9
数字的范围是有限制的,超出限制的就无法精确表示。JS 能够准确表示的整数范围在 -2^53
到 2^53
之间(不含两个端点),超过这个范围,就无法精确表示这个整数。
如果一个式子不能被计算,那么它的值就会变成 NaN
( Not a Number,不是一个数字,最典型的就是给 -1 开平方,或者尝试给一个不全是数字的字符串转成数字。
> Math.sqrt(-1); // 给 -1 开平方
NaN
> parseInt("123"); // 正常的转换
123
> parseInt("Your life is which you loved"); //给字符转数字
NaN
>
2
3
4
5
6
7
字符串
另一个非常常用的就是字符串。顾名思义,这就是一串的字符。它可以用一对英文单引号( ''
)或者一对英文双引号 ( ""
) 包裹一串字符来表示,比如 '我是一个字符串'
"我是另一个字符串"
。
同样和别的语言不一样的是,JS 只有字符串类型,而没有单个字符类型。你可以用只有一个字符的字符串代替。
如果要在字符串里面包含特殊字符可以在字符前加上反斜杠 ( \
) 进行转义。比方说要包含引号的时候,就可以用 \"
或者 \'
来表示引号本身而不是字符串结尾。
你可以使用加号 +
来对字符串进行拼接操作,这可以得到这些字符串首尾相连的结果。
> "I am a str"; // 使用一对双引号
'I am a str'
> 'Another str'; // 一对单引号
'Another str'
> 'a'; // 单个字符的字符串
'a'
> '一个有引号\"的字符串'; // 转义
'一个有引号"的字符串'
> '也可以这样带上单引号:\'. '; // 也是转义
"也可以这样带上单引号:'. "
> "aa" + "bb"; // 拼接
'aabb'
> 'abc' + ' ' + 'def'; // 多个一起拼接
'abc def'
>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
转换
字符串和数字之间可以相互转换。这些转换通过一些 JS 内置的函数进行。有些是显式进行的(比如调用一个函数),有些是隐式进行的(比如让字符串和数字相加)。
一般情况下,涉及数字转字符串的我们采取隐式的方式。字符串转数字的时候我们使用 parseFloat()
和 parseInt()
函数来显式转换,用法是在括号内放一个字符串。 parseFloat()
的结果如果有小数会带上小数部分, parseInt()
的结果会直接去掉小数部分(而不是四舍五入)有关函数更为具体的内容我们以后会探讨,这里可以简单看作是一些封装起来的代码,执行之后返回一个结果。
下面我们来演示这些。
> "The answer is " + 42; // 一个拼接
'The answer is 42'
> 42 + " is the answer"; // 另一个拼接
'42 is the answer'
> "37" + 7; // 加法会把数字转成字符串
'377'
> "37" - 7; // 除了加法都会将字符串转成数字
30
> parseFloat("37") - 7; // parseFloat 的结果也一样
30
> parseFloat("123"); // 这里也是整数
123
> parseFloat("123.45"); // 小数部分也正常
123.45
> parseFloat("10e5"); // 还可以用科学计数法
1000000
> parseInt("123"); // parseInt 的整数转换
123
> parseInt("123.45"); // 现在去掉小数了
123
>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
变量
变量本质上是值(例如数字或字符串)的容器,比如你可以用它去存一个数字 123
,也可以存一个 Hello world!
字符串。
变量可以通过这三个关键字声明:
let
: 这是在 ECMAScript 2015 里面添加的。我们建议如果没有支持旧浏览器的需求就使用它。var
: 这是旧的声明变量方式,现在也可以用,但是不推荐,因为var
可以被重复声明,这会导致很多问题。const
: 这是常量,声明之后不能修改。
最好不要使用 var,因为它可以被重复声明,如果不小心重复声明了一个变量,会导致问题问题。同时用 var 声明的变量会发生变量提升,产生更多难以预料的问题。
变量名在 JS 里面又叫做标识符。标识符是有规则的,必须以字母、下划线(_)或者美元符号($)开头,而不能是数字开头;但是后续的字符也可以是数字(0-9)。同时 JS 支持 Unicode 字符集(也就是你可以声明一个中文变量,虽然没啥用)。JS 是大小写敏感的,比如 aA
和 aa
就是两个变量。
除了上面的规则之外,你也不能使用 JS 的保留字给变量命名。保留字,即是组成 JavaScript 的实际语法的单词。比方说什么 if
, while
, for
这些。一般变量命名是不会和保留字重复的。
我们建议使用大小写驼峰命名法,也就是包含多个单词的变量名称的第一个单词开头小写,其余单词开头大写。比如我要命名一个 "My first name" 来存名字,变量名就可以写成 myFirstName
。
变量声明之后,默认的值是 undefined
。你可以通过比较一个变量是否等于 undefined
来判断变量是否初始化 (使用双等号 ==
或三等号 ===
来比较,例如 x === undefined;
)。直接在控制台里面打一个变量名可以输出变量的值,如果调用没有声明的变量会弹出一个错误。
非 const
变量在声明之后可以重新赋值。赋值使用单个等号,格式为 变量名 = 新的值;
。新的值可以是单个值,比如 x = 1;
也可以是一个表达式 x = 1 + 2;
, 或者 x = x + 1;
(给 x 加上 1)。
声明和赋值可以写在一起,比如 let x = 1;
就可以声明一个变量,值为 1
。
我们现在可以这样试着声明变量,下面是在 Nodejs 上运行的结果,当然浏览器也一样。(语句一定要加上分号,虽然没有分号也能运行,但是最好不要养成坏习惯):
> let myFirstName; /*声明了一个 myFirstName*/
undefined
> myFirstName; // 显示 myFirstName 的值
undefined
> var myValue; // 声明了一个 myValue
undefined
> myFirstName = "Steve"; /*给 myFirstName 赋值了 "Steve" */
'Steve'
> myFirstName;
'Steve'
> myValue = 1 + 1; /*给 myValue 赋值了 1 + 1,值是 2 */
2
> myValue;
2
> myValue = "aaa"; /*重新给 myValue 赋值了一个字符串 */
'aaa'
> myValue; /*现在 myValue 是一个字符串*/
'aaa'
> let myFirstName;// 这里重复声明没有通过,而是报错了
Uncaught SyntaxError: Identifier 'myFirstName' has already been declared
> var myValue; /*这里重复声明,而且通过了*/
undefined
> myFirstName;
'Steve'
> myValue; /*还保留原来的值*/
1
> const myConst; /*声明 const 一定要先初始化!*/
const myConst;
^^^^^^^
Uncaught SyntaxError: Missing initializer in const declaration
> const myConst = 2; /*声明了一个 const 变量*/
undefined
> myConst; /*打印 myConst 的值*/
2
> myConst = 3; /*尝试给 myConst 重新赋值,不被允许*/
Uncaught TypeError: Assignment to constant variable.
>
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
27
28
29
30
31
32
33
34
35
36
37
38
你也可以现在就打开浏览器的控制台输入这些,来进行尝试。上机实践对学习编程非常重要,不上机很多问题在纸上看看是没有结果的。
由于 JS 的一些奇怪问题,我们建议不能使用下面的方式:
> bad1 = "This is bad"; /*没有加上关键字,自动变成全局变量 */
'This is bad'
> bad1;
'This is bad'
> bad2 === undefined; /*这是一个没有声明的变量的正常行为*/
Uncaught ReferenceError: bad2 is not defined
> bad2 === undefined; var bad2 = 0; /* 先使用后声明,发生变量提升*/
true
> bad2;
0
> bad3 = 0; let bad3; /*所以要用 let*/
Uncaught ReferenceError: Cannot access 'bad3' before initialization
>
2
3
4
5
6
7
8
9
10
11
12
13
用 var
声明的变量会发生变量提升,相当于把声明语句往上挪到作用域的开头了。这没什么用,反而会造成一些问题,所以你应该尽量只用 let
来声明。
变量赋值之后就可以用来进行一系列运算,就和普通的值一样。
> let x = 2; // 声明+初始化
undefined
> let y = 3;
undefined
> x + y; // 进行四则运算
5
> x - y;
-1
> x + 10;
12
> let str1 = 'AAA'; // 声明了两个字符串
undefined
> let str2 = 'BBB';
undefined
> str1 + str2; // 尝试一些拼接操作
'AAABBB'
> x + ' ' + str1;
'2 AAA'
> str1 + " is Str1";
'AAA is Str1'
>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
输入和输出
不像其他语言,JS 最初是给浏览器设计的,直到现在这也是它的最主要用途,所以它和 DOM 深度绑定,信息几乎都是从 HTML 页面读取,输出也基本都是写在 HTML 当中,甚至没有标准化的输入和输出 (Node.js 环境提供了一个输入的回调函数)。由于我们现在还没学过对象 (Object),所以我们采用一些其他方式进行输入输出。
我们使用 console.log()
函数进行输出,这个函数会在控制台上留下一条信息。不仅可以给它一个字符串,也可以给它一个语句,比如 console.log(1 + 1);
输出 2。
对于输入,我们选择手动更改代码中的相关变量赋值来进行替代。这可能有点麻烦,不过在学了 DOM 之后就可以用更标准的方法进行。
最后 —— 顺序结构
我们了解了 JS 里面的数字和字符串类型,以及它们的基本运算和转换关系。
我们还了解了变量,它是一种容器,可以用来存放变量。变量在赋值之后可以进行一系列的操作,就像普通的值一样。
现在,你可以写一些小程序来进行一些简单的运算了。比如一个最简单的 A+B Problem,输出 a+b 的结果:
let a = 1;
let b = 1;
let c = a + b;
console.log(c);
2
3
4
试试写出类似的 A-B,A*B,然后按照我们之前 Hello world 的方式加到浏览器里面运行。
这里的程序就是一个典型的顺序结构,代码从上到下一步步运行,最后得出结果。这是程序的三大基本结构之一,另外两个分别是分支和循环,我们会在不久之后提到。了解三大基本结构之后,理论上你就可以写出一些真正能用的程序了。
数组
数组是一系列按顺序排列的值的集合。它可以一下子存下一系列的值,就相当于一些按顺序排列的变量。比如我们现在要去超市买东西,依次买了很多物品,并且记录了它们的价格。
数组包裹在一对中括号内 ( []
) ,里面的值按顺序排列,并且使用英文逗号进行分隔 ( ,
) 。可以通过在数组名后面加上一个 ( []
) ,然后在括号内使用通过从零开始的下标来找到对应的值。下面我们给出了一系列的例子。
> let shopping = ['bread', 'milk', 'cheese', 'hummus', 'noodles'];
undefined
> shopping; // 打印 shopping 数组
[ 'bread', 'milk', 'cheese', 'hummus', 'noodles' ]
> let price = [ 5.00, 3.50, 12.50, 15.00, 7.50];
undefined
> price; // 打印 price 数组
[ 5, 3.5, 12.5, 15, 7.5 ]
> shopping[0]; // 使用下标进行查找,从 0 开始到比长度小一的值(这里是 4)结束
'bread'
> price[0]; // price 的第一个元素
5
> price[100]; // 越界之后值会变成 undefined
undefined
> shopping[3];
'hummus'
> price[2] = 114; // 可以给对应的下标赋值,这里是 2,就是第三个
114
> price; // 可以看到第三个发生变化了
[ 5, 3.5, 114, 15, 7.5 ]
> shopping[2] = 1234; // 还可以给原来的赋值不同类型,就和普通变量一样
1234
> shopping;
[ 'bread', 'milk', 1234, 'hummus', 'noodles' ]
> let anEmptyArray = []; // 可以先声明一个空的数组,以后再往里面加东西
undefined
> anEmptyArray; // 现在它是空的
[]
>
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
27
28
29
数组里的值甚至可以是另一个数组,这叫做数组嵌套,下面演示了嵌套数组的行为。
> let random = ['tree', 795, [0, 1, 2]]; // 声明一个嵌套的数组
undefined
> random[2][1]; // 访问嵌套数组,第一个[]导航到数组,第二个继续导航
1
> random[0] = ['acacia', 'birch', 'spruce', 'oak']; // 赋值为一个数组
[ 'acacia', 'birch', 'spruce', 'oak' ]
> random; // 现在里面有两个嵌套数组了
[ [ 'acacia', 'birch', 'spruce', 'oak' ], 795, [ 0, 1, 2 ] ]
> random[0][2]; // 同样可以访问
'spruce'
> random[0][3] = ['oak leaves', 'oak log']; // 数组套了三层了
[ 'oak leaves', 'oak log' ]
> random[0][3][1]; // 访问到第三层的数组
'oak log'
>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
数组方法
length
最常用的数组方法,可能就是获取数组长度了。数组长度可以通过 length
方法来获取,就是在数组名之后加上一个 .length
,例如 sequence.length
就可以了解我们下面定义的数组的长度。
> let sequence = [1, 1, 2, 3, 5, 8, 13]; // 声明一个数组
undefined
> sequence.length; // 打印数组的长度
7
>
2
3
4
5
length 属性最常用的时候,就是循环遍历一个数组中的所有项目。比如说下面的这个代码:
let sequence = [1, 1, 2, 3, 5, 8, 13];
for (let i = 0; i < sequence.length; i = i + 1) {
console.log(sequence[i]);
}
2
3
4
我们以后会详细了解循环,但是这里先稍微提下这里主要干的事情:
- 在数组中的元素编号 0 开始循环。
- 在元素编号等于数组长度的时候停止循环。 这适用于任何长度的数组,但在这种情况下,它将在编号 7 的时候终止循环(还记得数组的编号是从 0 开始的吗?0 到 6 就是 7)。
- 对于每个元素,使用 console.log () 将其打印到浏览器控制台。
字符串和数组之间的转换
有时候你会需要把一个有规律的字符串转化成数组来处理数据,比方说这样的字符串
let myData = 'Manchester,London,Liverpool,Birmingham,Leeds,Carlisle';
显然,这里是用 ,
来分割的一系列单词。如果我们要把它转成一个数组,就可以用 split()
方法,它会对字符串进行处理,然后返回一个数组。方法的用法是在字符串之后加上一个 .split()
,在括号内指定分隔符(默认是空格)。
> let myData = 'Manchester,London,Liverpool,Birmingham,Leeds,Carlisle';
undefined
> let myArray = myData.split(','); // 转换成数组
undefined
> myArray; // 现在已经转成数组了
[
'Manchester',
'London',
'Liverpool',
'Birmingham',
'Leeds',
'Carlisle'
]
> myArray[2]; // 第三个
'Liverpool'
> myArray[myArray.length - 1]; // 最后一个
'Carlisle'
>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
如果要把数组转成对应的字符串,可以用相反的方法 .join()
,用法和前面的相同。(假设我们这里是紧跟着上面执行的)
> let myNewString = myArray.join(',');
undefined
> myNewString;
'Manchester,London,Liverpool,Birmingham,Leeds,Carlisle'
>
2
3
4
5
添加和删除数组项
这里我们可以使用 push()
和 pop()
方法在数组尾部进行添加和删除元素。使用 push()
方法之后会返回新数组的长度,使用 pop()
方法之后会返回被删除的那个值。也许你可以用变量存下它们。
> let myArray = ['Manchester', 'London', 'Liverpool', 'Birmingham', 'Leeds', 'Carlisle'];
undefined
> myArray.push('Cardiff'); // 添加一个元素
7
> myArray;
[
'Manchester',
'London',
'Liverpool',
'Birmingham',
'Leeds',
'Carlisle',
'Cardiff'
]
> myArray.push('Bradford', 'Brighton'); // 添加两个元素
9
> myArray;
[
'Manchester', 'London',
'Liverpool', 'Birmingham',
'Leeds', 'Carlisle',
'Cardiff', 'Bradford',
'Brighton'
]
> let newLength = myArray.push('Bristol'); // 存下新的长度
undefined
> myArray;
[
'Manchester', 'London',
'Liverpool', 'Birmingham',
'Leeds', 'Carlisle',
'Cardiff', 'Bradford',
'Brighton', 'Bristol'
]
> newLength; // 现在的新长度
10
> myArray.pop(); // 删除最后一个元素
'Bristol'
> let removedItem = myArray.pop(); // 存下删掉的元素
undefined
> myArray;
[
'Manchester',
'London',
'Liverpool',
'Birmingham',
'Leeds',
'Carlisle',
'Cardiff',
'Bradford'
]
> removedItem; // 删掉的元素
'Brighton'
>
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
采取这个方法我们可以模拟一个栈操作。栈是一种 LIFO(先进先出)的数据结构。通俗的讲起来就像是叠盘子,后面的叠在上面,拿的时候就拿走最上面的。最后叠的最先拿出来,就是一个先进先出了。以后我们会单独开章节讲数据结构,当作拓展的内容。
对象
我们刚刚提到了数组,这是一种按顺序存值的方法。它用途非常广泛,但是因为只能按下标找值,在有些时候可能会比较难用。比如现在我们要存一个人的相关信息,比如名字,性别,年龄,简介这些,如果用数组就会遇到一个难题:必须给这些信息确定一个顺序,规定第一个是名字,第二个是性别等等。这样写会带来理解上面的困难(一堆乱七八糟的数字可不是很好阅读的)。如果使用一系列的单一变量去存,会搞出一大堆变量。那么有没有更好的方法呢?答案是使用对象。
对象 (Object) 是包含一系列相关数据和方法的集合(通常由一些变量和函数组成,我们称之为对象里面的属性和方法)。由于我们目前还没有讲函数,我们暂时先讲对象的数值相关的东西,以后在函数部分再提对象的方法。
对象使用一对大括号 ( {}
) 来表示,里面包含着很多用逗号分开的成员,每一个成员都拥有一个名字(下面的 name, age 这些),和一个值(如 ["Linus", "Torvalds"], 52 都是值)。每一个名字和值 (Name and Value) 之间由冒号(:)分隔,然后就可以通过成员的名字来查找对应的值。下面是一个例子。为了看得更清楚,我们在其中插入了换行,一行一个成员。我们推荐在以后编程实践当中适当使用换行,因为可以让代码更加整洁。
let person = {
name: ["Linus", "Torvalds"],
age: 52,
gender: 'male',
saying: 'NVIDIA, **** YOU!'
};
2
3
4
5
6
可以使用点表示法来查找对应的值,就是在对象标识符之后加上一个点,在点后面加上键名称就可以了,比如 person.age
就可以对应 person
的 age
对应的值,即 52
。
还有另一种表示法是括号表示法,用法类似数组,是在一个中括号 []
内加上键名称来对应的,例如 person["saying"]
就可以对应到那个 saying
。
我们建议使用点表示法,因为更加简洁,方便打字。
下面是在 NodeJS 里面运行的结果,换行之后那三个点是 node 加上的,表示上一句话还没结束。这不是代码的一部分,所以自己写代码的时候不要加上去。
> let person = {
... name: ["Linus", "Torvalds"],
... age: 52,
... gender: 'male',
... saying: 'NVIDIA, **** YOU!'
... };
undefined
> person;
{
name: [ 'Linus', 'Torvalds' ],
age: 52,
gender: 'male',
saying: 'NVIDIA, **** YOU!'
}
> person.age; // 点表示法
52
> person['saying']; // 括号表示法
'NVIDIA, **** YOU!'
>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
子命名空间
可以用一个对象来做另一个对象成员的值。比如我们现在可以把 person
的 name
从一个数组换成一个对象。就像是下面这样。
let person = {
name: {
firstName: 'Linus',
lastName: 'Torvalds'
},
age: 52,
gender: 'male',
saying: 'NVIDIA, **** YOU!'
};
2
3
4
5
6
7
8
9
这样就可以用点表示法进行多重的查找,比如 person.name.firstName
这样子
> let person = {
... name: {
..... firstName: 'Linus',
..... lastName: 'Torvalds'
..... },
... age: 52,
... gender: 'male',
... saying: 'NVIDIA, **** YOU!'
... };
undefined
> person.name.firstName;
'Linus'
> person.name.lastName;
'Torvalds'
>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
这样非常有用,因为你可以在对象里面嵌套另一个对象,从而有结构地保存一系列的数据,起到方便阅读和维护的作用。
设置对象成员
有时候我们需要对对象进行一系列的操作。比方说我们可能会修改一个成员,可能会加上一个还没有的成员,或者一个成员不要了我们可以删除它。这些都是可以做到的。
现在我们再创建一次上文中的 person
对象,然后对这个变量进行一系列操作:
let person = {
name: ["Linus", "Torvalds"],
age: 52,
gender: 'male',
saying: 'NVIDIA, **** YOU!'
};
2
3
4
5
6
现在你可以试着修改其中的成员:
> person.name = 'Tux'; // 设置名字
'Tux'
> person.age = 25; // 设置年龄
25
> person; // 现在已经变化了
{ name: 'Tux',
age: 25,
gender: 'male',
saying: 'NVIDIA, **** YOU!' }
>
2
3
4
5
6
7
8
9
10
你可以删除其中的成员
> delete person.saying;
true
> person; // 现在已经成功删除了
{ name: 'Tux', age: 25, gender: 'male' }
>
2
3
4
5
还可以直接添加原来没有的成员
> person.newValue = 123;
123
> person; // 可以看到直接加上了一个值
{ name: 'Tux', age: 25, gender: 'male', newValue: 123 }
>
2
3
4
5
方法
其实对象远远比我们现在讲的要复杂多了,比如对象的成员可以不只是一个普通的值,还可以是函数 (事实上这个才是对象里面用的最多的)。关于函数的定义比较复杂,现在你可以认为是一组执行任务或计算值的语句。在对象里面作为的函数叫做这个对象的方法。
下面我们看下这个例子,我们给它添加了一个用来在控制台进行输出的方法。
person.say = function (){
console.log("Hello, I am " + this.name + ".");
};
2
3
在 NodeJS 中运行。
> person.say = function (){
... console.log("Hello, I am " + this.name + ".");
... // 这里的 this 指的是当前的对象,相当于外面用 person.name
... };
[Function]
> person.say; // 这里不是调用函数,而是会指向函数本身
[Function]let cat = {
name: "Ket",
say: function() {
console.log("My name is " + this.name);
}
}
cat.say();
> person.say(); // 要加上括号才能调用,现在输出了内容
Hello, I am Tux.
undefined
>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
可以看到我们让 person
对象输出了自己的名字。简单地说我们干了这几件事情:
- 使用 function 关键字表明下面是一个函数
- 在函数里定义了一条
console.log()
语句,向控制台输出。 - 用 this 定位到自己本身,然后把自己的名字拼接到字符串内
- 使用
person.say()
来进行输出。
所以我们之前遇到的很多东西 (例如本章讲的 push()
pop()
或者刚刚用到的 console.log()
) 其实都是方法。有关于函数的内容我们会在以后详细地了解。
小结
我们了解了数组和对象。
数组是一种按顺序保存一系列值的数据结构,声明的时候用一对的中括号 []
来表示,其中可以包含用逗号分隔的值。
数组的每个元素可以采用下标进行查找,下标范围从 0 开始到比长度小一的值结束,你可以用 length
来获取长度。
数组和字符串之间可以使用 split()
和 join()
方法进行相互转换。
可以用 push()
在数组末尾加入值,也可以用 pop()
来删除末尾的值。
对象通过一对大括号 {}
来声明,里面用逗号分隔每一个成员。成员是由 名字: 值
的配对组成的。
对象的成员可以用点表示法查找,也可以使用括号表示法。
对象的成员就像一个普通的变量,可以进行修改。如果给一个原本没有的成员进行赋值,会添加一个新的成员。 对象的成员可以是函数,这也叫做方法。
练习
罗辑经常会忘记自己在谈的对象的名字,因此要你去用对象去记录她们的信息。现在他给你了一些信息,要你马上拿一个对象存起来。记住,只能用一个对象存。(名字都是《三体》书里抄的)
要求声明一个 girls
对象,将变量存到同名的成员内,然后使用 console.log()
输出对象。
let name1 = "张珊"
let name2 = "陈晶晶"
2
下面是一个数组,你需要把其中的第二项和第四项交换,然后输出数组。(注意下标从零开始)
let myArray = [ 1, 4, 3, 2, 5, 6, 7];