目录
Symbol
- ES6 引入的一种新的数据类型,表示独一无二的值
- JavaScript 语言的第七种数据类型,类似于字符串
- 通过函数 Symbol() 定义 Symbol 类型的数据
Symbol 特点:
- Symbol 的值是唯一的,用来解决命名冲突的问题
- Symbol 值不能与其他数据进行运算
- Symbol 定义的对象属性不能使用 for...in 遍历循环
- 但是可以使用 Reflect.ownKeys 来获取对象的所有键名
// 创建 SYmbol
let s = Symbol()
console.log(s, typeof s)
let pername1 = Symbol('张三')
let pername2 = Symbol('张三')
// 括号内的张三仅仅是 Symbol 的标志,并不是值
// 两个张三的编号并不相同
console.log(pername1 === pername2) // false
// 使用 Symbol.for 方法创建
// 此时的 Symbol 是一个对象,这样的数据称为函数对象
// 此种方法中,根据传入的参数,可以创建一个唯一的 Symbol 值
let pername3 = Symbol.for('罗翔')
let pername4 = Symbol.for('罗翔')
console.log(pername3)
console.log(pername3 === pername4) // true
// Symbol 不能与其他数据进行计算
// 不能与任何数据包括自身进行计算
// 包括字符串拼接、数字计算,比较等等
let result = s + 100 // 语法错误
let result = s > 100 // 语法错误
let result = s + s // 语法错误
对象添加 Symbol 类型的属性
- 给对象添加属性和方法,表示该对象和属性是独一无二的
// 向对象添加方法 up down
let game = {
name: '超级马里奥',
up: function () {
console.log('跳跃')
},
down: function () {
console.log('趴下')
},
}
// 声明一个对象
// 第一种添加方式
let methods = {
// 利用 Symbol 给对象添加属性和方法,简单高效且安全
up: Symbol(),
down: Symbol(),
}
game[methods.up] = function () {
console.log('好好学习,天天向上')
}
game[methods.down] = function () {
console.log('不好好学习,天天向下')
}
console.log(game)
// 第二种添加方式
let cards = {
name: '狼人杀',
[Symbol('say')]: function () {
console.log('轮到我发言了')
},
[Symbol('eye')]: function () {
console.log('天黑请闭眼')
},
}
console.log(cards)
Symbol 的内置属性
- 除了定义自己使用的 Symbol 值以外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法
- hasInstance、isConcatSpreadable 等都是 Symbol 的属性
- 而这个属性又在与 Symbol 组成一个整体作为对象的属性
- 通过对这些属性的设置,可以改变对象在特定场景下的表现结果
属性 | 含义 |
---|---|
Symbol.hasInstance | 当其他对象使用 instanceof 运算符,判断是否为该对象的实例时,会调用这个方法 |
Symbol.isConcatSpreadable | 对象 Symbol.isConcatSpreadable 属性等于的是一个布尔值,表示该对象用于 Array.prototype.concat() 时,是否可以展开 |
Symbol.unscopables | 该对象指定了使用 with 关键字时,哪些属性会被 with 环境排除 |
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.toPrimitive | 该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值 |
Symbol.toStringTag | 在该对象上面调用 toString 方法时,返回该方法的返回值 |
Symbol.species | 创建衍生对象时,会使用该属性 |
Symbol.hasInstance
class Person {
static [Symbol.hasInstance](param) {
console.log(param)
console.log('此方法被用来检测类型')
// 使用以下两条语句控制检测结果
// return true;
// return false;
}
}
// 当其他对象使用 instanceof 运算符,判断是否为该对象的实例时(特定的场景)
// 调用 hasInstance 方法(自动调用)
// 将 instanceof 前面的值 country 传递给 Person 方法,判断 Person 方法中是否存在该实例
let country = {}
console.log(country instanceof Person)
Symbol.isConcatSpreadable
- 当数组进行合并( Array.prototype.concat())的时候,isConcatSpreadable 判断该数组是否可以展开
const arr = [1, 2, 3]
const arr1 = [4, 5, 6]
const arr2 = [7, 8, 9]
// 当值为 false 时表示该数组不展开作为一个整体与 arr 进行合并
arr1[Symbol.isConcatSpreadable] = false // (4) [1, 2, 3, Array(3)]
console.log(arr.concat(arr1))
// 当值为 true 时表示该数组展开然后再与 arr 进行合并
// 此时相当于直接进行数组合并 arr.concat(arr2)
arr2[Symbol.isConcatSpreadable] = true // (4) [1, 2, 3, Array(3)]
console.log(arr.concat(arr2)) // (6) [1, 2, 3, 7, 8, 9]
JavaScript 数据类型总结
- USONB
- you are so niubility 你是如此的牛掰
- U undefined
- S string symbol
- O object
- N null
- B boolean
迭代器
迭代器简介
迭代器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署了 Iterator 接口,就可以完成遍历操作
1. ES6 创造了一种新的遍历命令 for...of 循环,Iterator 接口主要供 for...of 消费
2. 原生具备 Iterator 接口的数据(可用 for...of 遍历)
- Array
- Arguments
- Set
- Map
- String
- TypeArray
- NodeList
3. 工作原理
- 创建一个指针对象,指向当前数据结构的初始位置
- 第一次调用对象的 next 方法,指针自动指向数据结构的第一个成员
- 接下来不断调用 next 方法,指针一直往后移动,直到指向最后一个成员
- 每调用 next 方法返回一个包含 value 和 done 属性的对象
注:需要自定义遍历数据的时候,需要想到迭代器
// 声明一个数组
const xiyou = ['唐僧', '孙悟空', '猪八戒', '沙僧']
// for...in 循环保存的是键名
// 0 1 2 3
for (let i in xiyou) {
console.log(i)
}
// 使用 for...of 遍历数组
// for...of 循环保存的是键值
// 唐僧 孙悟空 猪八戒 沙僧
for (let v of xiyou) {
console.log(v)
}
// 只要对象中包含 Symbol.iterator 属性,就可以使用 for...of 遍历
// Symbol(Symbol.iterator): ƒ values() 该属性对应的值是一个函数
console.log(xiyou)
let iterator = xiyou[Symbol.iterator]()
console.log(iterator) // next: ƒ next()
// 调用对象的 next 方法
console.log(iterator.next()) // {value: "唐僧", done: false}
console.log(iterator.next()) // {value: "孙悟空", done: false}
console.log(iterator.next()) // {value: "猪八戒", done: false}
console.log(iterator.next()) // {value: "沙僧", done: false}
console.log(iterator.next()) // {value: undefined, done: true}
// done 表示循环是否完成,true 表示循环完成,false 表示循环继续
迭代器的应用
自定义遍历数据
// 声明一个对象
const xiaoclass = {
name: '终极幼儿园',
stus: ['小埼玉', '小悟空', '小迪迦', '小霸天虎'],
// 自定义遍历数组
[Symbol.iterator]() {
let index = 0
// 第一种解决 this 指向的方法
_this = this
// 第二种解决 this 指向的方法: 将 next 函数设置为箭头函数
return {
next: function () {
if (index < _this.stus.length) {
const result = {
value: _this.stus[index],
done: false,
}
// 下标自增
index++
// 返回结果
return result
} else {
return {
value: undefined,
done: true,
}
}
},
}
},
}
// 遍历这个对象
// 使用 for...of 遍历,每次返回的结果是数组 stus 中的成员
for (let v of xiaoclass) {
console.log(v)
}
核心代码
// 自定义遍历数组
[Symbol.iterator]() {
let index = 0;
// 第一种解决 this 指向的方法
_this = this;
// 第二种解决 this 指向的方法: 将 next 函数设置为箭头函数
return {
next: function() {
if (index < _this.stus.length) {
const result = {
value: _this.stus[index],
done: false
};
// 下标自增
index++;
// 返回结果
return result;
} else {
return {
value: undefined,
done: true
}
}
}
};
}
生成器
生成器函数声明与调用
生成器函数是 ES6 提供的一种异步编程解决方案,语法与传统函数完全不同
// 生成器其实就是一个特殊的函数,用于异步编程
// 以前的异步编程就是单纯的回调函数
function* gen() {
console.log('Hello generator')
}
let iterator = gen()
// 直接调用函数无法执行
console.log(iterator) // 返回一个生成器 Generator
// 使用 next 方法执行函数
iterator.next() // Hello generator
// 生成器函数中可以有 yield 语句
// yield 相当于函数代码的分隔符,将函数代码分割成几块
// 有 n 个 yield,函数代码就被分割为 n+1 块
function* gen1() {
console.log(111)
yield '一只没有耳朵'
console.log(222)
yield '一只没有尾巴'
console.log(333)
yield '真奇怪'
console.log(444)
}
let iterator1 = gen1()
// 此方法通过 next 方法控制代码的执行进程
// 一个 next, 执行一个输出函数
iterator1.next() // 111
iterator1.next() // 222
iterator1.next() // 333
iterator1.next() // 444
function* gen2() {
yield '一只没有耳朵'
yield '一只没有尾巴'
yield '真奇怪'
}
let iterator2 = gen2()
for (let v of gen2()) {
// 每一次调用返回的结果是 yield 后面表达式的结果或者自变量的值
console.log(v)
}
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}
生成器函数参数传递
- next 方法可以传入实参,此实参就是整个 yield 语句的返回结果
- 每一次 next 调用传入的实参,都将作为上一个 yield 语句整体的返回结果
function* gen(args) {
console.log(args)
let one = yield 111
console.log(one)
let two = yield 222
console.log(two)
let three = yield 333
console.log(three)
}
// 执行获取迭代器对象
let iterator = gen('AAA')
console.log(iterator.next())
// next 方法可以传入实参
// 此实参就是整个 yield 语句的返回结果
// 第二次 next 调用传入的实参将作为第一个 yield 语句整体的返回结果
console.log(iterator.next('BBB'))
// 第三次 next 调用传入的实参将作为第二个 yield 语句整体的返回结果
console.log(iterator.next('CCC'))
// 第四次 next 调用传入的实参将作为第三个 yield 语句整体的返回结果
console.log(iterator.next('DDD'))
生成器函数实例1
// 异步编程 文件操作,网络操作(Ajax,request),数据库操作
// 1s 后控制台输出 111,2s 后控制台输出 222,3s 后控制台输出 333
// 代码缩进将不断向后,此现象称为回调地狱
// setTimeout(() => {
// console.log(111);
// setTimeout(() => {
// console.log(222);
// setTimeout(() => {
// console.log(333);
// }, 3000)
// }, 2000)
// }, 1000)
// 用生成器函数实现上述需求
function one() {
setTimeout(() => {
console.log(111)
iterator.next()
}, 1000)
}
function two() {
setTimeout(() => {
console.log(222)
iterator.next()
}, 2000)
}
function three() {
setTimeout(() => {
console.log(333)
iterator.next()
}, 3000)
}
// 将三个函数放入生成器中
function* gen() {
yield one()
yield two()
yield three()
}
// 调用生成器函数
let iterator = gen()
iterator.next()
生成器函实例2
// 模拟获取 用户数据 订单数据 商品数据
function getUsers() {
setTimeout(() => {
let data = '用户数据'
// 调用 next 方法,并将 data 传入
// 第二次调用 next 方法,此实参将作为第一个 yield 语句的返回结果
iterator.next(data)
}, 1000)
}
function getOrders() {
setTimeout(() => {
let data = '订单数据'
// 第三次调用 next 方法,此实参将作为第二个 yield 语句的返回结果
iterator.next(data)
}, 1000)
}
function getGoods() {
setTimeout(() => {
let data = '商品数据'
// 第四次调用 next 方法,此实参将作为第三个 yield 语句的返回结果
iterator.next(data)
}, 1000)
}
function* gen() {
let users = yield getUsers()
// console.log(users);
let orders = yield getOrders()
// console.log(orders);
let goods = yield getGoods()
// console.log(goods);
}
let iterator = gen()
iterator.next()