目录
第7种数据类型,Symbol
Symbol 的值是唯一的,用来解决命名冲突的问题
Symbol 值不能与其他数据进行运算
Symbol 定义的对象属性不能使用for…in循环遍历,但是可以使用Reflect.ownKeys 来获取对象的所有键名
- 代码例:
// 创建Symbol
let s = Symbol()
console.log(s, typeof s) // Symbol() 'symbol'
// 创建Symbol时写入名称,相当于注释名
let s2 = Symbol('name')
let s3 = Symbol('name')
console.log(s2 === s3) // false,每个symbol都是唯一的,不会完全相等
// Symbol.for创建
let s4 = Symbol.for('age')
let s5 = Symbol.for('age')
console.log(s4 === s5) // true,Symbol.for创建的,只要其中的标识值相同,例:'age',那么被创建的symbol就完全相等
// 创建的symbol不能进行运算,以下操作都会报错
let res1 = s + 100
let res2 = s > 100
let res3 = s + '你好'
- Symbol创建对象属性
- 代码例:
let obj = {...}
// 在不知道以上对象中含有哪些属性时,使用Symbol对其创建属性
// 可以保证绝对的唯一性,且不会对原本对象里的属性造成影响
// 方法1
let methods = {
fn: Symbol(),
fn2: Symbol()
}
obj[methods.fn] = function() {
console.log('fn()')
}
obj[methods.fn2] = function() {
console.log('fn2()')
}
// 方法2
let obj2 = {
name: 'sky',
[Symbol('fn')]: function(){
console.log('fn()')
},
[Symbol('fn2')]: function(){
console.log('fn2()')
}
}
- Symbol内置的值
Symbol内置了11种值,这些值指向语言内部使用的方法,不同的方法会在特定的场景下自动执行。
方法名 | 说明 |
---|---|
Symbol.hasInstance | 当其他对象使用 instanceof 运算符,判断是否为该对象的实例时,会调用这个方法 |
Symbol.isConcatSpreadable | 对象的 Symbol.isConcatSpreadable 属性等于的是一个布尔值,表示该对象用于 Array.prototype.concat()时,是否可以展开。 |
Symbol.species | 创建衍生对象时,会使用该属性 |
Symbol.match | 当执行 str.match(myObject) 时,如果该属性存在,会调用它,返回该方法的返回值。 |
Symbol.replace | 当该对象被 str.replace(myObject)方法调用时,会返回该方法的返回值。 |
Symbol.search | 当该对象被 str.search (myObject)方法调用时,会返回该方法的返回值。 |
Symbol.split | 当该对象被 str.split(myObject)方法调用时,会返回该方法的返回值。 |
Symbol.iterator | 对象进行 for…of 循环时,会调用 Symbol.iterator 方法,返回该对象的默认遍历器 |
Symbol.toPrimitive | 该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。 |
Symbol. toStringTag | 在该对象上面调用 toString 方法时,返回该方法的返回值 |
Symbol. unscopables | 该对象指定了使用 with 关键字时,哪些属性会被 with环境排除。 |
迭代器,for…of
- 基本使用及实现原理
- 代码例:
// 声明数组
const arr = ['sky', 'tom', 'jack', 'joe']
// for...in
for(let i in arr){
console.log(i) // 返回数组索引下标,依次输出 0 1 2 3
}
// for...of
for(let j of arr){
console.log(j) // 返回数组内的每个值,依次输出 'sky' 'tom' 'jack' 'joe'
}
对象进行 for…of 循环时,会调用 Symbol.iterator 方法,返回该对象的默认遍历器
实现原理:
- 创建一个指针对象,指向当前数据结构的起始位置
- 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
- 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
- 每调用 next 方法返回一个包含 value 和 done 属性的对象
- 代码例:
// 声明数组
const arr = ['sky', 'tom', 'jack', 'joe']
// arr数组有个Symbol.iterator方法,我们用一个变量来接收这个方法的返回结果
let iterator = arr[Symbol.iterator]()
// 而这个返回对象内,有个next方法
// 执行该方法会依次指向该数据结构内的成员,返回一个包含value和done属性的对象
console.log(iterator.next()) // {value: 'sky', done: false}
console.log(iterator.next()) // {value: 'tom', done: false}
console.log(iterator.next()) // {value: 'jack', done: false}
console.log(iterator.next()) // {value: 'joe', done: false}
console.log(iterator.next()) // {value: undefined, done: true}
- 使用迭代器自定义遍历对象
- 代码例:
// 创建一个对象,包含数组
const obj1 = {
name: 'sky',
arr: ['joe', 'tom', 'jack', 'bob']
}
// 遍历该对象
for(let i of obj1){
console.log(i)
}
// 由于for...of遍历的是数组,无法直接遍历对象,因此以上代码会报错
// 我们需要在该对象中自定义需要遍历的数组,才能遍历返回正确结果
// 创建一个对象,包含数组
const obj2 = {
name: 'sky',
arr: ['joe', 'tom', 'jack', 'bob'],
// 在需要遍历的对象内,自定义创建Symbol.iterator方法
[Symbol.iterator]() {
// this指向
let _this = this
// index索引变量
let index = 0
// 需要遍历的数组长度
let length = this.arr.length
// Symbol.iterator方法内会返回一个对象
return{
// 返回对象内包含一个next方法
next(){
// next方法内自定义需要遍历的数组对象
if(index < length){
// next 方法返回一个包含 value 和 done 属性的对象
const result = {value: _this.arr[index], done: false}
// index自增
index++
// 返回对象
return result
}else{
return {value: undefined, done: true}
}
}
}
}
}
// 遍历该对象
for(let j of obj2){
console.log(j) // 返回对象内arr数组的值,依次输出 'joe', 'tom', 'jack', 'bob'
}
生成器函数
生成器就是一个特殊的函数
- 代码例:
// 创建一个生成器函数,*号靠左靠右都没关系
function * gen(){
console.log('hello')
}
// 直接执行该函数,内部语句是不执行的
gen()
// 生成器函数的返回结果就是一个迭代器对象
let iterator = gen()
// 用其中的next方法才能运行语句
console.log(iterator.next()) // 'hello'
// yield分隔符,可以用来控制分隔每次执行next方法时,所需要执行的语句
function * gen2(){
console.log(111) // 第一次next方法执行的语句
yield '第一段执行完毕'
console.log(222) // 第二次next方法执行的语句
yield '第二段执行完毕'
console.log(333) // 第三次next方法执行的语句
yield '第三段执行完毕,还有最后一段未执行'
console.log(444) // 第四次next方法执行的语句
}
let iterator2 = gen2()
console.log(iterator2.next()) // {value: '第一段执行完毕', done: false}
console.log(iterator2.next()) // {value: '第二段执行完毕', done: false}
console.log(iterator2.next()) // {value: '第三段执行完毕', done: false}
console.log(iterator2.next()) // {value: undefined, done: true}
- 生成器函数传参
- 代码例:
// 创建生成器函数
function * gen(arg){
console.log(arg) // 'gen执行时的参数'
// 创建变量,接收next方法内的实参
let next2Arg = yield 1
console.log(next2Arg) // '第2次next的参数'
let next3Arg = yield 2
console.log(next3Arg) // '第3次next的参数'
let next4Arg = yield 3
console.log(next4Arg) // '第4次next的参数'
}
let iterator = gen('gen执行时的参数')
iterator.next()
// 第2次next方法开始可以传入实参
// 该实参由当前next方法所执行语句的前一个yield所接受
iterator.next('第2次next的参数')
iterator.next('第3次next的参数')
iterator.next('第4次next的参数')
- 生成器函数-模拟请求数据
- 代码例:
// 请求用户数据
function getUsers() {
setTimeout(()=>{
let data = '用户数据'
// 传入用户数据,执行下个next
iterator.next(data)
},1000)
}
// 请求订单数据
function getOrders() {
setTimeout(()=>{
console.log(users) // '用户数据'
let data = '订单数据'
// 传入订单数据,执行下个next
iterator.next(data)
},1000)
}
// 请求商品数据
function getGoods() {
setTimeout(()=>{
console.log(orders) // '订单数据'
let data = '商品数据'
// 传入商品数据,执行下个next
iterator.next(data)
},1000)
}
// 创建生成器函数
function * gen() {
// users 用户数据实参
let users = yield getUsers()
// orders 订单数据实参
let orders = yield getOrders()
// goods 商品数据实参
let goods = yield getGoods()
}
// 创建迭代器对象并调用next
let iterator = gen()
iterator.next()
- Recorded by Scorpio_sky@2021-01-17