ECMAScript 6 | 新特性

新特性概览

参考文章: http://www.cnblogs.com/Wayou/p/es6_new_features.html

—————————————————————————————————————————————————————————

ES6测试引入文件

<<bootstrap.js>>

<<traceur.js>>

—————————————————————————————————————————————————————————

let命令

  • let命令用来声明变量,用法类似于var,但仅仅可以使用在定义的代码块中
  • 不存在变量提升
  • 暂时性死区:只要块级作用域内存在let命令,它所声明的变量就"绑定"binding这个区域,不再受外部影响
  • 不允许重复声明:let不允许在相同作用域内重复声明同一个变量

样例

<<test.js>>

// 在该代码块中声明let,外部不能调用,花括号之内
{
    let a = 100;
    var b = 200;
}

// console.log(a);
// 在这里var进行了变量提升,可以在全局中使用,let不可以变量提升
console.log(b);
// ********************************************************************
// 不存在变量提升
// ES5
var arr = [],
    arrTest = [];
// 在循环中c并没有赋给数组,而是被i循环覆盖为9,所以输出的都是9
for (var i = 0; i < 10; i++) {
    var c = i;
    arr[i] = function() {
        return c;
    }
}
// 遍历执行
for (var i = 0; i < 10; i++) {
    arrTest[i] = arr[i]();
}
console.log(arrTest);

// ES6
var arr2 = [];
// var c → let d
for (var i = 0; i < 10; i++) {
    let d = i;
    arr2[i] = function() {
        return d;
    }
}
for (var i = 0; i < 10; i++) {
    arrTest[i] = arr2[i]();
}
console.log(arrTest);
// 教学视频中没有说清楚,那么是否是因为let d 只对对应的代码块起作用,出现了10个不同的d呢?
// ********************************************************************
// 暂时性死区
{
    console.log(e);
    let e = 100;
    console.log(e);
}
// 在视频中得到的结果是undefined和100,而在我的测试中第一个console.log得到的是报错信息,提示未定义
// 在该代码块中let管辖着e,当未声明变量e时,打印e则会提示未定义的,暂时不可以使用的
// ********************************************************************
// 不允许重复声明
{
    var f = 1;
    let f = 100;
}
{
    let g = 100;
    var g = 1;
}
// 重复声明会产生报错信息Identifier 'f' has already been declared

<<index.html>>

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <script src="traceur.js"></script>
    <script src="bootstrap.js"></script>
    <script type="text/javascript" src="test.js"></script>
</head>

<body>
</body>

</html>

—————————————————————————————————————————————————————————

块级作用域

let实际上为JavaScript新增了块级作用域

  • ES5中只有全局作用域和函数作用域,没有块级作用域,容易出现以下问题:
    • 内层变量可能会覆盖外层变量
    • 用来计数的循环变量泄露为全局变量

<<index.html>>

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <script src="traceur.js"></script>
    <script src="bootstrap.js"></script>
    <!-- ES5 -->
    <script type="text/javascript">
    var time = new Date();

    function f1() {
        console.log(time);
        if (false) {
            var time = 'hello'; // 变量重复声明了之后,内部time会覆盖已有变量的内存地址,在运行时console.log会寻找地址,此时地址还没有存入值'hello',所以输出为undefined
            // time ='hello'; // 如果是使用赋值语句,则正常输出time为Date();
        }
    }
    f1();
    // ****************************************
    // 循环变量泄露为全局变量的问题
    for (var i = 0; i < 12; i++) {}
    // i跳出了循环体,循环结束后i没有被销毁
    console.log(i);

    // 在其他函数中也会被使用
    function f2() {
        console.log(i);
    }
    f2();
    // ****************************************
    // Demo1:
    // 第二次声明nTest1会覆盖第一次声明,得到200的值
    function f3() {
        var nTest1 = 100;
        if (true) {
            var nTest1 = 200;
        }
        console.log(nTest1);
    }
    f3();
    // ****************************************
    // Demo2:自调用函数
    // 视频演示中ES5得到的是inside,ES6得到的是outside
    // 测试时ES5报错,ES6正常inside
    function fun(){
        console.log('i am outside');
    };
    (function (){
        if (false) {
            function fun(){
                console.log("i am inside");
            };
        }
        fun();
    })();
    </script>
    <!-- ES6 -->
    <script type="text/traceur">
    // ****************************************
    // Demo1:
        function f4() {
            let nTest2 = 100;
            if (true) {
                let nTest2 = 200;
                console.log(nTest2);
            }
            console.log(nTest2);
        }
        f4();
    // ****************************************
    // Demo2:自调用函数
    function fun(){
        console.log('i am outside');
    }
    (function (){
        if (false) {
            function fun(){
                console.log("i am inside");
            }
        }
        fun();
    }())
    </script>
