前言
1.本章讲的是ES6 object语法,map数据结构,set数据结构,object.assign
一、对象属性
1.简单属性(Property Shorthand)
①.ES5的做法: Object 的属性必须是 key-value 形式
var x = 0, y = 0;
obj = { x: x, y: y };
②.ES6的做法:简写的形式来表达
var x = 0, y = 0
obj = { x, y }
2.计算属性名字(Computed Property Names)
①.ES5的做法:Object 的 key 在定义时必须是字符串,如果想增加“动态”的 key,必须是先计算出 key,利用 object[key] = value
的方式来修改。
var obj = {
foo: 'bar'
}
obj['baz' + quux()] = 42
②.ES6的做法:直接用变量或者表达式来定义 key
let obj = {
foo: 'bar',
['baz'+ quux()]: 42
}
3.方法属性(Method Properties):
①ES5的做法:ES5 时代 Object 的属性只支持常规函数,对于异步函数是不支持的。
var obj = {
foo: function (a, b) {
},
bar: function (x, y) {
}
// quux: no equivalent in ES5
}
②ES6的做法:从 ES6 开始对象内的方法可以简写,包括常规函数和异步函数。
let obj = {
foo (a, b) {
},
bar (x, y) {
},
* quux (x, y) {
}
}
二、Set数据结构与Map数据结构
JavaScript 里通常使用 Array 或 Object 来存储数据。但是在频繁操作数据的过程中查找或者统计并需要手动来实现,并不能简单的直接使用。
比如如何保证 Array 是去重的,如何统计 Object 的数据总数等,必须自己去手动实现类似的需求,不是很方便。
在 ES6 中为了解决上述痛点,新增了数据结构 Set 和 Map,它们分别对应传统数据结构的“集合”和“字典”。
1.Set数据结构
①.写法:
let s = new Set([1, 2, 3, 4])
初始化的参数必须是可遍历的,可以是数组或者自定义遍历的数据结构。
②.添加数据
s.add('hello')
s.add('goodbye')
或链式添加
s.add('hello').add('goodbye')
说明:Set 数据结构不允许数据重复,所以添加重复的数据是无效的。
③.删除数据
(1).删除指定数据
// 删除指定数据
s.delete('hello') // true
(2).清除数据
s.clear()
④.统计数据
Set 可以快速进行统计数据,如数据是否存在、数据的总数。
// 判断是否包含数据项,返回 true 或 false
s.has('hello') // true
// 计算数据项总数
s.size // 2
⑤.查询数据
- keys():返回键名的遍历器
- values():返回键值的遍历器
- entries():返回键值对的遍历器
- forEach():使用回调函数遍历每个成员
- for…of:可以直接遍历每个成员
console.log(s.keys()); // SetIterator {"hello", "goodbye"}
console.log(s.values()); // SetIterator {"hello", "goodbye"}
console.log(s.entries()); // SetIterator {"hello" => "hello", "goodbye" => "goodbye"}
s.forEach(item => {
console.log(item) // hello // goodbye
})
for (let item of s) {
console.log(item)
}
// hello
// goodbye
说明:
1.Set 对存储的类型是不限的,如果想限制只能存储对象,该怎么办?请使用 WeakSet!
2.因为 Set 中的值总是唯一的,所以需要判断两个值是否相等,那么 +0,-0,NaN,undefined可以被重复添加吗?
答案是不能重复添加的,本人试过
let oSet = new Set([+0,-0,3,undefined,NaN]) oSet.add('hello') oSet.add(undefined) oSet.add(-0) oSet.add(+0) oSet.add(NaN) console.log(oSet)
结果:
2.Map数据结构
①实例化
let map = new Map([iterable])
即里面可以这么写:
let map = new Map([["123","1234"],[666,"0987"],[666,"qua"]])
说明:
(1).Iterable 可以是一个数组或者其他 iterable 对象,其元素为键值对(两个元素的数组,例如: [[ 1, ‘one’ ],[ 2, ‘two’ ]])。 每个键值对都会添加到新的 Map。null 会被当做 undefined。
(2).同一个键,则以新的为打印数据,如上面[666,"0987"],[666,"qua"],只会打印[666,"qua"]
②添加数据
let map = new Map([["123","1234"],[666,"0987"],[666,"qua"]])
// 添加数据,说明数据是不能被重复的
console.log('添加数据')
let keyObj ={}
let keyFunc = function(){}
let keyString = "a string"
// 说明键可以是各种类型的,包括字符串,函数,对象
map.set(keyObj, "obj")
map.set(keyFunc, "函数")
map.set(keyString, "字符串")
console.log(map)
结果:
③删除数据
//删除数据
console.log('删除数据')
map.delete(keyObj)
map.delete("123")
console.log(map)
结果:
说明:如果清除数据可以用map.chear(),这里不做演示。
④.统计数据
//统计数据
console.log(map.size)
//判断是否有key-val
console.log(map.has(666))
结果:
⑤.查询数据
- get() 方法返回某个 Map 对象中的一个指定元素
- keys() 返回一个新的 Iterator 对象。它包含按照顺序插入 Map 对象中每个元素的 key 值
- values() 方法返回一个新的 Iterator 对象。它包含按顺序插入Map对象中每个元素的 value 值
- entries() 方法返回一个新的包含 [key, value] 对的 Iterator ?对象,返回的迭代器的迭代顺序与 Map 对象的插入顺序相同
- forEach() 方法将会以插入顺序对 Map 对象中的每一个键值对执行一次参数中提供的回调函数
- for…of 可以直接遍历每个成员
console.log(map.get(keyObj)) // 和键keyObj关联的值
console.log(map.keys()) // Iterator
console.log(map.values()) // Iterator
console.log(map.entries()) // Iterator
map.forEach((value, key, map) => {
console.log(value, key, map)
}, thisArg)
for ([key, value] of map) {
console.log(key, value)
}
说明:其实 Object 也是按键值对存储和读取的,那么他俩之间除了我们之前说的区别以外还有其他的吗?
键的类型
一个Object的键只能是字符串或者 Symbols,但一个 Map 的键可以是任意值,包括函数、对象、基本类型。
键的顺序
Map 中的键值是有序的,而添加到对象中的键则不是。因此,当对它进行遍历时,Map 对象是按插入的顺序返回键值。
键值对的统计
你可以通过 size 属性直接获取一个 Map 的键值对个数,而 Object 的键值对个数只能手动计算。
键值对的遍历
Map 可直接进行迭代,而 Object 的迭代需要先获取它的键数组,然后再进行迭代。
性能
Map 在涉及频繁增删键值对的场景下会有些性能优势。
三、Object.assign()
Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象,它将返回目标对象。
语法:
Object.assign(target, …sources)
target目标对象(必选)
sources源对象(可选)
代码1:
const target = { a: 1, b: 2 }
const source = { b: 4, c: 5 }
const returnedTarget = Object.assign(target, source)
console.log(target)
// expected output: Object { a: 1, b: 4, c: 5 }
console.log(returnedTarget)
// expected output: Object { a: 1, b: 4, c: 5 }
说明:
1.从语法上可以看出源对象的个数是不限制的(零个或多个),如果是零个直接返回目的对象,如果是多个相同属性的会被后边的源对象的属相覆盖。
2.如果目的对象不是对象,则会自动转换为对象,如下代码
代码:
let t = Object.assign(2) // Number {2} let s = Object.assign(2, { a: 2 }) // Number {2, a: 2}
题目:
1.如果目标对象传入的是 undefined 和 null 将会怎样呢?
则会报错,如图
2.如果源对象的参数是 undefined 和 null 又会怎样呢?
没有问题
3.如果目标对象是个嵌套的对象,子对象的属性会被覆盖吗?
const target = { a: 1, b: { c: { d: { e: { f : 1, m : 2 } } } } } const source = { b : 4, b: { c: { d: { e: { f : 2, } } } } } const returnedTarget = Object.assign(target, source) console.log(target)
结果:
但是这也是问题原因,这样子拷贝将原来有的数据也变成没有了,如上面的m:2,这是不符合这个方法的原理的
说明:Object.assign()只是一个浅拷贝,是引用类型的,因为对应的指针都是指向一个地方的,所以数据都会发生变化,如果想实现应有的效果则必需使用深拷贝(即递归拷贝)。深拷贝查阅我另一篇文章。深拷贝