es6学习笔记 - 解构赋值

什么是解构赋值

解构赋值允许你使用类似数组或对象字面量的语法将数组和对象的属性赋给各种变量。这种赋值语法极度简洁,同时还比传统的属性访问方法更为清晰。
通常来说,我们访问数组的元素的时候是这样子的:

    var first = someArray[0];
    var second = someArray[1];
    var third = someArray[2];

使用解构赋值赋值则会变得比较简单并且直观:

    var [first, second, third] = someArray;

SpiderMonkey(Firefox的JavaScript引擎)已经支持解构的大部分功能,但是仍不健全。你可以通过bug 694100跟踪解构和其它ES6特性在SpiderMonkey中的支持情况。

数组和迭代器的解构

以上是数组解构的简单例子,语法的形式为:

// [变量1,变量2,...,变量N] = 数组
[ variable1, variable2, ..., variableN ] = array;

这将为variable1到variableN的变量赋予数组中相应元素项的值。如果你想在赋值的同时声明变量,可在赋值语句前加入var、let或const关键字,例如:

    var [ variable1, variable2, ..., variableN ] = array;
    let [ variable1, variable2, ..., variableN ] = array;
    const [ variable1, variable2, ..., variableN ] = array;

事实上,用变量来描述并不恰当,因为你可以对任意深度的嵌套数组进行解构:

var [foo, [[bar], baz]] = [1, [[2], 3]];
    console.log(foo);   // => 1
    console.log(bar);   // => 2
    console.log(baz);   // => 3

此外,你可以在对应位留空来跳过被解构数组中的某些元素:

var [,,third] = ["foo", "bar", "baz"];
console.log(third);  //  => "baz"

而且你还可以通过“不定参数”模式捕获数组中的所有尾随元素:

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

当访问空数组或越界访问数组时,对其解构与对其索引的行为一致,最终得到的结果都是:undefined。

console.log([][0]); // =>undefined
var [missing] = [];    
console.log(missing); // => undefined    

请注意,数组解构赋值的模式同样适用于任意迭代器:

function* fibs() {   // 生成器函数
      var a = 0;
      var b = 1;
      while (true) {
        yield a;    // yield: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/yield
        [a, b] = [b, a + b];
      }
    }
    var [first, second, third, fourth, fifth, sixth] = fibs();
    console.log(sixth);  // =>5
对象的解构

通过解构对象,你可以把它的每个属性与不同的变量绑定,首先指定被绑定的属性,然后紧跟一个要解构的变量:

    var robotA = { name: "Bender" };
    var robotB = { name: "Flexo" };
    var { name: nameA } = robotA;
    var { name: nameB } = robotB;
    console.log(nameA);   // => "Bender"
    console.log(nameB);  // => "Flexo"

当属性名与变量名一致时,可以通过一种实用的句法简写:

    var { foo, bar } = { foo: "lorem", bar: "ipsum" };
    console.log(foo); // => "lorem" 
    console.log(bar); // => "ipsum"

与数组解构一样,你可以随意嵌套并进一步组合对象解构:

   var complicatedObj = {
      arrayProp: [
        "Zapp",
        { second: "Brannigan" }
      ]
    };
    var { arrayProp: [first, { second }] } = complicatedObj;
    console.log(first); // => "Zapp"
    console.log(second); // =>  "Brannigan"

当你解构一个未定义的属性时,得到的值为undefined:

    var { missing } = {};
    console.log(missing); // => undefined

请注意,当你解构对象并赋值给变量时,如果你已经声明或不打算声明这些变量(即赋值语句前没有let、const或var关键字),你应该注意这样一个潜在的语法错误:

{ blowUp } = { blowUp: 10 }; // Syntax error 语法错误

