ECMAScript 6学习(五)
本人是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...in、for...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 属性等于true或undefined,可以展开。
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除外),不接受其他类型的值作为键名,而且键名所指向的对象,不计入垃圾回收机制,不可遍历。
WeakMap与Map在API上的区别主要是两个,一是没有遍历操作(即没有key()、values()和entries()方法),也没有size属性;二是无法清空,即不支持clear方法。这与WeakMap的键不被计入引用、被垃圾回收机制忽略有关。因此,WeakMap只有四个方法可用:get()、set()、has()、delete()。