资讯专栏INFORMATION COLUMN

ES6解构赋值运算符

qpal / 404人阅读

摘要:本次我领到的任务是在中有一个解构赋值运算符,可以大大方便数据字段的获取。解构赋值运算符配合会比较有用。

本次我领到的任务是:

在ES6中有一个解构赋值运算符,可以大大方便数据字段的获取。 比如

const [a, b] = [1, 2, 3];
const {name, age} = {name: "helijia", age: 3};

上面的语句是我们常用的,可是你能解释为什么下面的语句也能正常工作吗?

const [a, b] = "abc";
const {toString: s} = 123;

任务:

1. 解释上面两个解构语句的工作原理
2. 你能够实现自定义类的数组解构吗?

比如:

class A = …
const a = new A();
const [e1, e2] = a;  //  怎么样才能让自定义的类也能支持支持数组的解构呢?

应用 默认值

ES5时,处理默认值的惯用法:

const scale = opts.scale || 1;

现在可以:

const {scale = 1} = opts;

不过两者并不等价,默认值只会在目标值是undefined时才会生效。

const {scale = 1} = {}
// scale === 1

const {scale = 1} = {scale: null}
// scale === null

const {scale = 1} = {scale: undefined}
// scale === 1
交换

有个技巧是可以在一条语句内实现变量值的交换。

原来需要三句话:

var tmp = a;
var a = b;
var b = a;

现在只需要:

const [a, b] = [b, a];

这让我们在实现一些基础算法时更加精练。

函数返回多个值

在ES5中函数只能返回一个值,有了解构赋值,可以模拟出多个返回值。

const [a, b] = f();

当然从设计上说,js的函数的返回值还是应该是单一的模型比较合适。

很小看到接口层面返回一个数组作为多个值。 可能在编写一些专业领域或DSL应用时会用得到。

而在实现时经常会使用解构赋值带来的便利:

const {name, age} = getInfo();
忽略数组中的一些值
const [a, , c] = [1, 2, 3];
// a === 1
// c === c

我想到的一个应用是一下子从正则表达式match对象中取出多个元素。

const re = /^([^=]+)=(.*)$/;
const [, key, value] = re.exec("name=helijia");
// key === "name"
// value === "helijia"
spread

解构赋值运算符配合spread会比较有用。

const {name, age, ...exts} = item;

return ;

exts对象中并不包含name和age,如果在ES5中要费好几句语句。

数组也支持spread,但是数组本身具有slice等函数,所以一般用不上。

var [head, ...tail] = [1, 2, 3, 4];
console.log(tail);
// [2, 3, 4]
重命名

对象的解构还支持重新命名,这在名字冲突,或者简化代码时会比较有用。

const item = {
  artisanNick: "玉米"
  artisanLevel: 10,
  artisanType: 3
};

const {artisanNick:nick, artisanLevel:level, artisanType:type} = item;

原来我们写成

const nick = item.artisanNick;
const level = item.artisanLevel;
const type = item.artisanType;

可配合默认值一起用:

const {a:aa = 10, b:bb = 5} = {a: 3}
函数参数的解构

这在实际开发中就用的比较多了,比如在React组件开发中:

const Product = ({name, price}) => (
  
name: {name}
price: {price}
);
babel对解构赋值的支持

以上描述的特性使用babel编译器就能在主流浏览器中工作,babel对ES6的支持是通过将代码编译成ES5代码来实现的;
而nodejs和chrome原生是直接支持es6的,它们是基于V8引擎在解释器层面支持ES6,因此两者能力是有差异的。

通过babel编译的ES6,最后本质是ES5代码,是静态的,所以只能支持一些语法糖的功能;

下面是一些示例:

常量
// ES6
const [a, b, c, d] = [1, 2, 3, 4];

// 对应的ES5
var a = 1;
var b = 2;
var c = 3;
var d = 4;
数组
// ES6
const list = [1, 2, 3, 4];
const [a, b, c, d] = list;

