ES6 语法2 Symol-迭代器-生成器

目录

Symbol

对象添加 Symbol 类型的属性

Symbol 的内置属性

Symbol.hasInstance

Symbol.isConcatSpreadable

JavaScript 数据类型总结

迭代器

迭代器简介

迭代器的应用

自定义遍历数据

核心代码

生成器

生成器函数声明与调用

生成器函数参数传递

生成器函数实例1

生成器函实例2


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 值,指向语言内部使用的方法
  • hasInstanceisConcatSpreadable 等都是 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()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值