为什么会出错?这是因为JavaScript语法通知解析引擎将任何以{开始的语句解析为一个块语句(例如,{console}是一个合法块语句)。解决方案是将整个表达式用一对小括号包裹:

({ safe } = {});

这样就没有语法错误了;

解构值不是对象、数组或迭代器

当你尝试解构null或undefined时,你会得到一个类型错误:

var {blowUp} = null;     // TypeError: null has no properties(null没有属性)

然而,你可以解构其它原始类型,例如:布尔值、数值、字符串,但是你将得到undefined:

    var {wtf} = NaN;
    console.log(wtf);   // undefined

你可能对此感到意外,但经过进一步审查你就会发现,原因其实非常简单。当使用对象赋值模式时,被解构的值[需要被强制转换为对象。大多数类型都可以被转换为对象,但nullundefined 却无法进行转换。当使用数组赋值模式时,被解构的值一定要包含一个迭代器。

默认值

当你要解构的属性未定义时你可以提供一个默认值:

    var [missing = true] = [];
    console.log(missing);  // =>true
    var { message: msg = "Something went wrong" } = {};
    console.log(msg); // => "Something went wrong"
    var { x = 3 } = {};  
    console.log(x); //=> 3
解构的实际应用
函数参数定义

作为开发者,我们需要实现设计良好的API,通常的做法是为函数设计一个对象作为参数,然后将不同的实际参数作为对象属性,以避免让API使用者记住 多个参数的使用顺序。我们可以使用解构特性来避免这种问题,当我们想要引用它的其中一个属性时,大可不必反复使用这种单一参数对象。

function removeBreakpoint({ url, line, column }) {
      // ...
}

这是一段来自Firefox开发工具JavaScript调试器(同样使用JavaScript实现)的代码片段,它看起来非常简洁,我们会发现这种代码模式特别讨喜。

配置对象参数

延伸一下之前的示例,我们同样可以给需要解构的对象属性赋予默认值。当我们构造一个提供配置的对象,并且需要这个对象的属性携带默认值时,解构特性就派上用场了。举个例子,jQuery的ajax函数使用一个配置对象作为它的第二参数,我们可以这样重写函数定义:

jQuery.ajax = function (url, {
      async = true,
      beforeSend = noop,
      cache = true,
      complete = noop,
      crossDomain = false,
      global = true,
      // ... 更多配置
    }) {
      // ... do stuff
    };

如此一来,我们可以避免对配置对象的每个属性都重复var foo = config.foo || theDefaultFoo;这样的操作(可以设置默认值)。

与ES6迭代器协议协同使用

ECMAScript 6中定义了一个迭代器协议,我们在《深入浅出ES6(二):迭代器和for-of循环》中已经详细解析过。当你迭代Maps(ES6标准库中新加入的一种对象)后,你可以得到一系列形如[key, value]
的键值对,我们可将这些键值对解构,更轻松地访问键和值:

var map = new Map();
    map.set(window, "the global");
    map.set(document, "the document");
    for (var [key, value] of map) {
      console.log(key + " is " + value);
    }
    // "[object Window] is the global"
    // "[object HTMLDocument] is the document"

只遍历键:

for (var [key] of map) {
      // ...
}

或只遍历值:

for (var [,value] of map) {
      // ...
 }
多重返回值

JavaScript语言中尚未整合多重返回值的特性,但是无须多此一举,因为你自己就可以返回一个数组并将结果解构:

    function returnMultipleValues() {
        return [1, 2];
    }
    var [foo, bar] = returnMultipleValues();

或者,你可以用一个对象作为容器并为返回值命名:

  function returnMultipleValues() {
      return {
        foo: 1,
        bar: 2
      };
    }
    var { foo, bar } = returnMultipleValues();

这两个模式都比额外保存一个临时变量要好得多。

   function returnMultipleValues() {
      return {
        foo: 1,
        bar: 2
      };
    }
    var temp = returnMultipleValues();
    var foo = temp.foo;
    var bar = temp.bar;

或者使用CPS变换:

function returnMultipleValues(k) {
      k(1, 2);
}
returnMultipleValues((foo, bar) => ...);
使用解构导入部分CommonJS模块

你是否尚未使用ES6模块?还用着CommonJS的模块呢吧!没问题,当我们导入CommonJS模块X时,很可能在模块X中导出了许多你根本没打算用的函数。通过解构,你可以显式定义模块的一部分来拆分使用,同时还不会污染你的命名空间:

 const { SourceMapConsumer, SourceNode } = require("source-map");

如果你使用ES6模块,你一定知道在import声明中有一个相似的语法。

解构在许多独立小场景中非常实用。在Mozilla我们已经积累了许多有关解构的使用经验。十年前,Lars Hansen在Opera中引入了JS解构特性,Brendan Eich随后就给Firefox也增加了相应的支持,移植时版本为Firefox 2。所以我们可以肯定,渐渐地,你会在每天使用的语言中加入解构这个新特性,它可以让你的代码变得更加精简整洁。
本文引自:http://www.infoq.com/cn/articles/es6-in-depth-destructuring/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容