</head>

<body>
</body>

</html>

—————————————————————————————————————————————————————————

const命令

const关键字声明的是常量,不可改变

<<index.html>>

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <script src="traceur.js"></script>
    <script src="bootstrap.js"></script>
    <!-- ES5 -->
    <script type="text/javascript">
        // const命令
        const Pi = 3.1415;
        console.log(Pi);
        // 赋值的话返回错误信息Assignment to constant variable.
        // Pi = 1;
        // ***********************************************************
        // const的块级作用域问题
        if (true) {
            var a = 1;
        }
        // Error: a is not defined
        // 可见const关键字定义的也有块级作用域的问题
        // console.log(a);
    </script>
    <!-- ES6 -->
    <script type="text/traceur">
        // 暂时性死区
        if (true) {
            console.log(b);
            const b = 2;
        }
        // ***********************************************************
        {
            //Error:["file:///F:/6.Code/Front-End/JavaScript/Test/index_inline_script_1.js:6:16: Duplicate declaration, c"]
            //不可重复声明
            //var c=200;
            const c = 300;
            console.log(c);
        }
        // ***********************************************************
        // 通过const声明对象时,显示这个对象是只读的,该对象是冻结状态的,但仍然可以写入值
        const person = {};
        person.name = "hello";
        console.log(person);
        person.name = "hugh";
        console.log(person);
        console.log(Object.isSealed());
        console.log(Object.isFrozen());
        // 如果以冻结方法定义的对象,则是无法写入的
        const person2 = Object.freeze({});
        person2.name = "hugh";
        person2.age = 21;
        console.log(person2);
        console.log(person2.name);
        console.log(person2.age);
        console.log(Object.isFrozen());
        // 正确的const冻结对象的方法
        const person3 = Object.freeze({
            name: 'hhh',
            age: 1
        });
        console.log(person3);
        console.log(Object.isFrozen());
        // ***********************************************************
        // const声明的数组可以数组操作,但不可以整体赋值,视为重新定义
        const arr1 = [];
        arr1.push(1);
        arr1.push(2);
        console.log(arr1);
        arr1.pop();
        console.log(arr1);
        console.log(arr1.length);
        //Error:["file:///F:/6.Code/Front-End/JavaScript/Test/index_inline_script_1.js:30:5: arr1 is read-only"]
        //arr1 = ['a','b','c'];
        //console.log(arr1);
        // ***********************************************************
        // 彻底冻结对象,请对比参考JavaScript中的递归冻结函数方法
        var constantize = (obj) => {
            Object.freeze(obj);
            Object.keys(obj).forEach((key,value)=>{
                if(typeof obj[key]==='object'{
                    constantize(obj[key]);
                });
            });
        };
    </script>
</head>

<body>
</body>

</html>

—————————————————————————————————————————————————————————

跨模块常量

<<index.html>>

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <script src="traceur.js"></script>
    <script src="bootstrap.js"></script>
    <!-- ES5 -->
    <script type="text/javascript">

    </script>
    <!-- ES6 -->
    <script type="text/traceur">
    // module.js, 在这里将变量输出
    export const cVariantName = "111";
    export const iVariantName = 3;
    export const fVariantName = 4.111;

    // use.js, 在这里将变量全部引入
    import * as variant from './module.js';
    console.log(variant.cVariantName); // 输出"111";
    console.log(variant.iVariantName); // 输出3;
    console.log(variant.fVariantName); // 输出4.111;

    // otherUse.js, 引入部分变量
    import {iVariantName,fVariantName} as variant from './module.js';
    console.log(variant.iVariantName); // 输出3;
    console.log(variant.fVariantName); // 输出4.111;

    // onlyUse.js, 只引入一个变量
    import iVariantName as variant from './module.js';
    console.log(variant.iVariantName); // 输出3;
    </script>
