美文网首页
了解es6(声明、解构、模板字符串)

了解es6(声明、解构、模板字符串)

作者: 锋享前端 | 来源:发表于2018-09-05 11:35 被阅读0次

ES6简介

ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。

一、ES6之let和const的使用

1.1块级作用域

在ES5之前,不存在块级作用域,在编程的时候很多时候会带来很多的不便,ES6新增了块级作用域,补足了这方面的缺陷。

块级声明指的是该声明的变量无法被代码块外部访问。块作用域,又被称为词法作用域(lexical scopes),可以在如下的条件下创建:

  • 函数内部
  • 在代码块(即 { })内部

块级作用域是很多类C语言的工作机制,ECMAScript 6 引入块级声明的目的是增强 JavaScript 的灵活性,同时又能与其它编程语言保持一致。

1.2 let声明

使用let声明变量的语法和使用var声明的语法是一样的。但是let声明的变量的作用域会限制在当前的代码块中。这是let与var的最大区别。

<script type="text/javascript">
    /***  var 和 ES6 let对比 ****/
        {
            var a = 10;
            let b = 20;
        }

        console.log(a);
        // 因为let声明后具有块级作用域 出了代码块后就访问不到 说报错
        console.log(b);
</script>

<script type="text/javascript">
    //在for循环中的使用
      for (var i = 0; i < 5; i++) {

      }

      console.log(i);

      for (let j = 0; j < 5; j++) {

      }
      //这里 j 也是存在代码块中, 这里会报错
      console.log(j);
</script>

<script type="text/javascript">
      //用var 声明的时候 有变量提升的特性
      console.log(c);
      var c = 'c';

      //使用let 不会有变量提升
      console.log(d);
      let d = 'd';
</script>

<script type="text/javascript">
    var e = "e";
    if (true) {
      e = "ee";
      //在代码块中,只要存在let命令,它声明的所有变量都会绑定在它身上。
      let e = "小王";
      console.log(e);
    }
</script>

总之在代码块内,使用变量之前,一定要用let声明好。
如果let在使用变量之后声明的话,我们称为:"暂时性死区" (temporary dead zone,简称:TDZ)。

<script type="text/javascript">
    if (true) {
      //TDZ 开始
      d = "d";
      console.log(d)
      //TDZ 结束
      let d = "dd";
      console.log(d);
    }
</script>

let不允许在同一作用域中声明重复的变量

<script type="text/javascript">
    (function () {
      let a = "a";
      //let a = "b";
    })();

    (function () {
      let a = "a";
      //var a = "b";
    })();
                
    (function (arg) {
      //let arg = "a";
    })();
                
    (function (arg) {
      {
        //自己在独立的一个块级里,就不会和形参的arg有冲突了。
        let arg = "a";
      }
    })();
</script>

关于全局变量,在var声明的情况下,全局变量就等同于全局对象的属性,用ES6的let声明全局变量则不会。

<script type="text/javascript">
    var str = 33;
    console.log(window.str); //33

    let str2 = "bcd";
    console.log(window.str2); //undefined
    console.log(str2); //bcd
</script>

 利用let解决一个比较经典的问题

<input type="button" value="按钮1" id="button1" />
<input type="button" value="按钮2" id="button2" />
<input type="button" value="按钮3" id="button3" />
<script type="text/javascript">
    for (var z = 1; z < 4; z++) {
      var button = document.getElementById('button' + z);
        button.addEventListener('click', function () {
          alert('button' + (z));  //每次打印结果 都是 button4
        });
    }

    //利用函数立即执行、形成闭包。每次保存z的值 这样就可以实现我们想要的结果
    for (var z = 1; z < 4; z++) {
      var button = document.getElementById('button' + z);
      //每次循环把i存储起来
      (function (z) {
        button.addEventListener('click', function () {
          alert('button' + (z));
        });
      })(z);
    }
    
    //更简单的解决办法,形成块级作用域,使用let
    for (let z = 1; z < 4; z++) {
      var button = document.getElementById('button' + z);
        button.addEventListener('click', function () {
          alert('button' + (z));  //每次打印结果 都是 button4
        });
    }
</script>

1.3 const声明

在 ES6 使用const来声明的变量称之为常量。这意味着它们不能再次被赋值。由于这个原因,所有的 const 声明的变量都必须在声明处初始化。

const声明的常量和let变量一样也是具有块级作用域的特性,一样不支持变量(常量)提升,一样存在"TDZ"。
const命令用来声明常量,常量一旦声明,就无法改变。
const命令用来声明常量,一定要初始化。