// ES5
var list = [1, 2, 3, 4];
var a = list[0];
var b = list[1];
var c = list[2];
var d = list[3];
别名和默认值
// ES6
const {a:aa = 10, b:bb = 5} = {a: 3}

// ES5
var _a = { a: 3 };
var _a$a = _a.a;
var aa = _a$a === undefined ? 10 : _a$a;
var _a$b = _a.b;
var bb = _a$b === undefined ? 5 : _a$b;

重命名和默认值的处理。

字符串
// ES6
const [a, b, c] = "1234";

// ES5
var _ = "1234";
var a = _[0];
var b = _[1];
var c = _[2];

字符串也当成数组一样处理了,所以刚好正常工作。

迭代器

其实只要实现迭代器接口,就能够解构。

const set = new Set([1, 2, 3, 4])
const [a, b, c] = set;
console.log(a, b, c);
// 1 2 3

这段代码在chrome的cosnole和nodejs中都能正常工作,不过在babel中就歇菜了。

因为它编译后的结果为:

var set = new Set([1, 2, 3, 4]);
var a = set[0];
var b = set[1];
var c = set[2];

console.log(a, b, c);
// undefined undefined undefined

当然Map也是实现了迭代器接口的。

const map = new Map();
map.set("window", window);
map.set("document", document);

for (const [key, value] of map) {
  console.log(key + " is " + value);
}

const [[k1, v1], [k2, v2]] = map;   // destructring

再来一个例子:

function* iter() {
  yield 1;
  yield 2;
  yield 3;
}

const [a, b, c] = iter();
console.log(a, b, c);
// 1 2 3

同样这段代码在babel中也不能正常工作。

回到任务
const [a, b] = "abc";
const {toString: s} = 123;

任务:

1. 解释上面两个解构语句的工作原理
2. 你能够实现自定义类的数组解构吗?

所以以上两个语句能正常工作,原因是分场景的,在通过babel编译成ES5和通过解释器直接执行原理是不一样的。

babel编译器会把它编译成

// ES6
const [a, b, c] = "1234";
const {toString: s} = "123";

// ES5
var _ = "1234";
var a = _[0];
var b = _[1];
var c = _[2];
var _2 = "123";
var s = _2.toString;

而js引擎执行ES6是因为字符串实现了迭代器接口,以及支持对象属性访问。

对于第2个问题,我们可以让自定义类实现迭代器接口来支持,只是在babel中不能正常工作。

以下是一个示例:

class Random {
  [Symbol.iterator]() {
    return {
      next() {
        return {value: Math.random(), done: false};
      }
    }
  }
}

const random = new Random();
for (const n of random) {
  if (n > 0.8) {
    break;
  }
  console.log(n);
}

const [e1, e2, e3, e4] = new Random();
console.log(e1, e2, e3, e4);
规范和V8对解构赋值的支持

运行语义

看到数组的解构处理,第一步总是取得一个迭代器,然后操作这个迭代器。

从规范中知道,解构赋值操作符对应的元素就是 DestructuringAssignment,查询V8代码可知,

V8在parser阶段就会把解构赋值语句重写成等效的赋值语句, 这样解释器不需要做修改就可以运行新的语法,也保证了效率。

关键代码片段:

RewriteDestructuringAssignments

RewriteDestructuringAssignment

VisitArrayLiteral

Pattern Match

使用了近一年半的Elixir,有许多语言特性另人着迷,其中模式匹配就是一个。

在ES6中引入了和模式匹配语法有点接近的解构赋值(Destructring Assigmnent)语句,但是仅仅是部分精简代码的语法糖,而在语义和表达上并没有本质变化。

不过搜索github上,看到已有相关的proposal,以及babel实现的issue,所以借此机会熟悉了解一番。

另外发现一个js库js-pattern-matching,提供了一个函数来支持模式匹配

其中这个JS库在不引入新语法特性的基础上支持较好的模式匹配语法,我觉得挺赞的。 它的原理是利用function.toString,得到函数字符串,再生成匹配的新的函数。