</head>

<body>
</body>

</html>

—————————————————————————————————————————————————————————

全局对象属性

  • 全局变量是最顶层的对象
  • 浏览器环境指的是window对象
  • Node.js指的是global对象
  • JavaScript中所有全局变量都是全局对象的属性

    p.s.Node中这一条只对REPL环境使用,模块环境必须显式声明成global属性

  • ES6规定:
    • varfunction命令声明的全局变量,属于全局对象的属性
    • letconstclass命令声明的全局变量,不属于全局对象的属性

<<index.html>>

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <script src="traceur.js"></script>
    <script src="bootstrap.js"></script>
    <!-- ES5 -->
    <script type="text/javascript">

    </script>
    <!-- ES6 -->
    <script type="text/traceur">
    var varName = "varValue";
    // 浏览器环境
    console.log(window.varName); // 输出varValue
    // Node环境
    // console.log(global.varName);
    // 通用环境
    console.log(this.varName); // 输出varValue

    let letName = "letValue";
    console.log(window.letName); // 兼容模式:letValue, 严格模式:undefined
    console.log(this.letName); // 兼容模式:letValue, 严格模式:undefined
    </script>
</head>

<body>
</body>

</html>

—————————————————————————————————————————————————————————

解构赋值 Dustructuring

  • 解构:ES6允许按照一定模式,从数组和对象中提取值,对变量进行赋值
  • 不完全解构:等号左边的模式只匹配一部分等号右边的数组
  • 指定默认值:ES6内部使用严格相等运算符===来判断一个位置是否有值,如果数组成员不严格等于undefined默认值不会生效
  • letconst命令:只要某种数据结构具有iterator(迭代器)接口,都可以采用数组形式解构赋值

数组解构赋值

<<index.html>>

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <script src="traceur.js"></script>
    <script src="bootstrap.js"></script>
    <!-- ES5 -->
    <script type="text/javascript">
    // Set对象
    var s = new Set();
    s.add("Thomas Jefferson");
    s.add(1776);
    s.add("founding father");

    s.forEach(function (item) {
        console.log(item.toString() + ", ");
    });

    </script>
    <!-- ES6 -->
    <script type="text/traceur">
    //数组解构
    // 通过这种形式批量结构赋值
    var [a,b,c] = [1,2,3];
    console.log(a);
    console.log(b);
    console.log(c);
    // 对应位置数组解构
    let [foo,[[bar],,base]] = [1,[[2],3,4]];
    console.log(foo);
    console.log(bar);
    console.log(base);
    // ...tail 作为数组
    let [head, ...tail] = [0,1,2,3,4,5,6,7,8,9];
    console.log(head);
    console.log(tail);
    // 不完全解构
    let [x,y] = [1,2,3];
    console.log(x);
    console.log(y);
    let [x1,y1] = [100];
    console.log(x1); // 100
    console.log(y1); // undefined
    // 指定默认值
    var [temp = 'string'] = []; // 在这里temp的默认值为string,如果不给值的话为默认string
    console.log(temp);
    var [temp1 = 'string1'] = ['string2'];
    console.log(temp1);
    var [temp2 = 'aaa',temp3] = ['bbb']; 
    console.log(temp2); // 'bbb'
    console.log(temp3); // undefined, 解构时完全对应给定值,bbb赋值给temp2,没有值给tmep3,参见不完全解构
    var [temp4 = 'aaa'] = [undefined];
    console.log(temp4); // 'aaa' 因为给定的值是未定义的
    // 非遍历结构 - 报错
    // var [temp5] = 1; // traceur.js:31513 TypeError: 1[Symbol.iterator] is not a function

    // 迭代器
    let [a1,b1,c1] = new Set(['aaa','bbb','ccc']);
    console.log(a1);
    console.log(b1);
    console.log(c1);
    function* fibs(){
        let a2 = 0;
        let b2 = 1;
        while(true){
            yield a2;
            [a2,b2] = [b2,a2+b2];
        }
    }
    var [first,second,third,fourth,fifth,sixth] = fibs();
    console.log(first);
    console.log(second);
    console.log(third);
    console.log(fourth);
    console.log(fifth);
    console.log(sixth);
    </script>
