美文网首页
ECMAScript 6学习(五)

ECMAScript 6学习(五)

作者: Bui_vlee | 来源:发表于2017-02-22 13:00 被阅读9次

    本人是android开发的,由于最近React Native的火热,再加上自己完全不懂JS的语法,俗话说的好"落后就要挨打",虽然不知道谁说的,不过很有道理.

    学习书籍《ECMAScript 6 入门 》

    Symbol和Set、Map


    Symbol

    ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。它是JavaScript语言的第七种数据类型,前六种是:Undefined、Null、布尔值(Boolean)、字符串(String)、数值(Number)、对象(Object)。

    Symbol值通过Symbol函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的Symbol类型。凡是属性名属于Symbol类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。

    Symbol 作为属性名,该属性不会出现在for...infor...of循环中,也不会被Object.keys()Object.getOwnPropertyNames()JSON.stringify()返回。它通过Object.getOwnPropertySymbols方法返回一个数组,成员是当前对象的所有用作属性名的 Symbol 值。

    var obj = {};

    var a = Symbol('a');

    var b = Symbol('b');

    obj[a] = 'Hello';

    obj[b] = 'World';

    var objectSymbols = Object.getOwnPropertySymbols(obj);

    objectSymbols

    // [Symbol(a), Symbol(b)]

    另一个新的API,Reflect.ownKeys方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。

    let obj = {

    [Symbol('my_key')]: 1,

    enum: 2,

    nonEnum: 3

    };

    Reflect.ownKeys(obj)

    //  ["enum", "nonEnum", Symbol(my_key)]


    Symbol.for(),Symbol.keyFor()

    有时,我们希望重新使用同一个Symbol值,Symbol.for方法可以做到这一点。它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的Symbol值。如果有,就返回这个Symbol值,否则就新建并返回一个以该字符串为名称的Symbol值。

    var s1 = Symbol.for('foo');

    var s2 = Symbol.for('foo');

    s1 === s2 // true

    Symbol.for()Symbol()这两种写法,都会生成新的Symbol。它们的区别是,前者会被登记在全局环境中供搜索,后者不会。Symbol.for()不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的key是否已经存在,如果不存在才会新建一个值。比如,如果你调用Symbol.for("cat")30次,每次都会返回同一个 Symbol 值,但是调用Symbol("cat")30次,会返回30个不同的Symbol值。

    Symbol.for("bar") === Symbol.for("bar")

    // true

    Symbol("bar") === Symbol("bar")

    // false

    Symbol.keyFor方法返回一个已登记的 Symbol 类型值的key,未登记的Symbol值,返回undefined.

    var s1 = Symbol.for("foo");

    Symbol.keyFor(s1) // "foo"

    var s2 = Symbol("foo");

    Symbol.keyFor(s2) // undefined

    需要注意的是,Symbol.for为Symbol值登记的名字,是全局环境的,可以在不同的 iframe 或 service worker 中取到同一个值。


    内置的Symbol值

    Symbol.hasInstance  当其他对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法。

    class Even {

    static [Symbol.hasInstance](obj) {

         return Number(obj) % 2 === 0;

        }

    }

    1 instanceof Even // false

    2 instanceof Even // true

    12345 instanceof Even // false


    Symbol.isConcatSpreadable  表示该对象使用Array.prototype.concat()时,是否可以展开。

    Symbol.isConcatSpreadable 属性等于trueundefined,可以展开。

    Symbol.isConcatSpreadable 属性等于false,不可以展开。

    let arr1 = ['c', 'd'];

    ['a', 'b'].concat(arr1, 'e') // ['a', 'b', 'c', 'd', 'e']

    arr1 [Symbol.isConcatSpreadable] // undefined

    let arr2 = ['c', 'd'];

    arr2[Symbol.isConcatSpreadable] = false;

    ['a', 'b'].concat(arr2, 'e') // ['a', 'b', ['c','d'], 'e']


    Symbol.species 指向当前对象的构造函数。

    class MyArray extends Array {

    static get [Symbol.species]() { return Array; }

    }

    var a = new MyArray(1,2,3);

    var mapped = a.map(x => x * x);

    mapped instanceof MyArray // false

    mapped instanceof Array // true

    上面代码中,由于构造函数被替换成了Array。所以,mapped对象不是MyArray的实例,而是Array的实例。


    Symbol.match 指向一个函数。当执行str.match(myObject)时,如果该属性存在,会调用它,返回该方法的返回值。

    String.prototype.match(regexp)

    // 等同于

    regexp[Symbol.match](this)

    class MyMatcher {

       [Symbol.match](string) {

           return 'hello world'.indexOf(string);

        }

    }

    'e'.match(new MyMatcher()) // 1


    Symbol.replace 指向一个方法,当该对象被String.prototype.replace方法调用时,会返回该方法的返回值。

    Symbol.replace方法会收到两个参数,第一个参数是replace方法正在作用的对象,下面例子是Hello,第二个参数是替换后的值,上面例子是World

    const x = {};

    x[Symbol.replace] = (...s) => console.log(s);

    'Hello'.replace(x, 'World') // ["Hello", "World"]


    Symbol.search 指向一个方法,当该对象被String.prototype.search方法调用时,会返回该方法的返回值。

    String.prototype.search(regexp)

    // 等同于

    regexp[Symbol.search](this)

    class MySearch {

        constructor(value) {

             this.value = value;

        }

         [Symbol.search](string) {

              return string.indexOf(this.value);

          }

    }

    'foobar'.search(new MySearch('foo')) // 0


    Symbol.split 指向一个方法,当该对象被String.prototype.split方法调用时,会返回该方法的返回值。

    class MySplitter {

        constructor(value) {

            this.value = value;

        }

    [Symbol.split](string) {

       var index = string.indexOf(this.value);

       if (index === -1) {

             return string;

        }

       return [

         string.substr(0, index),

         string.substr(index + this.value.length)

       ];

      }

    }

    'foobar'.split(new MySplitter('foo'))

    // ['', 'bar']

    'foobar'.split(new MySplitter('bar'))

    // ['foo', '']

    'foobar'.split(new MySplitter('baz'))

    // 'foobar'


    Symbol.iterator 指向该对象的默认遍历器方法。

    var myIterable = {};

    myIterable[Symbol.iterator] = function* () {

          yield 1;

          yield 2;

          yield 3;

    };

    [...myIterable] // [1, 2, 3]


    Symbol.toPrimitive 指向一个方法。该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值。

    Symbol.toPrimitive被调用时,会接受一个字符串参数,表示当前运算的模式,一共有三种模式。

    Number:该场合需要转成数值

    String:该场合需要转成字符串

    Default:该场合可以转成数值,也可以转成字符串

    let obj = {

    [Symbol.toPrimitive](hint) {

         switch (hint) {

                  case 'number':

                           return 123;

                  case 'string':

                           return 'str';

                  case 'default':

                            return 'default';

                   default:

                            throw new Error();

                }

           }

    };

    2 * obj // 246

    3 + obj // '3default'

    obj == 'default' // true

    String(obj) // 'str'


    Symbol.toStringTag 指向一个方法。在该对象上面调用Object.prototype.toString方法时,如果这个属性存在,它的返回值会出现在toString方法返回的字符串之中,表示对象的类型。也就是说,这个属性可以用来定制[object Object][object Array]object后面的那个字符串。

    class Collection {

        get [Symbol.toStringTag]() {

               return 'xxx';

         }

    }

    var x = new Collection();

    Object.prototype.toString.call(x) // "[object xxx]"

    ES6新增内置对象的Symbol.toStringTag属性值如下。

    JSON[Symbol.toStringTag]:'JSON'

    Math[Symbol.toStringTag]:'Math'

    Module对象M[Symbol.toStringTag]:'Module'

    ArrayBuffer.prototype[Symbol.toStringTag]:'ArrayBuffer'

    DataView.prototype[Symbol.toStringTag]:'DataView'

    Map.prototype[Symbol.toStringTag]:'Map'

    Promise.prototype[Symbol.toStringTag]:'Promise'

    Set.prototype[Symbol.toStringTag]:'Set'

    %TypedArray%.prototype[Symbol.toStringTag]:'Uint8Array'等

    WeakMap.prototype[Symbol.toStringTag]:'WeakMap'

    WeakSet.prototype[Symbol.toStringTag]:'WeakSet'

    %MapIteratorPrototype%[Symbol.toStringTag]:'Map Iterator'

    %SetIteratorPrototype%[Symbol.toStringTag]:'Set Iterator'

    %StringIteratorPrototype%[Symbol.toStringTag]:'String Iterator'

    Symbol.prototype[Symbol.toStringTag]:'Symbol'

    Generator.prototype[Symbol.toStringTag]:'Generator'

    GeneratorFunction.prototype[Symbol.toStringTag]:'GeneratorFunction'


    Symbol.unscopables 指向一个对象。该对象指定了使用with关键字时,哪些属性会被with环境排除。

    // 没有 unscopables 时

     class MyClass {

         foo() { return 1; }

    }

    var foo = function () { return 2; };

    with (MyClass.prototype) {

         foo(); // 1

    }


    // 有 unscopables 时

    class MyClass {

    foo() { return 1; }

       get [Symbol.unscopables]() {

          return { foo: true };

        }

    }

    var foo = function () { return 2; };

    with (MyClass.prototype) {

        foo(); // 2

    }

    上面代码通过指定Symbol.unscopables属性,使得with语法块不会在当前作用域寻找foo属性,即foo将指向外层作用域的变量。


    Set和Map

    这两个数据结构我就不多说和java差不多.

    1.Set

    Set 类似于数组,但是成员的值都是唯一的,没有重复的值。Set 函数可以接受一个数组(或类似数组的对象)作为参数,用来初始化。

    Set 书写格式如下:

    const s = new Set();  //Set初始化;

    var set = new Set([1,2,3,4,4]); // Set初始化并接受一个数组(或类似数组的对象);

    [2,3,5,4,5,2,2].forEach (x => s.add(x)); // Set通过add添加数据

    Set实例的属性和方法

    属性:

     ---- Set.prototype.constructor:构造函数,默认就是Set函数。

     ---- Set.prototype.size:返回Set实例的成员总数。

    方法:

    -- 操作方法(用于操作数据)

     ---- add(value):添加某个值,返回Set结构本身。

     ---- delete(value):删除某个值,返回一个布尔值,表示删除是否成功。

     ---- has(value):返回一个布尔值,表示该值是否为Set的成员。

     ---- clear():清除所有成员,没有返回值。


    -- 遍历方法(用于遍历成员)

     ---- keys():返回键名的遍历器

     ---- values():返回键值的遍历器

     ---- entries():返回键值对的遍历器

     ---- forEach():使用回调函数遍历每个成员


    2.WeakSet

    WeakSet结构与Set类似,也是不重复的值的集合。但是,它与Set有两个区别。

    首先,WeakSet的成员只能是对象,而不能是其他类型的值。

    其次,WeakSet中的对象都是弱引用,即垃圾回收机制不考虑WeakSet对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于WeakSet之中。这个特点意味着,无法引用WeakSet的成员,因此WeakSet是不可遍历的

    WeakSet可以接受一个数组或类似数组的对象作为参数。

    var ws = new WeakSet();

    var a = [[1,2],[3,4]]; 

    var ws = new WeakSet(a);

    注意,是a数组的成员成为WeakSet的成员,而不是a数组本身。这意味着,数组的成员只能是对象。

    WeakSet 结构有以下三个方法。

     ---- WeakSet.prototype.add(value):向WeakSet实例添加一个新成员。

     ---- WeakSet.prototype.delete(value):清除WeakSet实例的指定成员。

     ---- WeakSet.prototype.has(value):返回一个布尔值,表示某个值是否在.


    3.Map

    Map数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object结构提供了“字符串—值”的对应,Map结构提供了“值—值”的对应,是一种更完善的Hash结构实现。如果你需要“键值对”的数据结构,Map比Object更合适。

    Map实例的属性和方法

    size属性:返回Map结构的成员总数。


    set(key, value)

           set方法设置key所对应的键值,然后返回整个Map结构。如果key已经有值,则键值会被更新,否则就新生成该键。

           set方法返回的是Map本身,因此可以采用链式写法。

    let map = new Map()

    .set(1, 'a')

    .set(2, 'b')

    .set(3, 'c');


    get(key)

            get方法读取key对应的键值,如果找不到key,返回undefined

    var m = new Map();

    var hello = function() {console.log("hello");}

    m.set(hello, "Hello ES6!") // 键是函数

    m.get(hello)  // Hello ES6!


    has(key)

            has方法返回一个布尔值,表示某个键是否在Map数据结构中。

    var m= new Map();

    m.set("edition",6);

    m.has("edition") // true

    m.has("years")  // false


    delete(key)

            delete方法删除某个键,返回true。如果删除失败,返回false。

    var m = new Map();

    m.set(undefined, "nah");

    m.has(undefined)    // true

    m.delete(undefined)

    m.has(undefined)      // false


    clear()

            clear方法清除所有成员,没有返回值。


    Map遍历方法

     ---- keys():返回键名的遍历器。

     ---- values():返回键值的遍历器。

     ---- entries():返回所有成员的遍历器。

     ---- forEach():遍历Map的所有成员。


    4.WeakMap

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

    WeakMapMap在API上的区别主要是两个,一是没有遍历操作(即没有key()values()entries()方法),也没有size属性;二是无法清空,即不支持clear方法。这与WeakMap的键不被计入引用、被垃圾回收机制忽略有关。因此,WeakMap只有四个方法可用:get()set()has()delete()

    相关文章

      网友评论

          本文标题:ECMAScript 6学习(五)

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