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