</head>

<body>
</body>

</html>

对象解构赋值

<<index.html>>

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <script src="traceur.js"></script>
    <script src="bootstrap.js"></script>
    <!-- ES5 -->
    <script type="text/javascript">

    </script>
    <!-- ES6 -->
    <script type="text/traceur">
    // 对象解构赋值
    var {name,age,id} = {id:'0001',name:'hugh',age:20};
    console.log(name);
    console.log(age);
    console.log(id); // 可以不按顺序
    // 变量名和属性名不一致
    var {change_name,age,id} = {id:'0001',name:'hugh',age:20};
    console.log(change_name); // undefined
    // 将变量名和属性名对应起来
    var {name:person_name,age:person_age,id:person_id} = {id:'0001',name:'hugh',age:20};
    console.log(person_id);
    console.log(person_name);
    console.log(person_age);
    // 重构对象
    let object = {first:1,second:2};
    let {first:first_num , second:second_num} = object; // 结构对象
    console.log(first_num);
    console.log(second_num);
    // 指定默认值
    // 结构默认值的条件为 === undefined
    var {x = 3,y = 5} = {};
    console.log(x);
    console.log(y);
    var {massage:msg = "yes!"} = {};
    console.log(msg);
    // 已声明变量的解构赋值
    var test;
    // {test} = {test:1};
    ({test} = {test:1});
    console.log(test);
    // 现有对象的方法
    let {sin,cos,tan} = Math;
    console.log(sin(Math.PI/6));

    </script>
</head>

<body>
</body>

</html>

字符串解构赋值

<<index.html>>

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <script src="traceur.js"></script>
    <script src="bootstrap.js"></script>
    <!-- ES5 -->
    <script type="text/javascript">

    </script>
    <!-- ES6 -->
    <script type="text/traceur">
    // 字符串的解构赋值
    const [a,b,c,d,e,f] = "hello!"; // 逐位对应解构赋值
    console.log(a);
    console.log(b);
    console.log(c);
    console.log(d);
    console.log(e);
    console.log(f);
    // 字符串的length属性解构
    const {length : len} = "hhh"; // 通过对象取属性
    console.log(len);
    const {length} = "1111";
    console.log(length);
    </script>
</head>

<body>
</body>

</html>

函数参数解构赋值

<<index.html>>

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <script src="traceur.js"></script>
    <script src="bootstrap.js"></script>
    <!-- ES5 -->
    <script type="text/javascript">

    </script>
    <!-- ES6 -->
    <script type="text/traceur">
    // 函数参数的解构赋值
    function sum ([x, y]){
        return x + y;
    }
    console.log(sum([1, 2])); // 解构赋值传入一个数组,数组变量解构赋值

    // 函数参数解构默认值
    // 在这里0和1并不是赋值操作,而是声明变量,所以不能使用以下这种方法:function fun({x, y} = {x : 0, y : 1}),将{x, y} = {x : 0, y : 1}作为函数内部的语句来操作,如果传入的是fun(),return 0 0,传入fun({x:100}),return 100 undefined
    function fun({x = 0, y = 1} = {}){ 
        return [x, y];
    }
    console.log(fun({x : 100})); // y返回的是默认值
    console.log(fun()); // x,y均返回默认值
    </script>
</head>

<body>
</body>

</html>

解构赋值用途

  • 交换变量的值
  • 从函数返回多个值
  • 函数参数的定义
  • 提取Json数据
  • 函数参数的默认值
  • 遍历Map结构
  • 输入模块的指定方法

