ES6系列-1-命名与解构赋值

本篇主要介绍ES6新增的命名方式:let/const ,与解构赋值。

第一章 let和const

let

  1. ES6新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。也就是其声明的变量就绑定在这个区域中,不会再受到外界影响。

     {
       let a = 10;
       var b = 1;
     }
     console.log(b);//1
     console.log(a);//报错: a is not defined.
    

    例子2:

     for (let i = 0; i < 3; i++) {
       let i = 100;
       console.log(i);
     }
     //输出 三次100,说明循环体内部是一个单独的作用域,如果换成var,则只会输出一次100。
    

    特别对于for循环绑定事件获取索引问题可以很好的解决点我了解更多

  2. ES6明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。

  3. let声明,一个代码块内不可重复声明,否则会报错,而var命名则不会。

     function func(arg) {
       let arg; // 报错
     }
     
     function func(arg) {
       {
         let arg; // 不报错
       }
     }
    

const

  1. const声明一个只读的常量。一旦声明,常量的值就不能改变,

  2. const一旦声明变量,就必须立即初始化,不能留到以后赋值。

  3. const如果赋值一个对象时,保存的只是一个指向实际数据的指针

     const obj={};
     obj.name="123";//可以修改指针指向对象的数据
     obj=null;//报错!,不可修改指针的指向
    

顶层对象属性

ES5中顶层对象的属性与全局变量挂钩,这也被认为是js语言设计最大的败笔之一,各种命名冲突,很多只有在运行过程中才被暴露出来。

let a=1;
var b=2;
const c=3;
console.log(window.a);//undefined
console.log(window.b);//2
console.log(window.c);//undefined

目前在ES6中顶层对象依旧没有统一。

  1. 浏览器里面,顶层对象是window,但 Node 和 Web Worker 没有window。
  2. 浏览器和 Web Worker 里面,self也指向顶层对象,但是 Node 没有self。
  3. Node 里面,顶层对象是global,但其他环境都不支持。

变量声明的六种方式

待补充~

块级作用域

  1. es6 支持通过 {}(花括号)创建块级作用域
  2. es6 允许块级作用域的任意嵌套,外层作用域无法读取内层作用域的变量。
  3. es5的块级作用域一般通过自执行函数(IIFE)来创建,块级作用域的出现,IIFE不再必要了。
  4. ES5 规定,函数只能在顶层作用域和函数作用域之中声明,不能在块级作用域声明。ES6进行了修正,允许块级作用域内声明函数,但该函数只能在当前作用域内生效。外部无法访问(具体不同的浏览器实现会不一样)

第二章 变量的解构赋值

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

数组的解构赋值

将等号右边的数组,按照一定的匹配模式赋值给左边的变量。

基本用法

我们通常给变量赋值,采用下面写法:

let a = 1;
let b = 2;
let c = 3;

ES6 允许这样赋值

let [a, b, c] = [1, 2, 3];

上面代码表示,可以从数组中提取值,按照对应位置,对变量赋值。

注意

  1. 如果解构不成功,变量的值就等于undefined。
  2. 不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功。
  3. 如果等号的右边不是数组,(或者严格地说,不是可遍历的结构),那么将会报错。
     // 报错
     let [foo] = 1;
     let [foo] = false;
     let [foo] = NaN;
     let [foo] = undefined;
     let [foo] = null;
     let [foo] = {};
    

指定默认值

解构赋值允许指定默认值。

let [foo = true] = [];
foo // true

let [x, y = 'b'] = ['a']; // x='a', y='b'
let [x, y = 'b'] = ['a', undefined]; // x='a', y='b'

ES6 内部使用严格相等运算符(===),判断一个位置是否有值。所以,只有当一个数组成员严格等于undefined,默认值才会生效。

对象的解构赋值

解构不仅可以用于数组,还可以用于对象。
对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值(也可以将数组的索引认为是唯一的key,这样与对象的属性其实也是相通的)

let { bar, foo } = { foo: "aaa", bar: "bbb" };
foo // "aaa"
bar // "bbb"

let { baz } = { foo: "aaa", bar: "bbb" };
baz // undefined
//可以这样理解:前面的部分只是变量声明,声明了变量foo和bar, 等号后面是对这两个变量进行赋值,foo的值为'aaa',bar的为'bbb'。  

