Wang's blog Wang's blog
首页
  • 前端文章

    • HTML教程
    • CSS
    • JavaScript
  • 前端框架

    • Vue
    • React
    • VuePress
    • Electron
  • 后端技术

    • Npm
    • Node
    • TypeScript
  • 编程规范

    • 规范
  • 我的笔记
  • Git
  • GitHub
  • VSCode
  • Mac工具
  • 数据库
  • Google
  • 服务器
  • Python爬虫
  • 前端教程
更多
收藏
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Wang Mings

跟随大神,成为大神!
首页
  • 前端文章

    • HTML教程
    • CSS
    • JavaScript
  • 前端框架

    • Vue
    • React
    • VuePress
    • Electron
  • 后端技术

    • Npm
    • Node
    • TypeScript
  • 编程规范

    • 规范
  • 我的笔记
  • Git
  • GitHub
  • VSCode
  • Mac工具
  • 数据库
  • Google
  • 服务器
  • Python爬虫
  • 前端教程
更多
收藏
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Python爬虫

  • 前端教程

    • 团队规范

    • Project

    • JS

      • Canvas基础
      • 数据结构
      • 树的深度优先遍历与广度优先遍历
      • for in和for of区别
      • ES6-新增特性一览
      • ES6-解构赋值及原理
        • [#](#基础语法) 基础语法
          • [#](#数组) 数组
          • [#](#对象) 对象
          • [#](#函数参数) 函数参数
          • [#](#string-map-set) String/Map/Set
        • [#](#解构原理) 解构原理
          • [#](#iterator概念) Iterator概念
          • [#](#可迭代对象) 可迭代对象
          • [#](#解构语法糖) 解构语法糖
        • [#](#参考文章) 参考文章
      • ES6-Object
      • ES6-模块详解
      • ES6-Class
      • ES6-ECMAScript特性汇总
      • 输入URL背后的技术步骤
      • JavaScript与浏览器 - 线程与引擎
      • HTTP跨域解决方案
      • Http 2与Http 1.x比较
      • JavaScript原型
      • JavaScript继承
      • JavaScript事件循环
      • 动手实现Promise
      • JS设计模式
      • JS 经典面试题
      • 排序算法
      • 正则表达式
      • MVC、MVP、MVVM区别
      • Array API与V8源码解析
      • 从V8 sort源码看插入排序
    • NodeJS

    • Vue

    • React

    • 效率工具

    • 读书笔记

  • 教程
  • 前端教程
  • JS
wangmings
2022-07-19
目录

ES6-解构赋值及原理

# # ES6-解构赋值及原理

# # 基础语法

# # 数组

    // 基础类型解构
    let [a, b, c] = [1, 2, 3]
    console.log(a, b, c) // 1, 2, 3
    
    // 对象数组解构
    let [a, b, c] = [{name: '1'}, {name: '2'}, {name: '3'}]
    console.log(a, b, c) // {name: '1'}, {name: '2'}, {name: '3'}
    
    // ...解构
    let [head, ...tail] = [1, 2, 3, 4]
    console.log(head, tail) // 1, [2, 3, 4]
    
    // 嵌套解构
    let [a, [b], d] = [1, [2, 3], 4]
    console.log(a, b, d) // 1, 2, 4
    
    // 解构不成功为undefined
    let [a, b, c] = [1]
    console.log(a, b, c) // 1, undefined, undefined
    
    // 解构默认赋值
    let [a = 1, b = 2] = [3]
    console.log(a, b) // 3, 2
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# # 对象

    // 对象属性解构
    let { f1, f2 } = { f1: 'test1', f2: 'test2' }
    console.log(f1, f2) // test1, test2
    
    // 可以不按照顺序,这是数组解构和对象解构的区别之一
    let { f2, f1 } = { f1: 'test1', f2: 'test2' }
    console.log(f1, f2) // test1, test2
    
    // 解构对象重命名
    let { f1: rename, f2 } = { f1: 'test1', f2: 'test2' }
    console.log(rename, f2) // test1, test2
    
    // 嵌套解构
    let { f1: {f11}} = { f1: { f11: 'test11', f12: 'test12' } }
    console.log(f11) // test11
    
    // 默认值
    let { f1 = 'test1', f2: rename = 'test2' } = { f1: 'current1', f2: 'current2'}
    console.log(f1, rename) // current1, current2
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# # 函数参数

    // 参数解构
    function func1({ x, y }) {
        return x + y
    }
    func1({ x: 1, y: 2}) // 3
    
    function func1({ x = 1, y = 2 }) {
        return x + y
    }
    func1({x: 4}) // 6
    
1
2
3
4
5
6
7
8
9
10
11

# # String/Map/Set

    // String
    let [ a, b, c, ...rest ] = 'test123'
    console.log(a, b, c, rest) // t, e, s, [ 't', '1', '2', '3' ]
    
    // Map
    let [a, b] = new Map().set('f1', 'test1').set('f2', 'test2')
    console.log(a, b) // [ 'f1', 'test1' ], [ 'f2', 'test2' ]
    
    // Set
    let [a, b] = new Set([1, 2, 3])
    console.log(a, b) // 1, 2
    
1
2
3
4
5
6
7
8
9
10
11
12

# # 解构原理

解构是ES6提供的语法糖,其实内在是针对可迭代对象的Iterator接口,通过遍历器按顺序获取对应的值进行赋值。这里需要提前懂得ES6的两个概念:

  • Iterator
  • 可迭代对象

# # Iterator概念

Iterator是一种接口,为各种不一样的数据解构提供统一的访问机制。任何数据解构只要有Iterator接口,就能通过遍历操作,依次按顺序处理数据结构内所有成员。ES6中的for of的语法相当于遍历器,会在遍历数据结构时,自动寻找Iterator接口。

Iterator作用:

  • 为各种数据解构提供统一的访问接口
  • 使得数据解构能按次序排列处理
  • 可以使用ES6最新命令 for of进行遍历
    function makeIterator(array) {
        var nextIndex = 0
        return {
          next: function() {
            return nextIndex < array.length ?
                {value: array[nextIndex++]} :
                {done: true}
            }
        };
      }
    
    
    var it = makeIterator([0, 1, 2])
    
    console.log(it.next().value) // 0
    console.log(it.next().value) // 1
    console.log(it.next().value) // 2
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# # 可迭代对象

可迭代对象是Iterator接口的实现。这是ECMAScript 2015的补充,它不是内置或语法,而仅仅是协议。任何遵循该协议点对象都能成为可迭代对象。可迭代对象得有两个协议:可迭代协议_和_迭代器协议。

可迭代协议:对象必须实现@@iterator方法。即对象或其原型链上必须有一个名叫Symbol.iterator的属性。该属性的值为无参函数,函数返回迭代器协议。

属性 值
Symbol.iterator 返回一个对象的无参函数,被返回对象符合迭代器协议。

迭代器协议:定义了标准的方式来产生一个有限或无限序列值。其要求必须实现一个next()方法,该方法返回对象有done(boolean)和value属性。

属性 值
next 返回一个对象的无参函数,被返回对象拥有两个属性:done和value

done - 如果迭代器已经经过了被迭代序列时为 true。这时 value 可能描述了该迭代器的返回值。如果迭代器可以产生序列中的下一个值,则为 false。这等效于连同 done 属性也不指定。
value - 迭代器返回的任何 JavaScript 值。done 为 true 时可省略。 |

通过以上可知,自定义数据结构,只要拥有Iterator接口,并将其部署到自己的Symbol.iterator属性上,就可以成为可迭代对象,能被for of循环遍历。

    // 自定义可迭代对象
    let obj = {
        [Symbol.iterator] : function() {
            return{
                next: function() {
                    return { value: 1, done: true }
                }
            }
        }
    }
    
    for (let item of obj) {
        console.log(item) // 不会报错,因为obj已经是可迭代对象
    }
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# # 解构语法糖

String、Array、Map、Set等原生数据结构都是可迭代对象,可以通过for of循环遍历它。故可以通过ES6解构语法糖依次获取对应的值。

    // String
    let str = 'test'
    let iterFun = str[Symbol.iterator]
    let iterator = str[Symbol.iterator]()
    let first = iterator.next() // 等效于 let [first] = 'test'
    console.log(iterFun, iterator, first)
    // 打印
    // [Function: [Symbol.iterator]], {}, { value: 't', done: false }
    
    // Array
    let arr = ['a', 'b', 'c'];
    let iter = arr[Symbol.iterator]();
    
    // 以下等效于 let [first, second, third, four] = ['a', 'b', 'c']
    let first = iter.next() // { value: 'a', done: false }
    let second = iter.next() // { value: 'b', done: false }
    let third = iter.next() // { value: 'c', done: false }
    let four = iter.next() // { value: undefined, done: true }
    
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

原生object对象是默认没有部署Iterator接口,即object不是一个可迭代对象。因为遍历时,不知道到底哪个属性先遍历,哪个属性后遍历,需要开发者手动指定。不过object部署Iterator接口没有必要,因为ES6提供了Map数据结构。实际上对象被解构时,会被当作Map进行解构。所以虽然Map和Object很多地方相似,但ES6引入Map、Set对象是有其原因的。

# # 参考文章

阮一峰ECMAScript 6 (opens new window) (opens new window)

Mozilla - for of (opens new window) (opens new window)

Mozilla - Iteration protocols (opens new window) (opens new window)

编辑 (opens new window)
ES6-新增特性一览
ES6-Object

← ES6-新增特性一览 ES6-Object→

最近更新
01
theme-vdoing-blog博客静态编译问题
09-16
02
搜索引擎
07-19
03
友情链接
07-19
更多文章>
Theme by Vdoing | Copyright © 2019-2022 Evan Xu | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式