<<index.html>>

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <script src="traceur.js"></script>
    <script src="bootstrap.js"></script>
    <!-- ES5 -->
    <script type="text/javascript">

    </script>
    <!-- ES6 -->
    <script type="text/traceur">
    // 交换变量的值
    // ES5中通过temp变量, temp = a; a = b; b = temp;
    var x = 100, y = 200;
    [x, y] = [y, x];
    console.log([x, y]);

    // ************************************************************************************
    // 从函数返回多个值,优势:不需要使用fun.id之类的操作,直接取值到变量
    // 返回一个数组
    function fun(){
        return [1, 2, 3];
    }
    var [x, y, z] = fun();
    console.log(x);
    console.log(y);
    console.log(z);
    // 返回一个对象
    function fun1(){
        return {
            id : "0001",
            name : "hugh",
            age : 20
        };
    }
    var {id : person_id, name : person_name, age : person_age} = fun1();
    console.log(person_id);
    console.log(person_name);
    console.log(person_age);

    // ************************************************************************************
    // 函数参数的定义
    // 参数有次序
    function fun2([x, y, z]){
        console.log(x);
        console.log(y);
        console.log(z);
    }
    fun2([1, 2, 3]);
    // 参数无次序
    // 使用到的是对象的解构赋值,用于接口的交互,如果是使用AJAX交互时,没必要在传值前打包,直接封装成Json对象,打包好传过来,可以使整个模块更清晰
    function fun3({id, name, age}){
        console.log(id);
        console.log(name);
        console.log(age);
    }
    fun3({name : "dong", id : '0002', age : 30});

    // ************************************************************************************
    // 提取Json数据
    var jsonData = {
        id : "0003",
        name : "dong",
        age : 21,
        score : {
            Chinese : 99,
            Math : 98,
            English : 97
        }
    };
    // 通常情况下提取Json数据
    console.log(jsonData);
    console.log("id:"+jsonData.id);
    console.log("name:"+jsonData.name);
    console.log("chinese score:"+jsonData.score.Chinese);
    console.log("english score:"+jsonData.score.English);
    // ES6下解构后
    let {id:number, xingming, age, score:chengji} = jsonData;
    console.log("ES6:"+number);
    console.log("ES6:"+xingming);
    console.log("ES6:"+age);
    console.log("ES6:"+chengji.Chinese);

    // ************************************************************************************
    // 函数参数的默认值
    // 内建的ajax
    // jQuery.ajax({
    //     url :'/path/to/file',
    //     type :'POST',
    //     dataType : 'xml/html/script/json/jsonp',
    //     data : {param1 : 'value1'},
    //     complate : function(xhr, textStatus){
    //         // called when complete
    //     },
    //     success : function(data, textStatus, xhr){
    //         // called when success
    //     },
    //     error : function(xhr, textStatus, errorThrown){
    //         // called when there is an error  
    //     }
    // });
    // 解构赋值方式,通过参数的默认值来进行ajax运算,突出函数的传值
    // 避免了在函数体内部再写 var foo = config.foo || 'default foo'; 这样的语句
    // jQuery.ajax = function (url, {
    //     async = true,
    //     beforeSend = function (){},
    //     cache = true,
    //     complete = function (){},
    //     crossDomain = false,
    //     global = true,
    //     // ...more config
    // }){
    //     // ...do stuff
    // };

    // ************************************************************************************
    // 遍历Map解构
    var map = new Map();
    map.set("id","0007"); // 创建键值对
    map.set("name","hehe");
    console.log(map); // map结构是object
    for(let [key, value] of map){ // 遍历获取键值对
        console.log(key + " is " + value);
    }
    for (let[key] of map){ // 遍历获取键名
        console.log(key);
    }
    for (let [, value] of map){ // 遍历获取键值, p.s. [ , value] !!!
        console.log(value);
    } 

    // ************************************************************************************
    // 输入模块的指定方法
    const { SourceMapConsumer , SourceNode } = require("source-map");
    </script>
</head>

<body>
</body>

</html>

原文地址:https://www.cnblogs.com/hughdong/p/7249667.html