65.9K
CodeProject 正在变化。 阅读更多。
Home

JavaScript 解构:它太酷了

starIconstarIconstarIconstarIconstarIcon

5.00/5 (20投票s)

2017年8月13日

CPOL

7分钟阅读

viewsIcon

18635

downloadIcon

83

本文讨论了 ECMAScript 6 最酷的功能之一,即“解构”

我们为什么在这里?

这很简单。我们来这里是为了学习 ECMAScript 6 (ES6) 的一个叫做 解构 的功能。你了解 ECMAScript 6 (ES6) 对吧???

简单来说,ECMAScript 是一种脚本语言规范。ECMAScript 的主要实现之一是 JavaScript。ECMAScript 6,也称为 ECMAScript 2015,是 ECMAScript 标准的最新版本。让我们在下一节中通过一个故事来开始学习它的主要功能之一“解构”。

魔术盒的故事

让我们想象我们有一个 魔术盒。

我们的魔术盒有这些功能

  • 你可以把任何东西放进魔术盒里
  • 你可以一次性装满所有物品,或者你可以随时装填
  • 魔术盒可以回答你关于它里面物品的问题。

嗯,谁会不想要这样的魔术盒呢,对吧?

所以我们把一些东西放进魔术盒里。我们放进去

  • 3 本书
  • 5 个玩具
  • 一盒食物,里面有披萨、巧克力和冰淇淋
  • 一个更小的魔术盒,里面有 3 支笔和一个水果盒,里面有四种水果(例如:苹果、芒果、香蕉和葡萄)

让我们稍微可视化一下我们的魔术盒。

现在我们魔术盒里有一些物品了,让我们问它一些问题。假设有十个人向魔术盒提出了不同的问题。问题可以是

  1. 你有多少本书?
  2. 你有多少个玩具?
  3. 第一件食物是什么?
  4. 第二件食物是什么?
  5. 第三件食物是什么?
  6. 小魔术盒里有多少支笔?
  7. 小魔术盒里的第一种水果是什么?
  8. 小魔术盒里的第二种水果是什么?
  9. 小魔术盒里的第三种水果是什么?
  10. 小魔术盒里的第四种水果是什么?

魔术盒说:“我能回答你所有的问题,但一次只能回答一个”。

嗯,这没什么,但是如果它能一次性给出所有答案呢?


你觉得无聊吗?来吧!文章才刚开始,你就犯困了!:)

我用这个魔术盒的故事让你觉得无聊的原因是,JavaScript 对象构造非常灵活,就像魔术盒一样。你可以放入任何你想要的东西,比如字符串、数组、函数、其他对象等。你可以一次性创建对象,也可以随时分配属性。JavaScript 的灵活性是众所周知且广泛记载的,所以我们不再赘述。但是从对象中提取属性呢?!嗯,在 ECMAScript 6 (ES6) 出现之前,JavaScript 和我们的魔术盒一样有一些限制。为了理解这一点,让我们在 JavaScript 中创建我们的示例魔术盒对象。

var magicBox = {
    book: 2,
    toy: 5,
    foodBox: ['pizza', 'chocolate', 'ice cream'],
    smallMagicbox: {
        pen: 3,
        fruits: ["apple","mango", "banana","grape"]
    }
};

现在让我们尝试获取我们向魔术盒提出的问题的答案。在 JavaScript 中,它看起来像这样

/*ECMAScript 5 (ES5) Statements for extracting certain value*/
var book = magicBox.book;
var toy = magicBox.toy;
var firstFoodItem = magicBox.foodBox[0];
var secondFoodItem = magicBox.foodBox[1];
var thirdFoodItem = magicBox.foodBox[2];

var pen = magicBox.smallMagicbox.pen;
var firstFruitItem = magicBox.smallMagicbox.fruits[0];
var secondFruitItem = magicBox.smallMagicbox.fruits[1];
var thirdFruitItem = magicBox.smallMagicbox.fruits[2];
var fourthFruitItem = magicBox.smallMagicbox.fruits[3];

这就是我们长期以来的编码方式。嗯,不再是了。ES6 是我们的救星,它带来了名为解构的强大功能。它使我们能够将属性绑定到我们需要的任意数量的变量,并且它适用于数组和对象。我们将在下一节讨论这一点。我们将逐步学习如何在一行中提取所有属性,而不是 10 个单独的语句。

它是如何工作的?

假设我们想从 `magicBox` 对象中提取书本和玩具的数量。使用 ES6 解构,我们这样做

var magicBox = {
    book: 2,
    toy: 5,
    foodBox: ['pizza', 'chocolate', 'ice cream'],
    smallMagicbox: {
        pen: 3,
        fruits: ["apple", "mango", "banana", "grape"]
    }
};
var {book, toy} =  magicBox; /*extracting property value with matched property name;*/
console.log(book); //2
console.log(toy); //5

