阅读 500

(JS基础)变量声明、解构赋值和展开运算符

声明变量

使用 var 、let 、const 都可以声明变量,var 是我们熟悉的就不多说,下面讨论一下另外两个。

let

在 ES6 之前的 JavaScript 是没有块级作用域。而 let 的诞生也带来的块级作用域。举个例:

function fn1() {
  for (let i of [1, 3, 2, 4, 5, 9]) { }
  console.log(i)
}
function fn2() {
  for (var i of [1, 3, 2, 4, 5, 9]) { }
  console.log(i)
}
fn1();    // 报错,因为let声明的变量有块级作用域
fn2();    // 9
复制代码

还有,let "修复"了 var 的变量提升(变量提升,意思是函数及变量的声明都将被提升到函数的最顶部),即 let 声明的变量不存在变量提升。看下例子:

console.log(b);    // undefined
var b = 10;
console.log(a);    // 报错
let a = 10;复制代码

除此,let 还会造成暂时性死区(即,只要块级作用域内存在let命令,它所声明的变量就"绑定"(binding)这个区域,不再受外部的影响)。例子:

var a = 1;
{
  // 报错。因为变量"c"已经被绑定在此区域,且不能变量提升
  console.log(a);   
  let a = 2;
  // 暂时性死区内 重复声明 会报错
  var a = 3;
}复制代码

const

const 其实与 let 基本一致,唯一不同的是,const 声明时必须赋值。而 const 声明的变量是不能被修改的。

const a = 1;
a = 2;    // 报错,常量不能被修改
复制代码


解构赋值

按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。

基本语法

let { foo, baz } = { foo: 'a', bar: 'b' };  // foo='a' baz=undefined
let { foo: bar } = { foo: 'a', bar: 'b' };  // bar='a' 被重新命名
let [one, two] = [100, 200, 300];           // one=100 two=200复制代码

对象的结构赋值中,等号左边对象的"key"暂且称为"匹配器",等号左边对象的"value"暂且称为"命名器",当两者相同时,可省略"命名键"。

执行解构赋值时,从等号右边的对象的"key"寻找与"匹配器"匹配的属性,找到则赋值,否则为 undefined ,而"命名器"则作为新变量的名字。如上例子中第二行,"foo"匹配对象的"foo"属性,并复制到"bar"变量上,则有"bar='a'"。

数组是特殊的对象,数组每项的"key"为 0 开始递增的整数,所以总是按顺序匹配赋值。

下面针对 对象 的解构赋值详细讲解,数组则同理。

嵌套赋值

let obj = { p: ['Hello', { y: 'World' }] };
let { p, p: [x, { y }] } = obj;
x  // "Hello"
y  // "World"
p  // ["Hello", {y: "World"}]

const node = { loc: { start: { line: 1, column: 5 } } };
let { loc, loc: { start }, loc: { start: { line } } } = node;
line    // 1
loc     // Object {start: Object}
start   // Object {line: 1, column: 5}

let arr = [], obj = {};
[arr[0], obj.prop] = [1, 2];
// arr = [1] obj = {prop: 2}复制代码

解构默认值

var { x, y = 3 } = { x: 1 };         // x = 1 y = 3
var { x, y = 3 } = { x: 1, y=2 };    // x = 1 y = 2
// 使用"命名器"也可以设定默认值
var { x, y:a = 3 } = { x: 1 };       // x = 1 a = 3复制代码

函数参数解构

本质是[x,y,...] = arguments的解构赋值。上面的解构默认值和嵌套赋值同理可用。

// 本质是 {x,y}=arguments[0] 的解构赋值
function fn({x, y}) { }复制代码

几种常用的用途

  1. 函数的传参(上面已讲解)。
  2. 交换赋值
    • let x = 1, y = 2;
      [x, y] = [y, x];复制代码
  3. 提取 JSON 对象中的数据
    • let jsonData = { id: 12, name: 'lisi', data: '...' }
      let { id, name, data: num } = jsonData;复制代码
  4. 遍历 map 对象
    • const map = new Map();
      map.set('first', 'hello');
      map.set('second', 'world');
      for (let [key, value] of map) {
        console.log(key + " is " + value);
      }复制代码
  5. require 加载模块时指定别名
    • const { SourceMapConsumer, SourceNode } = require("source-map");复制代码


展开运算符(...)

展开运算符(spread)是三个点(...)。好比把对象的"{}"去掉,数组则是去掉"[]"。

基本语法

let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };    // x=1 y=2 z={a:3,b:4}
let arr = [...[1, 2, 3], 4];                        // arr=[1,2,3,4]
let obj = { ...{ x: 1, y: 2 }, z: 3 }               // obj={x:1,y:2,z:3}
复制代码

在数组中的应用

  1. 浅复制:(数组内含有数组,则只复制内数组的指针)
    • const arr = [1, 2, 3];
      const newArr = [...arr];    // newArr=[1,2,3]
      复制代码
  2. 合并数组
    • const
        arr1 = ['a', 'b'],
        arr2 = ['c'],
        arr3 = ['d', 'e'];
      let arr = [...arr1, ...arr2, ...arr3];    // arr=['a','b','c','d','e']
      复制代码
  3. 与解构赋值结合:(注意,使用展开运算符的对象必须在最后
    • const [first, ...rest] = [1, 2, 3, 4, 5];    // first=1 rest=[2,3,4,5]
      const [first, ...rest] = ["foo"];            // first=1 rest=[]
      const [...rest, first] = ["foo"];            // 报错
      复制代码
  4. 字符串
    • let strs = [...'hello'];    // strs=["h","e","l","l","o"]
      // 快速翻转字符串
      let newStr = [...'hello'].reverse().join();    // "olleh"
      复制代码
  5. 类数组转换成数组
    • // 任何定义了遍历器(Iterator)接口的对象,都可以用扩展运算符转为真正的数组。
      let nodeList = document.querySelectorAll('div');
      let array = [...nodeList];
      复制代码

在对象中的应用

  1. 浅复制:(和数组一样,对象内的对象只能复制指针)
    • let obj1 = { a: 3, b: 4 };
      let obj2 = { ...obj1 };    // obj2={a:3,b:4}复制代码
  2. 字符串
    • let strs = {...'hello'}    // strs={0:"h",1:"e",2:"l",3:"l",4:"o"}复制代码
  3. 用于数组
    • let foo = { ...['a', 'b', 'c'] };    // foo={0:"a",1:"b",2:"c"}复制代码
  4. 对象合并:(只能合并对象内可枚举的,非继承的属性。且后者会覆盖前者同名属性
    • let
        a = { x: 1, y: 2 },
        b = { y: 3, z: 4 };
      let ab = { ...a, ...b };            // ab={ x:1,y:3,z:4}
      // 等同于
      let ab1 = Object.assign({}, a, b);  // ab={ x:1,y:3,z:4}
      复制代码


文章分类
前端
文章标签