我写了几个简单的示例试用了一下,感觉还不错,不过在类型匹配时有些BUG。

基于解构赋值的匹配
const match = require("js-pattern-matching");

const sum = (list) => match(list) (
  ([x,...xs]) => x + sum(xs),
  ([]) => 0
);

console.log(sum([]));
console.log(sum([1, 2, 3, 4]));
常量匹配

因为要符合语法,所以加个前续v=,文档说是可改成其他字母。

const fibs = (n) => match(n) (
  (v= 0) => 0,
  (v= 1) => 1,
  _ => fibs(n - 2) + fibs(n - 1)
);

for (let i = 0; i < 10; i++) {
  console.log(fibs(i));
}
类型匹配
const type = (v) => match(v) (
  (Array) => "array",
  (Object) => "object",
  _ => "unknow"
);

我也看了proposal的语法,感觉风格和原来的js差异太大,设计成Expression,可以在任何地方使用,可能会因为功能太强而导致滥用,反而起不到原来模式匹配优雅简洁的目的。

其他人的一些探索,不过这个语法不是很美观。

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

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

相关文章

  • 学习ES6 变量的解构赋值

    摘要:变量的解构赋值数组的解构赋值允许写成下面这样本质上,这种写法属于模式匹配,只要等号两边的模式相同,左边的变量就会被赋予对应的值。对象的解构赋值对象的解构与数组有一个重要的不同。由于和无法转为对象,所以对他们进行解构赋值,都会报错。 变量的解构赋值 数组的解构赋值 let a = 1; let b = 2; let c = 3; ES6允许写成下面这样 let [a,b,c] = [1,...

    sugarmo 评论0 收藏0
  • ES6-变量的解构赋值(3)

    摘要:数组的解构赋值特点根据数据的下标来赋值的,有次序。模式能够匹配起来,如长度为数值和布尔值的解构赋值解构赋值的规则是只要等号右边的值不是对象或数组,就先将其转为对象。布尔值解构赋值为字符串的一种。在很多独立细小的方面,解构赋值都非常有用。 1、解构赋值简介 官方解释:按照一定的模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。 举个例子,想获取数组中...

    Jason_Geng 评论0 收藏0
  • es6解构赋值、扩展算符、rest参数使用

    摘要:模板字符串甚至还能嵌套解构赋值允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构。运算符使用场景应该稍少一些,主要是处理不定数量参数,可以避免对象的使用。 es6中较为常用的书写风格 为了书写的方便,es6中提出了很多比较友好的书写方式,其中最为常见的属于以下几个: 字符串模板 `abcdef${test}` 解构赋值 let [a, b, c] = [1,...

    xuweijian 评论0 收藏0
  • [译] 最深刻而易懂的ES6解构教程

    摘要:被解构的数据项位于赋值运算符的右侧,可以是任何数组和对象的组合,允许随意嵌套。数组模式位于赋值运算符的左侧,被结构的数组在其右侧。 解构是ES6的新特性,用于从JavaScript对象和数组中提取数据,语法上比ES5所提供的更加简洁、紧凑、清晰。它不仅能减少你的代码量,还能从根本上改变你的编码方式。用的越多,你就会发现越多塑造数据和函数的方式,这些实现方式在过去几乎是不可能的。本文将深...

    AlphaGooo 评论0 收藏0
  • ES6 ...操作符

    摘要:在语法中,操作符有两种意义剩余语法,参数和展开语法,展开数组对象,作为函数数组对象的扩展运算符。使用和参数进行操作其余参数传给原始函数展开语法运算则可以看作是参数的逆运算。 在ES6语法中,...操作符有两种意义:rest(剩余语法,rest参数) 和 spread(展开语法,展开数组/对象),作为函数、数组、对象的扩展运算符。 从某种意义上说,剩余语法与展开语法是相反的:剩余语法将多...

    MorePainMoreGain 评论0 收藏0

发表评论

0条评论

qpal

|高级讲师

TA的文章

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