我们来看看这一行

var {book, toy} = magicBox;

赋值的左侧称为对象模式。在这里,我们必须在花括号内给出与对象属性相同的名称。这将把 `magixBox` 对象的匹配属性值赋给父作用域中同名变量。

现在有人可能会说

噢,得了!我不想让我的变量和对象属性同名。

嗯,解构也有解决办法。要将值分配给一个单独命名的变量,我们必须这样写

var magicBox = {
    book: 2,
    toy: 5,
    foodBox: ['pizza', 'chocolate', 'ice cream'],
    smallMagicbox: {
        pen: 3,
        fruits: ["apple", "mango", "banana", "grape"]
    }
};
/*assigning to different named variable;*/
var {book: numberOfBooks, toy: numberOfToys} =  magicBox; 
console.log(numberOfBooks); //2
console.log(numberOfToys); //5

上述代码匹配属性名“book”和“toy”,然后将值分别赋给变量“numberOfBooks”和“numberOfToys”。

通过解构,我们可以深入到我们想要的任何对象中并提取其属性值。

var magicBox = {
    book: 2,
    toy: 5,
    foodBox: ['pizza', 'chocolate', 'ice cream'],
    smallMagicbox: {
        pen: 3,
        fruits: ["apple", "mango", "banana", "grape"]
    }
};
var {smallMagicbox: {pen}} =magicBox; //pull deep
console.log(pen);  //3

上述代码演示了我们如何从magicBox对象内部的smallMagicbox对象中提取值。

现在让我们看看如何从数组中提取值

var numbers = [1,5,9,7];
var [a,b,c,d] = numbers; /*assign array values to variables*/
console.log(a); //1
console.log(b); //5
console.log(c); //9
console.log(d); //7

此赋值的左侧 `var [a,b,c,d] = numbers;` 称为 数组模式。值根据其索引分配给变量。

现在让我们尝试从我们的 `magicBox` 食物数组中提取值。

var magicBox = {
    book: 2,
    toy: 5,
    foodBox: ['pizza', 'chocolate', 'ice cream'],
    smallMagicbox: {
        pen: 3,
        fruits: ["apple", "mango", "banana", "grape"]
    }
};
/*getting values from our magicBox's foodBox array*/
var { foodBox:[firstFoodItem, secondFoodItem, thirdFoodItem] } =  magicBox; 
console.log(firstFoodItem); //pizza
console.log(secondFoodItem); //chocholate
console.log(thirdFoodItem);  //icecream

我们还可以像这样深度提取 smallMagicbox 水果数组

var magicBox = {
    book: 2,
    toy: 5,
    foodBox: ['pizza', 'chocolate', 'ice cream'],
    smallMagicbox: {
        pen: 3,
        fruits: ["apple", "mango", "banana", "grape"]
    }
};
/* pull deep*/
var {smallMagicbox: 
{fruits: [firstFruitItem,secondFruitItem, thirdFruitItem,fourthFruitItem ]} } = magicBox;
console.log(firstFruitItem); //apple
console.log(secondFruitItem); //mango
console.log(thirdFruitItem); //banana
console.log(fourthFruitItem); //grape

现在我们已经了解了如何使用解构提取属性,让我们尝试一次性提取所有 `magicBox` 属性。

var magicBox = {
    book: 2,
    toy: 5,
    foodBox: ['pizza', 'chocolate', 'ice cream'],
    smallMagicbox: {
        pen: 3,
        fruits: ["apple", "mango", "banana", "grape"]
    }
};
/*extracting magicBox values in a single line*/
var {book, toy, foodBox: [firstFoodItem, secondFoodItem, thirdFoodItem], 
     smallMagicbox:{pen, fruits: [firstFruitItem,secondFruitItem, thirdFruitItem,fourthFruitItem ]}} 
     =magicBox;
console.log(book); //2
console.log(toy); //5
console.log(pen); //3
console.log(firstFoodItem); //pizza
console.log(secondFoodItem); //chocolate
console.log(thirdFoodItem); //ice cream
console.log(firstFruitItem); //apple
console.log(secondFruitItem); //mango
console.log(thirdFruitItem); //banana
console.log(fourthFruitItem); //grape

很酷,不是吗?

我们的魔术盒可能无法一次性回答所有问题,但有了 ES6,JavaScript 已经克服了它的局限性。在下一节中,我们将学习更多关于解构的特性和用法。

特性与用法

就像一个对象如果它不包含某个属性就会返回 undefined 一样,当我们试图查找一个不存在的属性时,解构也会这样做。例如,如果我们试图从我们的 `magicBox` 对象中提取一个“basketball”属性,它将返回 undefined,因为它没有名为“basketball”的属性。

