资讯专栏INFORMATION COLUMN

ES6学习(二)之解构赋值及其原理

chunquedong / 997人阅读

摘要:基本原理解构是提供的语法糖,其实内在是针对可迭代对象的接口,通过遍历器按顺序获取对应的值进行赋值。属性值返回一个对象的无参函数,被返回对象符合迭代器协议。迭代器协议定义了标准的方式来产生一个有限或无限序列值。

更多系列文章请看
1、基本语法 1.1、数组
// 基础类型解构
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 [x = 1] = [undefined];// x=1;
let [x = 1] = [null];// x=null; // 数组成员严格等于undefined,默认值才会生效
let [x = 1, y = x] = [];     // x=1; y=1
let [x = 1, y = x] = [2];    // x=2; y=2
let [x = 1, y = x] = [1, 2]; // x=1; y=2
let [x = y, y = 1] = [];     // ReferenceError: y is not defined 因为x用y做默认值时,y还没有声明
1.2 对象
// 对象属性解构
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.3 字符串/数值/布尔值
// String
let [ a, b, c, ...rest ] = "test123"
console.log(a, b, c, rest) // t, e, s, [ "t", "1", "2", "3" ]
let {length : len} = "hello"; // en // 5

// number
let {toString: s} = 123;
s === Number.prototype.toString // true

// boolean
let {toString: s} = true;
s === Boolean.prototype.toString // true

// 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
2、使用场景 2.1、 浅拷贝
let colors = [ "red", "green", "blue" ];
let [ ...clonedColors ] = colors;
console.log(clonedColors);  // "[red,green,blue]"
注意这里是浅拷贝
2.2、 交换变量
let x = 1;
let y = 2;
[x, y] = [y, x];
2.3、遍历Map结构
var map = new Map();
map.set("first", "hello");
map.set("second", "world");

for (let [key, value] of map) {
    console.log(key + " is " + value);
}
2.4、函数参数
function add(a,b,{c,d}){
    // ...
}
add(1,2,{});
add(1,2)//Uncaught TypeError: Cannot destructure property `c` of "undefined" or "null"
解构赋值的规则是,若等号右边的值不是对象或者数组,就会先将其转化成对象。由于undefined和null无法转化成对象,所以对其进行解构赋值时会报错。

它实际上是这样运行的:

function add(a, b, options) {
    let { c,d } = options;
    // ...
}

由于add(1,2)没有传options导致异常,如果我们options是选填的那么可以像下面这样

function add(a,b,{c,d}={}){
    // ...
}

当然大部分情况我们可以给默认值

function move ({x:0,y:0}={}){}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]
function move ({x,y}={x:0,y:0}){}
move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, undefined]
move({}); // [undefined, undefined]
move(); // [0, 0]

上面代码是为函数move的参数指定默认值,而不是为变量x和y指定默认值,所以会得到与前一种写法不同的结果。
undefined就会触发函数参数的默认值。

[1, undefined, 3].map((x = "yes") => x);
3、基本原理

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

Iterator

可迭代对象

3.1、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
3.2、可迭代对象

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

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

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

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

属性
next 返回一个对象的无参函数,被返回对象拥有两个属性:donevalue
done :如果迭代器已经经过了被迭代序列时为 true。这时 value 可能描述了该迭代器的返回值。如果迭代器可以产生序列中的下一个值,则为 false。这等效于连同 done 属性也不指定。
value :迭代器返回的任何 JavaScript 值。done 为 true 时可省略。

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

3.3、解构语法糖

StringArrayMapSet等原生数据结构都是可迭代对象,可以通过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 }
4、性能

当我们有解构赋值的形式来做函数参数时,执行的时候会增加很多中间变量,内存也会比之前高。但是业务代码还是更加关注可读性和可维护性。如果你写的是库代码,可以尝试这种优化,把参数展开后直接传递,到底能带来多少性能收益还得看最终的基准测试。

参考

ES6 的解构赋值前每次都创建一个对象吗?会加重 GC 的负担吗?

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/102895.html

相关文章

  • ES6学习手稿基本类型扩展

    摘要:它是一个通用标准,奠定了的基本语法。年月发布了的第一个版本,正式名称就是标准简称。结语的基本扩展还有一些没有在这里详细介绍。 前言 ES6标准以及颁布两年了,但是,好像还没有完全走进我们的日常开发。这篇文章从ES6的基本类型扩展入手,逐步展开对ES6的介绍。 ECMAScript和JavaScript JavaScript是由Netscape创造的,该公司1996年11月将JavaSc...

    tommego 评论0 收藏0
  • ES6学习笔记

    摘要:所以,如果一个数组成员不严格等于,默认值是不会生效的因为不严格等于对象解构对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。 http://es6.ruanyifeng.com/?se... 一、作用域 let //i作用域在全局,每次循环i都被重新赋值了而覆盖了之前的值 var a = []...

    Gu_Yan 评论0 收藏0
  • 箭头函数你想知道的都在这里

    摘要:没有箭头函数没有自己的对象,这不一定是件坏事,因为箭头函数可以访问外围函数的对象那如果我们就是要访问箭头函数的参数呢你可以通过命名参数或者参数的形式访问参数不能通过关键字调用函数有两个内部方法和。 1、基本语法回顾 我们先来回顾下箭头函数的基本语法。ES6 增加了箭头函数: var f = v => v; // 等同于 var f = function (v) { return ...

    xiaoqibTn 评论0 收藏0
  • ES6学习笔记()--------------------------------------变量

    摘要:如果变量名与属性名不一致,必须写成下面这样。这是因为此时,字符串被转换成了一个类似数组的对象。数字和布尔的解构赋值,解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。参数也可以是无序的解构赋值对提取对象中的数据,可以快速提取 ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构ES6之前我们申明多个变量需要按照下面的方法: let a=1; let b=2...

    王笑朝 评论0 收藏0
  • ES6学习笔记()--------------------------------------变量

    摘要:如果变量名与属性名不一致,必须写成下面这样。这是因为此时,字符串被转换成了一个类似数组的对象。数字和布尔的解构赋值,解构赋值时,如果等号右边是数值和布尔值,则会先转为对象。参数也可以是无序的解构赋值对提取对象中的数据,可以快速提取 ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构ES6之前我们申明多个变量需要按照下面的方法: let a=1; let b=2...

    geekidentity 评论0 收藏0

发表评论

0条评论

chunquedong

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<