ECMAScript 6学习(五)

2017-02-22  本文已影响9人  Bui_vlee

本人是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()

上一篇下一篇

猜你喜欢

热点阅读