var magicBox = {
    book: 2,
    toy: 5,
    foodBox: ['pizza', 'chocolate', 'ice cream'],
    smallMagicbox: {
        pen: 3,
        fruits: ["apple", "mango", "banana", "grape"]
    }
};
var {basketball} = magicBox;
console.log(basketball); //undefined

同样,如果您尝试获取不存在的深度嵌套属性,它将引发异常

magicBox.movieBox.SpiderMan /*this gives exception*/
var {movieBox:{SpiderMan}}= magicBox; /*similarly this gives exception in destructuring*/

如果被提取的属性是 undefined,我们可以赋默认值。因此,当我们尝试在 `magicBox` 中查找“basketball”属性,如果未找到,我们只需将某些内容赋给“basketball”变量。

var magicBox = {
    book: 2,
    toy: 5,
    foodBox: ['pizza', 'chocolate', 'ice cream'],
    smallMagicbox: {
        pen: 3,
        fruits: ["apple", "mango", "banana", "grape"]
    }
};
var {basketball= "Big Basketball"} = magicBox;
console.log(basketball); /*Big Basketball*/
var {basketball:bs= "Big Basketball"} = magicBox; /*You can alias and assign like this*/
console.log(bs); /*Big Basketball*/

解构最酷的特性之一是无需第三个变量即可交换 变量值。

以前,我们这样交换

var book = 2;
var toy =5;
var aux = book; //auxiliary variable
book = toy;
toy = aux;

使用解构,我们可以轻松地像下面这样交换,无需辅助变量:

var book = 2;
var toy =5;
[toy,book] = [book,toy];
console.log(book); //5
console.log(toy); //2

我们可以使用计算属性名称来提取值。让我们看一个 ES5 的例子

var propName = "book";
var magicBox = {book: 5};
var numberOfBooks = magicBox[propName]; /*instead of directly using magicBox["book"] 
                                         we are using a computed peoperty*/

在 ES6 中,我们可以实现相同的结果,但我们将减少一个语句

var propName = "book";
var {[propName]: numberOfBooks }= {book: 5};
console.log(numberOfBooks ); //5

对于数组,我们可以跳过某些值,只取我们想要的,如下所示

var [,a,,b,c] = [1,2,3,4,5]
console.log(a); //2
console.log(b); //4
console.log(c); //5

有一个剩余运算符(...)可以将剩余的数组值分配给一个变量

var [a,...b] = [1,2,3,4,5];
console.log(a); //2
console.log(b); //[2,3,4,5]

当我们想从函数中获取一些返回值时,解构就非常方便。

function getMember(){
   return {
        name : "Jonh",
        age: 25
   };
}
var {name,age} = getMember();
console.log(name); //John
console.log(age); //25

我们可以为函数参数使用默认值

function sum({num1=0, num2=0}){
   return num1+num2;
}
var result= sum({num2: 5});
console.log(result); //5
result = sum({});
console.log(result); //0

如果我们想让整个参数对象可选,那么我们这样写

function sum({num1=0, num2=0}={}){
return num1+num2;
}
result = sum();
console.log(result); //0

这里有两个级别的默认值赋值。一旦没有参数传递,函数参数就被设置为空对象。同样,当在空对象中没有找到任何属性时,`num1` 和 `num2` 就会被设置为 `0`;

ES6 的强大功能之一是带有解构的For-of 迭代。让我们看下面的例子

var employeeList = [{
        name: 'John',
        department: {
            name: 'Finance',
            managerName: 'Steve'
        }
    },
    {
        name: 'Mark',
        department: {
            name: 'Humar Resource',
            managerName: 'David'
        }
    }
]
for({name: n, department:{name: d, managerName: m }} of employeeList){
console.log("Name:" +n);
console.log("Department:" +d);
console.log("ManagerName:" +m);
}

注意我们是如何轻松地迭代列表并一次性提取所需的值的。想象一下我们之前为了检索这三个属性所必须付出的麻烦。大概是这样的

for(var i=0;i<employeeList.length;i++)
{
   var n= employeeList[i].name;
   var d= employeeList[i].department.name;
   var m= employeeList[i].department.manager;
}

本文到此为止。所有示例都包含在下载文件中。您也可以从那里轻松查看。您喜欢解构的特性吗?无论喜欢与否,这将在不久的将来在 JavaScript 编程中被广泛使用。因此,我建议您开始更多地学习解构和 ECMAScript 6 的其他特性。

快速练习小贴士

只需打开最新的 Google Chrome 浏览器。访问 https://jsfiddle.net/。在 JavaScript 部分,复制粘贴代码并点击运行。在浏览器控制台中查看结果。尽情享受!:)

结论

ECMAScript 6 (ES6) 仍然没有被所有现代浏览器完全支持,但它最终会被广泛采用。这就是 JavaScript 在不久的将来被编码的方式。因此,现在是我们熟悉其功能的最佳时机。解构是其最酷的功能之一。

© . All rights reserved.