zoukankan      html  css  js  c++  java
  • io.js入门(二)—— 所支持的ES6(上)

    io.js的官网上有专门介绍其所支持的ES6特性的页面(点我查看),上面介绍到,相比nodeJS,io.js已从根本上支持了新版V8引擎上所支持的ES6特性,无需再添加任何运行时标志(如 --harmony )。

    有的朋友可能对Node不熟悉,不太知道harmony标志的情况,这里简单介绍下。

    在NodeJS中,如果所要执行的脚本(假设为app.js)是基于ES6来编写的,那么得在命令的脚本路径前加上 “--harmony” 运行时标志,也就是这样执行脚本:

    node --harmony app.js

    “--harmony” 前缀表示让Node支持除了 “typeof” 外的所有标准ES6特性,除此之外还有 “--harmony_typeof”(开启typeof支持)之类的前缀,具体可以参详stackOverflow上的一个问答

    在io.js中,所有的ES6特性被划分为三大类—— 已标准化的(completed)特性、已确定将标准化的(staged)特性、仍处草案待定状态in progress的特性

    针对这三大类,io.js做了分别的处理,而不像Node那样都得加harmony标签:

    ⑴ 已标准化的ES6特性,如我们上文提到的,直接用指令执行即可,无需再加任何运行时标志;

    ⑵ 已确定将标准化的ES6特性,执行时需要加上运行时标志 “ --es_staging ” ,当然你也可以使用它的同义词 “ --harmony ” ;

    ⑶ 在草案上但仍未确定将标准化的ES6特性,执行时需要加上它们自己对应的harmony标志,比如你在脚本中使用了 arrow_functions 特性,那么需要加上标志 “--harmony_arrow_functions”。

    io.js建议不要使用 ⑵ 和 ⑶ 的不稳定的特性。

    下面将较详细地来介绍io.js原生支持的标准ES6特性。

    标准ES6特性

    该系列特性可直接使用,无需添加运行时标志,但小部分特性要求只能在严格模式("use strict";)下使用。

    1. 块级作用域/Block scoping(需要在严格模式下使用)

        ○ let

        ○ const

        ○ 块级作用域中的函数

    2. 集合/Collections

        ○ Map

        ○ WeakMap

        ○ Set

        ○ WeakSet

    3. Generators

    4. 二进制和八进制语法/Binary and Octal literals

    5. Promises

    6. 新的String方法/New String methods

    7. 符号/Symbols

    8. 字符串模板/Template strings

    let (只能在严格模式下使用)

    类似于var,声明一个变量,但只在声明处所处的块级作用域内有效:

    "use strict";  //使用严格模式
    {
        var a=1;
        let b=2;
        console.log(a);
        console.log(b);
    }
    console.log(a);
    console.log(b);   //undefined

    上述代码执行如下:

    使用 let 可以用于解决变量提升问题:

    "use strict";  //使用严格模式
    for (var i = 0, a=[]; i < 10; i++) {
        var c = i;
        a[i] = function () {
            console.log(c);
        };
    }
    a[0](); // 9
    
    for (var i = 0, b=[]; i < 10; i++) {
        let c = i;  //使用let解决变量提升问题
        b[i] = function () {
            console.log(c);
        };
    }
    b[0](); // 0
    View Code

    const (只能在严格模式下使用)

    类似于var,声明一个“常量” —— 初始赋值后将无法修改其值的变量。

    也类似于let,只能在其所声明的块级作用域内来访问到:

    "use strict";  //使用严格模式
    {
        const i = 1;
    }
    console.log(typeof i);  //undefined
    {
        const a = 123;
        a = 567;   //报错,a是常量不能修改
    }

    块级作用域中的函数 (只能在严格模式下使用)

    严格模式下,函数本身的作用域,在其所处的块级作用域内,可以以此解决函数声明提升问题:

    "use strict";  //使用严格模式
    function f() { console.log('outside!'); }
    (function () {
        if(false) {
            // 重复声明一次函数f
            function f() { console.log('inside!'); }
        }
    
        f(); //严格模式(ES6)下输出outside;非严格模式(ES5)输出inside
    }());

    Map

    新的js集合类型,类似与对象,属于键值对的集合,但其“键”可以为任何类型,而不仅仅局限于字符串:

    var map = new Map(), //新建一个map对象
        o = {"a": 1, "b": 2},
        s = "sth";
    
    //map.set(key,val) 表示为该map对象添加一个新的键值对
    map.set(o,"ooo");
    map.set("a",123);
    map.set(null,"it`s null");
    map.set(s,o);
    
    //map.delete(key) 表示删除该map对象的某个键值对
    map.delete(s);
    
    //map.has(key) 表示检查该map对象是否存在某键名,返回对应的boolean值
    console.log(map.has(o));  //true
    console.log(map.has(s));  //false
    
    //map.has(key) 表示获取该map对象中某键名所对应的值
    console.log(map.get(o));  //"ooo"
    console.log(map.get("a"));  //123
    console.log(map.get(null));  //"it`s null"
    
    //map.clear() 表示清空map对象中的全部键值对
    map.clear();
    console.log(map.get(o));  //undefined

    Map支持链式写法,故上方的某段代码我们可以这么写:

    map.set(o,"ooo")
        .set("a",123)
        .set(null,"it`s null")
        .set(s,o)
        .delete(s);

    我们可以用 map.size 属性(而不是length)获取Map中键值对的个数:

    var map = new Map([
        [1, 'one'],
        [2, 'two'],
        [3, 'three']
    ]);
    
    console.log(map.size);  //3

    Map提供了三种遍历器:

      map.keys() //返回键名的遍历器
      map.values() //返回键值的遍历器
      map.entries() //返回所有成员的遍历器

    我们可以使用 for...of... 方法来遍历map的遍历器:

    "use strict";
    let map = new Map([
        ['F', 'no'],
        ['T',  'yes']
    ]);
    
    for (let key of map.keys()) {
        console.log(key);
    }
    // "F"
    // "T"
    
    for (let value of map.values()) {
        console.log(value);
    }
    // "no"
    // "yes"
    
    for (let item of map.entries()) {
        console.log(item[0], item[1]);
    }
    // "F" "no"
    // "T" "yes"
    
    // 或者
    let arr = [[],[]];
    for (arr of map.entries()) {
        console.log(arr[0], arr[1]);
    }
    
    // 等同于使用map.entries()
    for (arr of map) {
        console.log(arr[0], arr[1]);
    }

    其中map的entries方法等同为Map结构的默认遍历器接口(Symbol.iterator):

    console.log(map[Symbol.iterator] === map.entries);
    // true

    另外,我们也可以使用 forEach 方法来遍历Map对象:

    var map = new Map([
        [1, 'one'],
        [2, 'two'],
        [3, 'three']
    ]);
    
    map.forEach(function(value, key){
        console.log("Key: %s, Value: %s", key, value);
    });
    // Key: 1, Value: one
    // Key: 2, Value: two
    // Key: 3, Value: three

    WeakMap

    WeakMap结构与Map结构基本类似,区别是它只接受对象作为键名(null除外),不接受基础类型的值作为键名,而且键名所指向的对象,不计入垃圾回收机制。

    另外,WeakMap只有 get()、set()、has()、delete() 这么四个方法,而没有 clear() 、size属性和遍历器。

    WeakMap示例如下:

    var wm = new WeakMap(),
        o = {}; // o = document.getElementById("idname");
    wm.set(o,"DOM");
    
    console.log(wm.get(o));  //"DOM"

    WeakMap的设计目的在于,键名是对象的弱引用(垃圾回收机制不将该引用考虑在内),所以其所对应的对象可能会被自动回收。当对象被回收后,WeakMap自动移除对应的键值对。典型应用是,一个对应DOM元素的WeakMap结构,当某个DOM元素被清除,其所对应的WeakMap记录就会自动被移除。基本上,WeakMap的专用场合就是,它的键所对应的对象,可能会在将来消失。WeakMap结构有助于防止内存泄漏。

    Set

    类似于数组,但是成员的值都是唯一的,没有重复的值。另外Set也支持链式写法:

    var items = new Set([1,2,null,3,5,5,5,5]);  //重复的值将无效处理
    
    console.log(items.size);  //5
    
    //set.add(value) 表示添加一个set成员
    items.add(2).add("aaa");  //“2”已存在,将无效处理
    console.log(items.size);  //6
    
    //set.delete(value) 表示删除一个set成员
    items.delete(2);
    
    //set.has(value) 表示检查是否存在某成员,返回相应boolean值
    console.log(items.has(2)); //false
    
    items.clear(); //表示删除全部成员
    console.log(items.size);  //0

    跟Map一样,Set也有keys()、values()、entries() 三种遍历器,但遍历到的key名等同与value值。

    不过Set的默认遍历器接口是 values() :

    console.log(Set[Symbol.iterator] === Set.values);  //ture

    同样的,我们也可以通过 for...of... 和 forEach 来遍历Map:

    var items = new Set([1,2,null,3,5,5,5,5]); 
    
    var arr = [[],[]];
    for (arr of items.entries()) {
        console.log(arr[0], arr[1]);
    }
    
    items.forEach(function(value, key){
        console.log("Key: %s, Value: %s", key, value);
    });

    Set没有 .get(val) 方法,也不像数组那样可以直接用索引下标取值,常规只是把Set作为数据库索引使用(如为Redis提供索引和查询)。

    WeakSet

    了解了Map和WeakMap的关系之后,相信你也很容易理解WeakSet跟Set的关系。WeakSet类同于Set,但其成员只能是对象,而且WeakSet无法遍历也没有size属性。

    相比Set,WeakSet能使用的方法只有 add(val) 、delete(val)、has(val) :

    var ws = new WeakSet(),
        obj = {},
        foo = {};
    
    ws.add(foo);
    ws.add(obj);
    ws.delete(obj);
    console.log(ws.has(foo));    // true
    console.log(ws.has(obj));    // false

    同WeakMap一样,WeakSet中的对象都是弱引用,即垃圾回收机制不考虑WeakSet对该对象的引用,对象删除时,WeakSet对应引用该对象的成员也跟着删除。

    Generators

    Generator在ES6中是一个备受瞩目的一个函数类型,它通过 function * funName 的形式来声明一个Generator函数,并以 yield 语句来生成函数内部的遍历器成员。

    调用Generator函数时,函数并不执行,而是返回一个遍历器(可以理解成暂停执行)。以后,每次调用这个遍历器的next方法,就从函数体的头部或者上一次停下来的地方开始执行(可以理解成恢复执行),直到遇到下一个yield语句为止:

    function * gFun() {
        yield "第一个遍历器成员,第一次调用next()会停在这里"; //生成遍历器成员
        var i = 0;
        while (true){
            console.log("第"+i+"次循环");
            yield i++; //生成遍历器成员
            console.log("继续第"+i+"次循环");
        }
    }
    
    var g = gFun();  //没有.next()语句的调用,Generator函数不会执行,故不会造成while无限循环
    
    console.log(g.next().value);
    //"第一个遍历器成员,第一次调用next()会停在这里"   
    console.log(g.next().value);
    //第0次循环 
    //0
    console.log(g.next().value);
    //继续第1次循环
    //第1次循环
    //1

    .next().value返回了遍历器成员的值,常规为 “yield” 或 "return" 语句所生成/返回的值。

    .next() 方法除了 value 属性外,还有判断遍历是否结束的 done 属性,它返回一个表示当前遍历是否结束的boolean值:

    function * gFun() {
        yield "第一个遍历器成员";
        var i = 0;
        yield ++i;
    }
    
    var g = gFun();  //没有.next()语句的调用,Generator函数不会执行,故不会造成while无限循环
    
    console.log(g.next());
    //{ value: '第一个遍历器成员', done: false }
    console.log(g.next());
    //{ value: 1, done: false }
    console.log(g.next());
    //{ value: undefined, done: true }

    Generator函数里比较有意思的地方是,可以给 .next() 加一个参数,该参数数值就会覆盖掉上一个yield语句的返回值,我们可以利用该特性来进行一些有趣的运算:

    function* foo(x) {
        var y = 2 * (yield (x + 1));
        var z = yield (y / 3);
        return (x + y + z);
    }
    
    var g = foo(5);
    
    console.log(g.next());
    //{ value:6, done:false }
    console.log(g.next(12));  // (y  /3)=(2*12  /3)=8
    //{ value:8, done:false }
    console.log(g.next(13)); // (x  +  y  +  z)=(5  +  2*12  +  13)=42
    //{ value:42, done:true }

    二进制和八进制语法

    原先的JS对二进制/八进制的转换处理不太友好,我们得动用parseInt()或toString()方法,并使用对应的进制位参数:

    var num = 11;
    console.log(parseInt(num,2)); //把num作为二进制转为十进制
    //3
    console.log(parseInt(num,8)); //把num作为八进制转为十进制
    //9
    console.log(num.toString(2)); //把num作为十进制转为二进制
    //1011
    console.log(num.toString(8)); //把num作为十进制转为八进制
    //13

    在ES6中有些许改善,比如以“0b”开头表示二进制,以“0”开头表示八进制,以“0x”开头表示十六进制:

    console.log(0b1101); //13
    console.log(0B11); //3
    console.log(071); //57
    console.log(081); //81  (因为“8”超过了八进制可用数值,这里被当作十进制来转换)
    console.log(0x11); //17
    console.log(0X2A); //42

    Promise

    如果你熟悉jQuery的deferred对象,那么你会很轻松地理解ES6的Promise特性 —— 用于延迟、异步状态处理。

    一般Promise会有三种状态:

      待定(pending):初始状态,执行、等待中,没有被履行或拒绝。
      完成(fulfilled/resolved):操作成功
      拒绝(rejected):操作失败。

    Promise是一个构造函数,用来生成Promise实例。它接受一个函数作为参数,该函数又有两个参数——resolve方法和reject方法。如果异步操作成功,则用resolve方法将Promise实例的状态变为“成功”;如果异步操作失败,则用reject方法将状态变为“失败”。

    promise实例生成以后,可以用then方法分别指定resolve方法和reject方法的回调函数:

    var promise = new Promise(function (resolve, reject) {
        if (/* 异步操作成功 */) {
            resolve(value); //返回resolved状态并触发resolve回调
        } else {
            reject(error); //返回rejected状态并触发reject回调
        }
    });
    
    promise.then(function (value) {
        // resolve,即成功的回调
    }, function (error) {
        // reject,即失败的回调
    });

    这种机制对Node/io.js来说是非常有用的,因为我们知道,Node/io.js走的无阻塞异步I/O,js部分在V8执行,I/O部分在线程池做异步处理,如果我们希望在I/O操作成功或失败后执行相应的回调函数,以常规的做法不得不在事件的回调中继续嵌套I/O状态的回调。但Promise的出现,很好地解决了该问题。

    拿Ajax举个例子:

    var getJSON = function(url) {
        var promise = new Promise(function(resolve, reject){
            var client = new XMLHttpRequest();
            client.open("GET", url);
            client.onreadystatechange = handler;
            client.responseType = "json";
            client.setRequestHeader("Accept", "application/json");
            client.send();
    
            function handler() {
                if (this.status === 200) {
                    resolve(this.response);  //成功则触发resolve回调
                } else {
                    reject(new Error(this.statusText));  //失败则触发reject回调
                }
            }
        });
    
        return promise;
    };
    
    getJSON("/posts.json").then(function(json) { //resolve的回调
        console.log('Contents: ' + json);
    }, function(error) {  //reject的回调
        console.error('出错了', error);
    });

    .then() 方法返回的是一个新的Promise对象,它支持链式写法,可以让代码逻辑更清晰。如上述 getJSON() 执行的代码段可写为:

    getJSON("/posts.json").then(function (json) {
        console.log('Contents: ' + json);
    }, function (error) {
        console.error('出错了', error);
    }).then(function () { //在上个then执行完之后才会执行
        console.log("已经执行完并输出信息了")
    });

    Promise对象还有 .all() 和 .trace() 方法,Promise.all 方法接受一个数组作为参数,数组对象为不同的Promise对象:

    //接之前的代码段
    var promises = [getJSON("/post/a.json"), getJSON("/post/b.json"),getJSON("/post/c.json")];
    
    Promise.all(promises).then(function (json) {
        console.log('Contents: ' + json);
    }, function (error) {
        console.error('出错了', error);
    });

    当数组对象的状态都变为resolved时,Promise.all(promises)的状态也跟着变为resolved。如果其中一个数组对象的状态为rejected,那Promise.all(promises)则变为rejected状态。

    Promise.trace 方法跟 Promise.all 方法类似,不过只要Promise.trace的参数中的某一个对象率先改变了状态,那么 Promise.trace(promises) 的状态也会变为该状态。

    如上文提到的,作为trace 方法跟 all 方法的参数,要求其必须为Promise实例组成的数组,如果有非Promise实例的对象想加入到数组参数中,我们可以先通过 Promise.resolve 方法将其转为Promise实例:

    var ES6Promise = Promise.resolve($.ajax('/whatever.json')); //将一个jQuery deferred对象转化为ES6的Promise实例

    另外,我们可以使用 .catch() 来捕捉Promise对象的错误信息,要知道的是,Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获:

    getJSON("/post/1.json").then(function(post) {
      return getJSON(post.commentURL);
    }).then(function(comments) {
      // some code
    }).catch(function(error) {
      // 处理前两个回调函数的错误
    });

    限于篇幅,ES6特性将分为两篇文章来介绍,不便之处请谅解。

    另本文大部分内容参考自阮一峰老师的ES6入门,但本文实例已事先对所有代码进行了校正(包括在io.js上的兼容性、代码错误的堪正等)

    共勉~

    donate

  • 相关阅读:
    Java实现 LeetCode 50 Pow(x,n)
    Java实现 LeetCode 50 Pow(x,n)
    Java实现 LeetCode 49 字母异位词分组
    Java实现 LeetCode 49 字母异位词分组
    Java实现 LeetCode 49 字母异位词分组
    Java实现 LeetCode 48 旋转图像
    Java实现 LeetCode 48 旋转图像
    Java实现 LeetCode 48 旋转图像
    Java实现 LeetCode 47 全排列 II(二)
    Java实现 LeetCode 47 全排列 II(二)
  • 原文地址:https://www.cnblogs.com/vajoy/p/4229764.html
Copyright © 2011-2022 走看看