<script type="text/javascript">
    const PI = "3.1415..";
    console.log(PI);
    PI = "能修改吗?"; // 报错,常量不能修改
    console.log(PI); 
</script>

注意:针对于引用类型,修改值就是修改变量指针的指向。指向不同的对象实例

<script type="text/javascript">
    const OBJ = {};
    //OBJ = { age : 18 };  不可以,这样相当于修改了对象的指针指向
    OBJ.name = "小雪";  //可以,这个没有修改对象的指针指向,而是修改内部的属性值
    console.log(OBJ);
                
    //我们可以使用Object.freeze()冻结这个对象,从而达到不能操作对象的属性
    Object.freeze(OBJ);
    OBJ.age = 18;
    OBJ.name = "小虹";
    console.log(OBJ);
</script>

二、变量的解构

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

以前,为变量赋值,只能直接指定值。

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

ES6 允许写成下面这样。

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

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

2.1 数组的解构

只要等号两边的模式相同,左边的变量就会被赋予对应的值。下面是一些使用嵌套数组进行解构的例子。

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

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

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

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

let [x, y, ...z] = ['a'];
console.log(x) // "a"
console.log(y) // undefined
console.log(z) // []

如果解构不成功,变量的值就等于undefined。

let [foo] = [];
let [bar, foo] = [1];

以上两种情况都属于解构不成功,foo的值都会等于undefined。

另一种情况是不完全解构,即等号左边的模式,只匹配一部分的等号右边的数组。这种情况下,解构依然可以成功。

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

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

上面两个例子,都属于不完全解构,但是可以成功。

如果等号的右边不是可遍历的结构,那么将会报错。

// 报错
let [foo] = 1;
let [foo] = false;
let [foo] = NaN;
let [foo] = undefined;
let [foo] = null;
let [foo] = {};

上面的语句都会报错,因为等号右边的值,要么转为对象以后不具备 Iterator 接口(前五个表达式),要么本身就不具备 Iterator 接口(最后一个表达式)。

2.2 对象的解构

解构不仅可以用于数组,还可以用于对象。

let { foo, bar } = { foo: "aaa", bar: "bbb" };
console.log(foo) // "aaa"
console.log(bar) // "bbb"

对象的解构与数组有一个重要的不同。数组的元素是按次序排列的,变量的取值由它的位置决定;而对象的属性没有次序,变量必须与属性同名,才能取到正确的值。

let { bar, foo } = { foo: "aaa", bar: "bbb" };
console.log(foo) // "aaa"
console.log(bar) // "bbb"

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

上面代码的第一个例子,等号左边的两个变量的次序,与等号右边两个同名属性的次序不一致,但是对取值完全没有影响。第二个例子的变量没有对应的同名属性,导致取不到值,最后等于undefined。

如果变量名与属性名不一致,必须写成下面这样。

let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
console.log(baz) // "aaa"

let obj = { first: 'hello', last: 'world' };
let { first: f, last: l } = obj;
console.log(f) // 'hello'
console.log(l) // 'world'

这实际上说明,对象的解构赋值是下面形式的简写)。

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

也就是说,对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。

let { foo: baz } = { foo: "aaa", bar: "bbb" };
console.log(baz) // "aaa"
console.log(foo) // error: foo is not defined

上面代码中,foo是匹配的模式,baz才是变量。真正被赋值的是变量baz,而不是模式foo。

如果要将一个已经声明的变量用于解构赋值,必须非常小心。

// 错误的写法
let x;
{x} = {x: 1};
// SyntaxError: syntax error

上面代码的写法会报错,因为 JavaScript 引擎会将{x}理解成一个代码块,从而发生语法错误。只有不将大括号写在行首,避免 JavaScript 将其解释为代码块,才能解决这个问题。

// 正确的写法
let x;
({x} = {x: 1});

2.3 字符串的解构

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

const [a, b, c, d, e] = 'hello';
console.log(a) // "h"
cnosole.log(b) // "e"
console.log(c) // "l"
console.log(d) // "l"
console.log(e) // "o"

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

let {length : len} = 'hello';
console.log(len) // 5

三、模板字符串

模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以当作普通字符串使用,也可以用来定义多行字符串,或者在字符串中嵌入变量。

// 普通字符串
`In JavaScript '\n' is a line-feed.`

// 多行字符串
`In JavaScript this is
 not legal.`

console.log(`string text line 1
string text line 2`);

// 字符串中嵌入变量
let name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`

相关文章

网友评论

      本文标题:了解es6(声明、解构、模板字符串)

      本文链接:https://www.haomeiwen.com/subject/ystawftx.html