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...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()

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 一、let 和 constlet:變量聲明, const:只讀常量聲明(聲明的時候賦值)。 let 與 var 的...
    dadage456閱讀 768評論 0 0
  • //Clojure入門教程: Clojure – Functional Programming for the J...
    葡萄喃喃囈語閱讀 3,745評論 0 7
  • 1.概述 ES5的對象屬性名都是字符串,這容易造成屬性名的沖突。比如,你使用了一個他人提供的對象,但又想為這個對象...
    趙然228閱讀 814評論 2 10
  • [TOC] 參考阮一峰的ECMAScript 6 入門參考深入淺出ES6 let和const let和const都...
    郭子web閱讀 1,808評論 0 1
  • 強大的for-of循環 ES6不會破壞你已經寫好的JS代碼。目前看來,成千上萬的Web網站依賴for-in循環,其...
    Awe閱讀 7,528評論 2 7