上一篇:28.ES6+学习(一)
目录
新增数据类型(了解)
Symbol
什么是Symbol
ES5 的对象属性名都是字符串,这容易造成属性名的冲突。比如,你使用了一个他人提供的对象,但又想为这个对象添加新的方法,新方法的名字就有可能与现有方法产生冲突。如果有一种机制,保证每个属性的名字都是独一无二的就好了,这样就从根本上防止属性名的冲突。这就是 ES6 引入Symbol
的原因。
/* 问题:属性名重名冲突 */
function fn(obj) {
// 想给obj添加一个新属性 =》 问题是传入的obj可能已经有了name属性
obj.name = 'atguigu'
obj.foo()
}
const obj = {
name: 'abc',
age: 12,
foo () {
console.log(this.name, this.age)
}
}
fn()
ES6 引入了一种新的原始数据类型Symbol
,表示独一无二的值。它是 JavaScript 语言的第七种数据类型
Symbol的使用
-
Symbol 值通过
Symbol
函数生成。 -
这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol 类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突
const s1 = Symbol() const s2 = Symbol('a') const s3 = Symbol('b') console.log(s1) // Symbol() console.log(s2) // Symbol(a) console.log(s3) // Symbol(b) console.log(typeof s2) // 'symbol' console.log(s2===s3) // false console.log(s2.toString()) // 'Symbol(a)'
Symbol表示独一无二的属性名
/* symbol值作为独一无二的属性名 */
const obj = {
name: 'jack',
foo () {
console.log('foo()')
}
}
const name = Symbol('name')
const foo = Symbol('foo')
obj[name] = 'jack222'
obj[foo] = () => {
console.log('foo22')
}
console.log(obj.name, obj[name])
obj.foo()
obj[foo]()
Symbol的注意事项
-
Symbol中传入的字符串没有任何意义,只是用来描述Symbol的
const obj1 = { name: "lily",//原有对象拥有name属性 age: 18, } const name = Symbol("name") const age = Symbol("age") obj1[name] = "xiaowang" obj1[age] = 21 console.log(obj1)
-
Symbol不能使用new调用
new Symbol() // TypeError: Symbol is not a constructor
-
类型转换的时候,能转换为字符串和布尔值,不能转数字
console.log(String(Symbol("a"))) // Symbol(a) console.log(Boolean(Symbol())) // true // console.log(Symbol() + 1) // TypeError: Cannot convert a Symbol value to a number // console.log(Number(Symbol())) // TypeError: Cannot convert a Symbol value to a number
-
如果把Symbol当作一个对象的属性名时,一定要用一个变量来储存,否则定义的属性没有办法引用
{ const obj = { name: "lily", age: 18, } obj[Symbol()] = "再见" // 如果直接设置,则再也获取不到这个属性了 console.log(obj) console.log(obj[Symbol()]) // undefined }
BigInt
-
JavaScript 所有数字都保存成 64 位浮点数,这给数值的表示带来了两大限制。一是数值的精度只能到 53 个二进制位(相当于 16 个十进制位),大于这个范围的整数,JavaScript 是无法精确表示的,这使得 JavaScript 不适合进行科学和金融方面的精确计算。二是大于或等于2的1024次方的数值,JavaScript 无法表示,会返回
Infinity
。 -
ES2019 引入了一种新的数据类型 BigInt(大整数),来解决这个问题。BigInt 只用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。
-
typeof
运算符对于 BigInt 类型的数据返回bigint
。 -
为了与 Number 类型区别,BigInt 类型的数据必须添加后缀
n
。 -
也可以通过BigInt函数将一般整数转换为大整数
// 大整数 const n1 = 123n console.log(n1, typeof n1) // 123n "bigint" const n2 = 123 console.log(n1===n2) // false // 一般整数转化为大整数 const n3 = BigInt(n2) console.log(n3, n3===n1) // 123n true const max = Number.MAX_SAFE_INTEGER console.log(max) console.log(max + 1) console.log(max + 2) // 不再变大, 不再正确 console.log(BigInt(max)) console.log(BigInt(max) + 1n) console.log(BigInt(max) + 2n) // 可以正确表示
新增数据结构 (次重要)
Map
什么是Map
-
JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。
-
为了解决这个问题,ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。
/* 为什么会有Map,因为对象属性名称必须是字符串,如果是其他类型也会自动转为字符串 */ const p1 = { name: "lily" }; const obj = { id: 1, [p1]: "good" // 最终属性名是p1.toString()的结果:'[object Object]' } console.log(obj) const map = new Map() map.set('name', 'tom') map.set(p1, 123) // key为p1对象 console.log(map)
Map的属性和方法
-
size
属性返回 Map 结构的成员总数。 -
set
方法设置键名key
对应的键值为value
,然后返回整个 Map 结构。如果key
已经有值,则值会被更新。set
方法返回的是当前的Map
对象,因此可以采用链式写法。 -
get
方法读取key
对应的键值,如果找不到key
,返回undefined
。 -
has
方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。 -
delete
方法删除某个键,返回true
。如果删除失败,返回false
。 -
clear
方法清除所有成员,没有返回值。 -
forEach()
:遍历 Map 的所有成员。 -
keys()
:返回键名的遍历器。 -
values()
:返回键值的遍历器。 -
entries()
:返回所有成员的遍历器。const p1 = { name: "lily" } const map = new Map([ ['name','tom'], [p1, 123] ]) /* - `size`属性返回 Map 结构的成员总数。 - `set`方法设置键名`key`对应的键值为`value`,然后返回整个 Map 结构。如果`key`已经有值,则值会被更新。`set`方法返回的是当前的`Map`对象,因此可以采用链式写法。 - `get`方法读取`key`对应的键值,如果找不到`key`,返回`undefined`。 - `has`方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。 - `delete`方法删除某个键,返回`true`。如果删除失败,返回`false`。 - `clear`方法清除所有成员,没有返回值。 - `forEach()`:遍历 Map 的所有成员。 */ console.log(map, map.size) map.set(() => {}, 12) console.log(map.get(p1)) console.log(map.has(p1), map.has(() => {})) map.delete('name') map.forEach((value, key) => console.log(value, key)) map.clear() console.log(map.size)
Set
什么是Set
-
ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
-
Set
本身是一个构造函数,用来生成 Set 数据结构。 -
Set
函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化let st = new Set([1, 2, 3, 4, 4, 3, 3, 1, 5]); console.log(st);
Set的属性及方法
-
size 返回set的长度
-
add 添加某个值,返回 Set 结构本身。
-
delete 删除某个值,返回一个布尔值,表示删除是否成功。
-
has 返回一个布尔值,表示该值是否为
Set
的成员 -
clear 清除所有成员,没有返回值。
-
forEach()
:遍历每个成员 -
keys()
:返回包含键名的遍历器,由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys
方法和values
方法的行为完全一致。 -
values()
:返回键值的遍历器 -
entries()
:返回键值对的遍历器const set = new Set([2, 3, 4, 4, 3, 3, 1, 5]) console.log(set) /* - size 返回set的长度 - add 添加某个值,返回 Set 结构本身。 - delete 删除某个值,返回一个布尔值,表示删除是否成功。 - has 返回一个布尔值,表示该值是否为`Set`的成员 - clear 清除所有成员,没有返回值。 - `forEach()`:遍历每个成员 */ console.log(set.size) set.add('abc') set.add(3) console.log(set, set.size) set.delete(6) console.log(set, set.size) console.log(set.has(5)) console.log(set.has(6)) set.forEach(value => console.log(value)) set.clear() console.log(set, set.size)
应用:利用Set去重
// 数组去重
[...new Set(array)]
// 字符串去重
[...new Set('ababbc')].join('')
// 'abc'
新增的遍历方式 (了解)
什么是Iterator
-
JavaScript 原有的表示“集合”的数据结构,主要是数组(
Array
)和对象(Object
),ES6 又添加了Map
和Set
。这样就有了四种数据集合,用户还可以组合使用它们,定义自己的数据结构,比如数组的成员是Map
,Map
的成员是对象。这样就需要一种统一的接口机制,来处理所有不同的数据结构。 -
遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。
-
Iterator 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创造了一种新的遍历命令
for...of
循环,Iterator 接口主要供for...of
消费。/* 自定义遍历器演示 */ const arr = [1, 2, 3]; function iterator() { //用一个计数器,保存现在处理到哪里了 let index = 0; return { next: () => { if (index < arr.length) { return { value: arr[index++], done: false } } else { return { done: true } } } } } //只要调用这个iterator函数,就可以依次得到成员了 const it = iterator(arr) console.log(it) let {value, done} = it.next() while(!done) { console.log(value) const item = it.next() value = item.value done = item.done }
使用ES6内置的iterator
-
一种数据结构只要部署了Iterator接口,我们就称这种数据结构是可以迭代的
-
在ES6中,只要数据结构具有了Symbol.iterator属性,那么就认为是可以迭代的
-
在ES6中,很多数据结构都部署了iterator接口(string,Array,set,Map)
-
应用场景:
- 解构赋值的时候默认调用iterator接口
- 扩展运算符使用的时候也默认调用iterator接口
- for of 使用的是iterator接口
-
注意:object对象是没有部署Iterator接口
/* 遍历数组 */ const arr = [1, 2, 3, 4, 5] console.log(arr[Symbol.iterator]) //arr[Symbol.iterator] 就是values函数 // 得到数组对应的遍历器对象 let it = arr[Symbol.iterator]() console.log(it) let {value, done} = it.next() while(!done) { console.log(value) const item = it.next() value = item.value done = item.done } /* 遍历字符串 */ const str = "abc"; const it = str[Symbol.iterator](); let {value, done} = it.next() while(!done) { console.log(value) const item = it.next() value = item.value done = item.done }
for…of
-
ES6 借鉴 C++、Java、C# 和 Python 语言,引入了
for...of
循环,作为遍历所有数据结构的统一的方法。 -
一个数据结构只要部署了
Symbol.iterator
属性,就被视为具有 iterator 接口,就可以用for...of
循环遍历它的成员。也就是说,for...of
循环内部调用的是数据结构的Symbol.iterator
方法。 -
for...of
循环可以使用的范围包括字符串、数组、Set 和 Map 结构、某些伪数组对象(比如arguments
对象、DOM的NodeList 对象)、后文的 Generator 对象。/* 使用for...of 遍历string/array/set/map */ const str = 'abcd' for (const value of str) { console.log(value) } for (const index in str) { console.log(index, str[index]) } console.log('-------------------------------') const arr = ['a', 'b', 'c'] for (const item of arr) { console.log(item) } for (const index in arr) { console.log(index, arr[index]) } console.log('----------------------------------') const set = new Set(['a', 'b', 'c']) for (const value of set) { console.log(value) } // for (const item in set) { // for...in 不能遍历set // console.log(item) // } console.log('----------------------------------') const map = new Map([['a',1], [2, 'b']]) for (const entry of map) { console.log(entry) } // for (const item in map) { // for...in 不能遍历map // console.log(item) // } console.log('----------------------------------') const obj = {a: 1, b: 3} // for (const item of obj) { // TypeError: obj is not iterable // console.log(item) // } for (const key in obj) { console.log(key, obj[key]) }
区别for…of 与 for…in
- for…in, ES6之前就有语法, 可以用来遍历字符串、object对象和数组,注意:不能遍历set和map
- for…of, 是ES6才有的语法, 可以遍历所有实现了Iterator接口的数据,包括:字符串,数组,set,map、arguments、NodeList。注意不能遍历object对象
类语法 (重要)
类/class的由来
-
JavaScript 语言中,生成实例对象的传统方法是通过构造函数
/* 通过构造函数创建实例 */ // 定义构造函数 function Person(name, age) { this.name = name this.age = age } // 给原型对象添加方法 Person.prototype.sayInfo = function () { console.log(`我叫${this.name}, 今年${this.age}`) } // 添加静态属性 Person.staticProp = 'abc' // 创建实例对象 const p1 = new Person('tom', '12') p1.sayInfo() console.log(Person.staticProp)
-
上面这种写法跟传统的面向对象语言(比如 C++ 和 Java)差异很大,很容易让新学习这门语言的程序员感到困惑。
-
ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过
class
关键字,可以定义类。 -
基本上,ES6 的
class
的本质就是构造函数, 可以看作是一个语法糖,新的class
写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。 -
constructor
方法是类的默认方法,通过new
命令生成对象实例时,自动调用该方法。一个类必须有constructor
方法,如果没有显式定义,一个空的constructor
方法会被默认添加。 -
构造函数的静态的方法或属性 直接用构造函数调用 使用static定义 ,如果不加static 则声明的属性和方法是构造器的
/* 通过类创建实例 */ class Person2 { // 添加静态属性 static staticProp = 'abcdef' // 构造器 ==》相当于构造函数 constructor (name, age) { this.name = name this.age = age } // 定义原型上的方法 sayInfo () { console.log(`偶叫${this.name}, 今年${this.age}`) } } const p2 = new Person2('jack', 13) p2.sayInfo() console.log(Person2.staticProp) /* 类的本质也就构造函数 */ console.log(typeof Person2, p2) console.log(p1)
类的继承
-
在ES5中,我们通常这么写
/* 基于构造函数的继承 */ function Student (name, age, price) { Person.call(this, name, age) // 借用父类型构造函数 this.price = price } Student.prototype = new Person() // 子类型的原型对象为父类型实例 Student.prototype.constructor = Student // 子类型原型对象的构造器为子类型 Student.prototype.sayInfo = function () { console.log(`我叫${this.name}, 今年${this.age}, 就业薪资${this.price}`) } const s1 = new Student('bz', 21, 15000) console.log(s1.name, s1.age, s1.price) s1.sayInfo()
-
class 可以通过
extends
关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多 -
ES6中继承的子类中,constructor中必须显示执行super()来调用父类型的构造器,用来初始化子类型对象的属性
/* 基于类的继承 */ class Student2 extends Person2 { constructor (name, age, price) { super(name, age) this.price = price } // 重写从父类型继承的方法 sayInfo () { console.log(`偶叫${this.name}, 今年${this.age}, 就业薪资${this.price}`) } } const s2 = new Student2('lz', 22, 14000) console.log(s2.name, s2.age, s2.price) s2.sayInfo()
下一篇:Promise学习