如果变量名与属性名不一致,必须写成下面这样(可以理解为变量名映射)

let { foo: baz } = { foo: "aaa", bar: "bbb" };
console.log(baz);//foo是匹配的模式,baz才是变量。真正被赋值的是变量baz

与数组一样,解构也可以用于嵌套结构的对象:

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

let { p: [x, { y }] } = obj;

console.log(x,y);// Hello World
console.log(p);// 报错

这时p是模式,不是变量,因此不会被赋值。如果p也要作为变量赋值,可以这样写:

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

let { p, p: [x, { y }] } = obj;

console.log(x,y);// Hello World
console.log(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}

字符串的解构赋值

字符串也可以解构赋值。这是因为此时,字符串被转换成了一个类似数组的对象。

    const [a, b, c, d, e] = 'hello';
    a // "h"
    b // "e"
    c // "l"
    d // "l"
    e // "o"

类似数组的对象都有一个length属性,因此还可以对这个属性解构赋值。

let {length : len} = 'hello';
len // 5

数值和布尔值的解构赋值

解构赋值时,如果等号右边是数值和布尔值,则会先转为对象,无法转换成对象的就报错(null 和 undefined)

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

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

let { prop: x } = undefined; // TypeError
let { prop: y } = null; // TypeError

函数参数的解构赋值

函数的参数也可以使用解构赋值。
例子1:

function add([x, y]){
  return x + y;
}

add([1, 2]); // 3

例子2:

[[1, 2], [3, 4]].map(([a, b]) => a + b); //[3, 7]
// 如果不好理解可以运行 [3, 7].map((item) => item); //[3, 7]

例子3:函数参数的解构也可以使用默认值。

function move({x = 0, y = 0} = {}) {
  return [x, y];
}

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

圆括号问题

结构语句中慎用圆括号,以下几种情况不要用圆括号,会报错:

  1. 变量声明语句
  2. 函数参数
  3. 赋值语句的模式

可以使用圆括号的情况只有一种:赋值语句的非模式部分,可以使用圆括号

解构赋值的用途

交换变量的值

    let x = 1;
    let y = 2;
    
    [x, y] = [y, x];
    console.log(x,y);//2,1

从函数返回多个值

函数只能返回一个值,如果要返回多个值,只能将它们放在数组或对象里返回。有了解构赋值,取出这些值就变得非常方便:

    // 返回一个数组
    
    function example() {
      return [1, 2, 3];
    }
    let [a, b, c] = example();
    
    // 返回一个对象
    
    function example() {
      return {
        foo: 1,
        bar: 2
      };
    }
    let { foo, bar } = example();

函数参数的定义

    // 参数是一组有次序的值
    function f([x, y, z]) { console.log(x+y+z) }
    f([1, 2, 3]); //6
     // 参数是一组无次序的值
    function f({x, y, z}) { console.log(2*x+y-z) }
    f({z: 3, y: 2, x: 1});//1

提取 JSON 数据

解构赋值对提取 JSON 对象中的数据,尤其有用。

    let jsonData = {
      id: 42,
      status: "OK",
      data: [867, 5309]
    };
    
    let { id, status, data: number } = jsonData;
    
    console.log(id, status, number);

函数参数的默认值

    function share({
        title="标题",
        url=window.location.href,
        content="分享描述"
    }){
        return JSON.stringify({
            title:title,
            shortLink:url,
            content:content
        })
    }
    console.log(share("自定义标题"));//{"title":"标题","shortLink":"http://zc.dev.bxd365.com/#/index","content":"分享描述"}

遍历 Map 结构

任何部署了 Iterator 接口的对象,都可以用for…of循环遍历。Map 结构原生支持 Iterator 接口,配合变量的解构赋值,获取键名和键值就非常方便。

    const map = new Map();
    map.set('first', 'hello');
    map.set('second', 'world');
    
    for (let [key, value] of map) {
      console.log(key + " is " + value);
    }
    // first is hello
    // second is world

如果只想获取键名,或者只想获取键值,可以写成下面这样:
// 获取键名
for (let [key] of map) {
// …
}

    // 获取键值
    for (let [,value] of map) {
      // ...
    }

输入模块的指定方法(webpack中常见)

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

参考文章