在 JavaScript 中,除了六种基本数据类型之外,就只有一种引用数据类型 —— 对象。
我们在本章将学习自己自定义对象,并且使用它们。除了使用自己自定义对象以外,JavaScript 语言中已经自带了很多对象,我们将其称之为“内置对象”,我们将在本章学习内置对象中的数组,Set 和 Map。
而在本章不管是数组、Set、Map,还是对象,它们都表现出了一个共同的特征,可以按某种特定的结构存放大量的数据。这些数据存放在一起作为一个整体我们可以称它们是一个集合;而集合中数据间存在关系,这就说明数据就是有结构的,那么这个集合也可以叫做数据结构。
所谓数据结构,就是计算机存储和组织数据的方式。说得通俗一点,主要就是指将数据以什么样的结构存储到计算机里面。
在程序里面,最为常见的数据结构,就是数组,这种结构将多个数据有序的排列在一起,形成了一个集合。除了数组以外,Set,Map 等 ECMAScript 6 (下面简称ES6)新增加的数据结构,也会在本章中向大家详细介绍。
本章主要包含以下内容:
-
对象
-
数组基础
-
数组的常用方法
-
Set
-
Map
5-1 对象
JavaScript 中的对象(Object)是一组数据的无序集合。其中,每一条数据都是由键:值组成(例如:name:'HanMeiMei' )。其中,值可以是任意数据类型,而键是字符串类型。
5-1-1 定义对象
定义对象的方式有两种:
-
字面量方式:
let obj1 = {};
-
构造函数方式:
let obj2 = new Object();
let student = {
name: 'HanMeiMei',
age: 20
}-
通过点运算符
.
来操作一个对象的属性。let person = {
name: "lisi",
job: "SoftWare Engineer"
};
person.name = "zhangsan"; // 修改已有的属性
person.age = 30; // 添加没有的属性
console.log( person.job ); // 查看已有的属性
-
通过方括号
[]
操作一个对象的属性。let person = {
name: "lisi",
job: "SoftWare Engineer"
};
person["name"] = "zhangsan"; // 修改已有的属性
person["age"] = 30; // 添加没有的属性
console.log( person["job"] ); // 查看已有的属性 -
通过 delete 关键字来删除一个对象的属性。
let person = {
name: "lisi",
job: "SoftWare Engineer"
};
delete person.job; // 删除 person 对象的属性 joblet person = {
name: 'HanMeiMei',
introduce: function(){
console.log( 'my name is HanMeiMei' );
}
}-
通过点运算符
.
来访问一个对象的方法。let person = {
name: 'HanMeiMei',
introduce: function(){
console.log( 'my name is HanMeiMei' );
}
}
person.introduce(); // 调用 person 对象的 introduce 方法 -
通过方括号
[]
来访问一个对象的方法。let person = {
name: 'HanMeiMei',
introduce: function(){
console.log( 'my name is HanMeiMei' );
}
}
person["introduce"](); // 调用 person 对象的 introduce 方法 -
通过 delete 关键字来删除一个对象的方法。
let person = {
name: 'HanMeiMei',
introduce: function(){
console.log( 'my name is HanMeiMei' );
}
}
delete person.introduce; // 方法名后面没有小括号Symbol
let person = {
name
age
gender
introduce
console
}
}
person
console
}let foo = {
bar
console
}
}
foolet student = {
name
sayName
console
}
}
studentname
console
consolelet a = 1;
let b = 2;
a
// 简写
aname
console
console// 没有默认值
let { name } = {};
console
// 有默认值
let { name = "张三" } = {};
console
name
consolelet arr1 = [];
Array
let arr1 = [];
Array
console
consolelet arr = [];
arr
arr
arrlet arr2 = [3,4];// 创建一个长度为2的数组,数组中存放了两个值:3和4
Arraylet arr = [];
arr
arr
console
// [ 1, <3 empty items>, 10 ]let arr = [1,"Hello",3.14,true];
let arr = [1,2,3,4,5];
consolelet arr = [1,2,3,4,5];
let i = 2;
consolelet arr = [1,2,3,4,5];
arr
console
// [ 1, 2, <1 empty item>, 4, 5 ]数组名
let ary = [100, 200, 300];
// 获取长度
console
// 设置长度
ary
consolelet ary = [100, 200, ['a', 'b'], 300, 400];
let ary = [100, 200, ['a', 'b'], 300, 400];
// 查看
console
// 修改
ary
// 新增
ary
// 删除
arylet ary = [100, 200, 300, 400];
// i 即用来控制循环次数,也可用作数组的下标
i
console
}ary
console
}let ary = [100, 200, 300, 400];
// 创建一个变量 value 用来接收数组中的每一个值
ary
console
}let ary = [100, 200, 300];
ary
console
ary
consolelet ary = [100, 200, 300];
ary
console
ary
console数组名
let ary = [100, 200, 300];
ary
console数组名
数组名let ary = ['hello', 'world', 'hello', 'kitty'];
console
console数组名
let ary = ['hello', 'world', 'hello', 'kitty'];
console
console数组名
let ary = [100, 200, 300, 400];
ary
consolelet ary = [100, 300, 200, 500, 400];
ary
console
ary
console数组名
return a - b; // 从小到大
return b - a; // 从大到小
});let ary = [100, 300, 200, 500, 400];
ary
return a - b;
})
console
ary
return b - a;
})
console数组名
字符串名let ary = [100, true, 'hello'];
ary
console
let str = "hello, nice to meet you";
str
consolelet arr = [10, 11, 12, 13, 14];
arr
console
console
})let arr = [10, 11, 12, 13, 14];
arr
return item * 2;
})
consolelet arr = [10, 11, 12, 13, 14];
arr
return item % 2 == 0;
})
consolelet arr = [10, 11, 12, 13, 14];
arr
return prev + next;
})
consolelet arr = [1, 4, 2, 3, 7, 5];
arr
return prev > next ? prev : next;
})
consolelet arr = [10, 11, 12, 13, 14];
arr
return item % 2 == 0;
})
consolelet arr = [10, 11, 12, 13, 14];
arr
return item % 2 == 0;
})
consoleSet
Set
Set
consoleSet
// 添加数据 5
set
console
// 删除数据 4s
set
console
// 查看是否存在数据 4
set
console
// 清除所有数据
set
consoleSet
color
console
}
// red
// green
// blue
color
console
}
// red
// green
// blue
color
console
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
color
console
})
// red
// green
// blueSet
colorlet arr = [1, 1, 2, 2, 3, 3];
SetMap
consoleMap
consoleMap
// 设置 name 的值为 lisi
map
console
// 获取 name 对应的值
map
console
// 查看是否存在 age
map
console
// 删除 age 键值对
map
console
// 清空所有数据
map
consoleMap
num
console
}
// one
// two
// three
num
console
}
// 1
// 2
// 3
num
console
}
// one 1
// two 2
// three 3
// 将上面代码通过解构改成如下
num
console
}
// one 1
// two 2
// three 3
num
console
})
// 1 one
// 2 two
// 3 threeMap
myMap
set
set
console
myMap
consolelet arr = [[true, "真"], [false, "假"]];
Map
consolefunction strMapToObj(strMap){
let obj = {};
for(let [k, v] of strMap){
obj[k] = v;
}
return obj;
}
Map
console
consolefunction objToStrMap(obj){
Map
for(let item in obj){
set
}
return strMap;
}
name
console
// {"name" => "zhangsan", "age" => 20}对于数组,它的特点是有序,这就要求我们关注数组的索引;对于 Set ,它的特点是不重复,那我们就可以通过它来排除重复;对于 Map ,它的特点是键值对(k-v),如果要处理映射关系,我们可以考虑它。而不管是哪一个数据结构,除了学会它的基本操作“增删查改”以外,我们也要学会怎么去遍历它们。
其中,对象的创建,对象属性和方法的基本操作和使用是我们认识引用数据类型的知识基础。我们也要学会使用其它的数据结构,合理的选择,才能写出更好的代码。
本章主要为读者介绍了引用数据类型,以及一个新的概念数据结构。不管是对象,还是数组,Set 和 Map,它们都可以用来组织存放数据,我们把他们都叫做数据结构。
总结
对象转为 Map
如果 Map 所有的键都是字符串,它就可以转为对象。
Map 转为对象
将数组传入 Map 构造函数中,就可以转为 Map。
数组转为 Map**
图5.7 Map 类型变量使用扩展运算符
Map 转为数组最方便的方法,就是使用扩展运算符
...
。Map 转为数组
5-5-3 与其他数据结构互换
-
keys():返回一个键名的遍历器。
-
values():返回一个键值的遍历器。
-
entries():返回一个键值对的遍历器。
-
forEach():使用回调函数遍历每个成员。
Map 提供了三个遍历器生成函数和一个遍历方法:
-
set(key, value):添加或修改数据。设置 key 所对应的键值,并返回 Map 结构本身。
-
get(key):获取数据。读取 key 对应的键值,如果找不到 key,返回 undefined。
-
has(key):查看是否存在某个数据,返回一个布尔值。
-
delete(key):删除数据。删除成功返回 true。
-
clear():清除所有数据,没有返回值。
操作方法:
Map 实例的方法也分为了两大类:操作方法(用于数据操作)和遍历方法(用于遍历数据)。
方法
-
size:返回 Map 结构的成员总数。
属性
Map 结构的实例有以下属性和方法。
###5-5-2 Map 实例的属性和方法
Map 函数也可以接受一个数组(或类似数组的对象)作为参数,用来进行初始化。但是跟 Set 不同的是,Map 中该数组中的成员是一对对表示键值对的数组。
Map 本身是一个构造函数,在使用构造函数时,通过传入参数进行数据初始化
创建 Map
###5-5-1 基本用法
也就是说,Object 结构提供了“字符串 - 值”的对应,Map 结构提供了“值 - 值”的对应,是一种更完善的 JSON 数据结构的实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。
为了解决这个问题,ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
JavaScript 中的对象(Object),本质上是键值对的集合,但是只能用字符串来做键名。这给它的使用带来了很大的限制。
##5-5 Map
扩展运算符和 Set 结构相结合,就可以去除数组的重复成员。
数组剔重
例如将 Set 结构转换为数组结构:
由于扩展运算符...内部的原理也是使用的 for-of 循环,所以也可以用于操作 Set 结构。
Set 转数组
###5-4-3 与数组相关操作
上例代码中,entries() 方法返回的遍历器,同时包括了键名和键值,所以每次输出一个数组,它的两个成员完全相等。
说明:
-
keys():返回一个键名的遍历器。
-
values():返回一个键值的遍历器。
-
entries():返回一个键值对的遍历器。
-
forEach():使用回调函数遍历每个成员。
Set 提供了三个遍历器生成函数和一个遍历方法。
遍历方法:
-
add(value):添加数据,并返回新的 Set 结构。
-
delete(value):删除数据,返回一个布尔值,表示是否删除成功。
-
has(value):查看是否存在某个数据,返回一个布尔值。
-
clear():清除所有数据,没有返回值。
操作方法:
Set 实例的方法分为两大类:操作方法(用于数据操作)和遍历方法(用于遍历数据)。
方法
-
size
:返回 Set 实例的成员总数。
属性
Set 结构的实例有以下属性和操作方法。
###5-4-2 Set 实例的属性和方法
通过上面代码运行结果我们可以看到,Set 成员当中如果存在重复的值, 会被自动删除掉。
Set 函数可以接受一个数组(或类似数组的对象)作为参数,用来进行数据初始化。
数据初始化
Set 本身是一个构造函数,调用构造函数用来生成 Set 数据结构。
###5-4-1 基本用法
ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
##5-4 Set
方法名 描述 区别 性能(A表示最优) for 循环 遍历数组 可以使用 break、continue 中断循环 A for-of 遍历所有复杂数据类型 遍历所有数据的键名 B forEach() 遍历数组 不可以使用 break、continue 中断循环 C map() 遍历操作数组元素并返回新数组 返回结果为一个新数组 作用不同,未做性能比较。 filter() 遍历筛选数组元素并返回新数组 返回结果为一个新数组 作用不同,未做性能比较。 reduce() 遍历计算数组元素并累计返回最终值 返回结果为累积计算后的最终值 作用不同,未做性能比较。 every() 遍历检测数组元素,有一项不满足检测条件,则返回 false 返回结果为一个布尔值 作用不同,未做性能比较。 some() 遍历检测数组元素,有一项满足检测条件,则返回 true 返回结果为一个布尔值 作用不同,未做性能比较。 ###5-3-6 遍历方法汇总
图5.6 some 遍历
图解:
说明:检测的元素中,只要有一项符合条件,就返回结果 true,如果全部都不满足条件,则返回结果 false。即一真必真。
描述:按顺序让数组中每一项依次执行某种相同的操作,用来检测数组中某一项是否都符合条件。
some()*
图5.5 every 遍历
图解:
说明:检测的元素中,如果全部都满足条件,返回 true,只要有一项不满足条件,则返回 false。即一假必假。
描述:按顺序让数组中每一项依次执行某种相同的操作,用来检测数组中每一项是否都符合条件。
every()
利用 reduce() 方法的特点,我们还可以通过该方法结合三目运算来求一个数组中的最大或最小值。
图5.4 reduce 遍历
图解:
描述:按顺序让数组中的前项和后项做某种计算,并累计返回最终值。
reduce()*
图5.3 filter 遍历
图解:
描述:按顺序让数组中每一项依次执行某种相同的操作,然后筛选出符合条件的元素,组成新数组并返回。
filter()*
图5.2 map 遍历
图解:
描述:按顺序让数组中每一项依次执行某种相同的操作,然后返回一个新数组。
map()
-
数组自带的 forEach 循环,使用频率较高,但实际上性能比普通 for 循环弱。
-
forEach不能使用 continue 和 break 语句中断循环,也不能使用 return 语句返回到外层函数。
说明:
图5.1 for-each 遍历
图解:
描述:按顺序让数组中每一项依次执行某种相同的操作。
自 2009 年 ES5 正式发布后,我们开始可以使用 forEach 方法来遍历数组。
forEach()
我们讲了数组两种最基本的遍历方式:for 循环和 for-of 。除了这两种以外,数组还提供了很多遍历的方法。
###5-3-5 数组的遍历
例:
语法结构如下:
-
join:数组拼接成字符串
-
split:字符串分割成数组(这是字符串的方法)
JavaScript 中提供了一组方法用户数组和字符串之间的相互转换。
###5-3-4 数组和字符串转换
例:
如果想要按照数字标准进行排序,就需要提供一个比较函数作为参数。语法结构如下:
sort 方法说明:sort 方法如果省略参数,数组内的数据会先自动通过 toString 方法将值转换成字符串,再按照字符串的比较规则按照从小到大排序。
-
reverse:将所有数据顺序颠倒;
-
sort:根据给定条件对所有数据进行排序;
JavaScript 中提供了一组方法,用于操作数组内所有数据的顺序。
###5-3-3 数据排序
例:
说明:slice 用来查找从起始位置到结束位置(不包含)之间的所有数据。
数据结构:
JavaScript 中提供了 slice 方法用来查找数组中的一部分数据,并将找到的数据组成一个新数组。
查找一部分数据
例:
语法结构:
JavaScript 中提供了 includes 方法用来查找某一条数据在数组中是否存在。
查找一条数据是否存在
例:
语法结构:
-
indexOf:查找数据第一次出现的下标;
-
lastIndexOf:查找数据最后一次出现的下标;
JavaScript 中提供了两个方法用来查找某一条数据在数组中的下标。如果没找到对应数据,则返回
-1
。查找一条数据的下标
###5-3-2 查找数据
说明:如果不需要删除数据,则 splice 方法的第二个参数为 0。
例:
JavaScript 中提供了一个万能方法,用于对数组任意位置的数据进行增加或删除。语法结构如下:
数组任意位置增删数据
-
unshift:向数组开头增加多条数据;
-
shift:删除数组开头的一条数据;
JavaScript 中提供了一组方法,用于对数组开头的数据进行增加或删除。
数组开头增删数据
-
push:向数组末尾增加多条数据;
-
pop:删除数组末尾的一条数据;
JavaScript 中提供了一组方法,用于对数组末尾的数据进行增加或删除。
数组末尾增删数据
###5-3-1 增删数据
数组作为一个引用数据类型的对象,包含了很多用于操作数组的方法。这里主要介绍一些比较常用的方法。
##5-3 数组的常用方法
说明:效率上来说 for-of 还是比不上普通的 for 循环。但是 for-of 除了可以遍历数组外,还可以遍历大多数类数组对象。例如后面会讲到的 Set、Map。
ES6 中新增了一种遍历数组的方法 for-of,相比起 for 循环来说它的代码更加简洁。
for-of
对于 for 循环的执行流程来说,i < ary.length 这个表达式会反复执行很多次,也就是说,数组的长度每次都要去重新获取。优化版 for 循环就是在普通版 for 循环基础之上,使用临时变量,将数组的长度 ary.length 缓存起来,避免重复获取数组长度。代码如下:
优化版 for 循环
for 循环是遍历数组最简单的一种,也是使用频率最高的一种。
for 循环
数组的遍历就是指依次访问数组中每一项数据。
数组的作用是可以批量保存多条数据,但是当我们需要对数据进行操作时,并不能通过直接操作数组来实现效果,还是需要将每一条数据从数组中取出来,然后再依次取对其进行操作。
###5-2-7 数组的遍历
多维数组和普通数组的操作方式一样,都是通过下标来实现:
这种数组里面嵌套数组的结构,就叫做多维数组。
JavaScript 中的数组有一个特点是可以储存任意类型的数据。也就意味着,数组里面的数据也可以是数组。
###5-2-6 多维数组
小技巧:想要快速清空一个数组,就可以将数组的长度赋值为 0。
例:
语法结构:
数组的长度,即数组内保存的数据的条数。JavaScript 中为数组提供了一个 length 属性,用来设置或返回数组的长度。
###5-2-5 数组的长度
我们可以使用 delete 运算符来删除数组中的某一个元素,示例如下:
5-2-4 删除元素
除了这种常规的访问方式,我们还可以使用变量的方式来进行访问,如下:
需要注意数组里面的第一个元素是从下标 0 开始的。
通过数组的索引,我们可以轻松的访问到存储在数组里面的元素,如下:
5-2-3 访问数组元素
由于 JavaScript 是动态语言,所以 JavaScript 里面的数组无法限定其存放数据的数据类型,数组中元素的数据类型可以是任意类型,但是在应用过程中不推荐这样做。
我们可以在数组的任意位置进行赋值,数组的长度会自动改变,空的位置使用由于不存在,可以认为全部是 undefined。
声明时直接赋值
先声明再赋值
给数组赋值的方法也非常简单,不过可以分为先声明再赋值和声明时直接赋值,如下:
5-2-2 数组赋值
上面的两种方式都创建了一个空数组,需要注意的是,无论是字面量形式创建的数组,还是构造函数创建的数组,当我们使用 typeof 来打印其数据类型的时候,都会返回一个 object,如下:
构造函数创建数组
字面量创建数组
创建数组的方式大致可以分为两种:字面量创建数组和使用构造函数创建数组。示例如下:
5-2-1 数组的创建
在 JavaScript 中,组成数组的各个值(数据)称为数组中的元素,元素可以是任意数据类型的值,通常我们在同一个数组中只保存同种数据类型的值;数组中元素的个数称为数组的长度,长度为0的数组叫做空数组;数组中的每一个元素都有可以通过一个数字来访问,被称为索引(或称下标),数组索引从 0 开始,所以数组中最后一个元素的索引应该是数组的长度减一。
数组,英文翻译为 Array,是大多数语言里面最常见的一种数据结构,它是一个有序的值列表,或者说一个有序的数据集合。
5-2 数组基础
对象的解构赋值允许指定默认值(下面的示例都省略了 name: ,使用了 ES6 新语法)。
对象解构的默认值
所以,前面对象解构的例子我们还可以简化一下:
上面的例子,var { name: name, age: age } 中,“:”后面的·是 var 将要声明的变量,“:”前面的 name 指定了该变量在赋值符号右面的对象中取哪一个属性的值,在 ES6 中,如果属性名跟属性值的变量名相同时,可以简写。即:
对象的解构
###5-1-6 对象的扩展
对象方法中的 this 永远指向调用该方法时.或[]前的那个对象。如上例中 bar 方法的 this 就一定指向 foo 对象。
对象的方法中可以使用 this 关键字。
###5-1-5 对象方法中的 this
for-in 可以遍历所有的属性和方法,要注意的是,for-in 无法遍历通过 symbol 值定义的属性。
for-in
###5-1-4 对象的遍历
-
对象方法的操作类似于对象属性的操作,同样具有以下三种方式:
方法操作
上面代码中,introduce 键对应的值都是一个函数,因此这条数据我们就叫做 student 对象的方法。
当对象中某一条数据的键所对应的值是函数类型,则我们将这条数据叫做对象的方法。
###5-1-3 对象的方法
-
属性操作
上面代码中,name 和 age 两个键对应的值都不是函数,因此这两条数据我们就叫做 student 对象的属性。
当对象中某一条数据的键所对应的值是非函数类型,则我们将这条数据叫做对象的属性。
###5-1-2 对象的属性
-
-