摘要:它是語言的第七種數(shù)據(jù)類型前六種是布爾值字符串?dāng)?shù)值對(duì)象。為了防止沖突這就是引入的原因。指向了這個(gè)內(nèi)部方法調(diào)用了返回對(duì)象的屬性等于一個(gè)布爾值,表示該對(duì)象使用時(shí),是否可以展開。數(shù)組的默認(rèn)行為是可以展開返回對(duì)象的屬性,指向當(dāng)前對(duì)象的構(gòu)造函數(shù)。
es6學(xué)習(xí)筆記-Symbol_v1.0
基本抄了一次內(nèi)容,有很多只是知道其然并不知其所以然,不過也算是加深了一次印象,另外每段代碼我都有手動(dòng)執(zhí)行過.
ES6引入了一種新的原始數(shù)據(jù)類型Symbol,表示獨(dú)一無二的值。
它是JavaScript語言的第七種數(shù)據(jù)類型,前六種是:Undefined、Null、布爾值(Boolean)、字符串(String)、數(shù)值(Number)、對(duì)象(Object)。
ES5的對(duì)象屬性名都是字符串,這容易造成屬性名的沖突。比如,你使用了一個(gè)他人提供的對(duì)象,但又想為這個(gè)對(duì)象添加新的方法(mixin模式),新方法的名字就有可能與現(xiàn)有方法產(chǎn)生沖突。為了防止沖突,這就是ES6引入Symbol的原因。
Symbol值通過Symbol函數(shù)生成。這就是說,對(duì)象的屬性名現(xiàn)在可以有兩種類型,一種是原來就有的字符串,另一種就是新增的Symbol類型。凡是屬性名屬于Symbol類型,就都是獨(dú)一無二的,可以保證不會(huì)與其他屬性名產(chǎn)生沖突。
let s = Symbol(); typeof s // "symbol"
Symbol函數(shù)可以接受一個(gè)字符串作為參數(shù),表示對(duì)Symbol實(shí)例的描述,主要是為了在控制臺(tái)顯示,或者轉(zhuǎn)為字符串時(shí),比較容易區(qū)分。
var s1 = Symbol("foo"); var s2 = Symbol("bar"); s1 // Symbol(foo) s2 // Symbol(bar) s1.toString() // "Symbol(foo)" s2.toString() // "Symbol(bar)"
Symbol值不能與其他類型的值進(jìn)行運(yùn)算(可以轉(zhuǎn)為布爾值,但不能運(yùn)算)
Symbol值作為對(duì)象屬性名時(shí),不能用點(diǎn)運(yùn)算符。
作為屬性名的Symbolvar mySymbol = Symbol(); // 第一種寫法 var a = {}; a[mySymbol] = "Hello!"; // 第二種寫法 var a = { //在對(duì)象內(nèi)部必須要使用方括號(hào)包裹Symbol屬性 [mySymbol]: "Hello!" }; // 第三種寫法 var a = {}; //Object.defineProperty(obj, prop(需定義或修改的屬性的名字), descriptor(將被定義或修改的屬性的描述符)) Object.defineProperty(a, mySymbol, { value: "Hello!" }); // 以上寫法都得到同樣結(jié)果 a[mySymbol] // "Hello!"
增強(qiáng)對(duì)象寫法
let s = Symbol(); let obj = {//跟下面一樣 [s]: function (arg) { ... }//用方括號(hào) }; obj[s](123); //-------------------------- let obj = { //這是增強(qiáng)對(duì)象寫法 [s](arg) { ... } };跟常量配合使用
主要是為了保證常量不會(huì)相同
log.levels = { DEBUG: Symbol("debug"),//這里就是 INFO: Symbol("info"), WARN: Symbol("warn") }; log(log.levels.DEBUG, "debug message"); log(log.levels.INFO, "info message"); //-------------------------- const COLOR_RED = Symbol(); //比較巧妙的設(shè)置為一個(gè)唯一的常量 const COLOR_GREEN = Symbol(); function getComplement(color) { switch (color) { case COLOR_RED: return COLOR_GREEN; case COLOR_GREEN: return COLOR_RED; default: throw new Error("Undefined color"); } }實(shí)例:消除魔術(shù)字符串
魔術(shù)字符串指的是,在代碼之中多次出現(xiàn)、與代碼形成強(qiáng)耦合的某一個(gè)具體的字符串或者數(shù)值。風(fēng)格良好的代碼,應(yīng)該盡量消除魔術(shù)字符串,該由含義清晰的變量代替。
function getArea(shape, options) { var area = 0; switch (shape) { case "Triangle": // 魔術(shù)字符串 area = .5 * options.width * options.height; break; /* ... more code ... */ } return area; } getArea("Triangle", { width: 100, height: 100 }); // 魔術(shù)字符串
改成這樣
var shapeType = { triangle: "Triangle"http://這樣消除了強(qiáng)耦合 }; //更直接的方式使用Symbol保持唯一 const shapeType = { triangle: Symbol() }; //------------------------- function getArea(shape, options) { var area = 0; switch (shape) { case shapeType.triangle: area = .5 * options.width * options.height; break; } return area; } getArea(shapeType.triangle, { width: 100, height: 100 });屬性名的遍歷
Symbol 作為屬性名,該屬性不會(huì)出現(xiàn)在for...in、for...of循環(huán)中,也不會(huì)被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。但是,它也不是私有屬性,有一個(gè)Object.getOwnPropertySymbols方法,可以獲取指定對(duì)象的所有 Symbol 屬性名。
Object.getOwnPropertySymbols方法返回一個(gè)數(shù)組,成員是當(dāng)前對(duì)象的所有用作屬性名的 Symbol 值。
var obj = {}; var a = Symbol("a"); var b = Symbol("b"); obj[a] = "Hello"; obj[b] = "World"; var objectSymbols = Object.getOwnPropertySymbols(obj); console.log(objectSymbols) // 返回[Symbol(a), Symbol(b)] for (var i in obj) {//并不能使用這個(gè)方法獲取Symbol console.log(i); // 無輸出 } console.log(Object.getOwnPropertyNames(obj))//并不能使用這個(gè)方法獲取Symbol // 返回[]Reflect.ownKeys
Reflect.ownKeys方法可以返回所有類型的鍵名,包括常規(guī)鍵名和 Symbol 鍵名。
let obj = { [Symbol("my_key")]: 1, enum: 2, nonEnum: 3 }; console.log(Reflect.ownKeys(obj)) // 返回 ["enum", "nonEnum", Symbol(my_key)]Symbol.for()
Symbol.for方法,它接受一個(gè)字符串作為參數(shù),然后搜索有沒有以該參數(shù)作為名稱的Symbol值。如果有,就返回這個(gè)Symbol值,否則就新建并返回一個(gè)以該字符串為名稱的Symbol值。
var s1 = Symbol.for("foo"); var s2 = Symbol.for("foo"); console.log(s1 === s2) // true console.log(Symbol.for("bar") === Symbol.for("bar")) // true console.log(Symbol("bar") === Symbol("bar")) // false
實(shí)例:模塊的 Singleton 模式Symbol.for()與Symbol()這兩種寫法,都會(huì)生成新的Symbol。它們的區(qū)別是,前者會(huì)被登記在全局環(huán)境中供搜索,后者不會(huì)。Symbol.for()不會(huì)每次調(diào)用就返回一個(gè)新的 Symbol 類型的值,而是會(huì)先檢查給定的key是否已經(jīng)存在,如果不存在才會(huì)新建一個(gè)值。
Singleton模式指的是調(diào)用一個(gè)類,任何時(shí)候返回的都是同一個(gè)實(shí)例。
對(duì)于Node來說,模塊文件可以看成是一個(gè)類。怎么保證每次執(zhí)行這個(gè)模塊文件,返回的都是同一個(gè)實(shí)例呢?
把實(shí)例放到頂層對(duì)象global
// mod.js function A() { this.foo = "hello"; } if (!global._foo) { global._foo = new A(); } module.exports = global._foo;
變量a任何時(shí)候加載的都是A的同一個(gè)實(shí)例。
var a = require("./mod.js"); console.log(a.foo);
但是,這里有一個(gè)問題,全局變量global._foo是可寫的,任何文件都可以修改。
var a = require("./mod.js"); global._foo = 123;//修改了全局變量global._foo
上面的代碼,會(huì)使得別的腳本加載mod.js都失真。
為了防止這種情況出現(xiàn),我們就可以使用Symbol。
// mod.js const FOO_KEY = Symbol.for("foo"); //Symbol.for生成是為了有些時(shí)候可以重新使用這個(gè)Symbol值 function A() { this.foo = "hello"; } if (!global[FOO_KEY]) { global[FOO_KEY] = new A(); } module.exports = global[FOO_KEY];
上面代碼中,可以保證global[FOO_KEY]不會(huì)被無意間覆蓋,但還是可以被改寫。
var a = require("./mod.js"); global[Symbol.for("foo")] = 123;
如果鍵名使用Symbol方法生成,那么外部將無法引用這個(gè)值,當(dāng)然也就無法改寫。
// mod.js const FOO_KEY = Symbol("foo");//保證每次都是唯一,因?yàn)镾ymbol生成 // 后面代碼相同 ……
上面代碼將導(dǎo)致其他腳本都無法引用FOO_KEY。但這樣也有一個(gè)問題,就是如果多次執(zhí)行這個(gè)腳本,每次得到的FOO_KEY都是不一樣的。雖然Node會(huì)將腳本的執(zhí)行結(jié)果緩存,一般情況下,不會(huì)多次執(zhí)行同一個(gè)腳本,但是用戶可以手動(dòng)清除緩存,所以也不是完全可靠。
內(nèi)置的Symbol值 Symbol.hasInstance對(duì)象的Symbol.hasInstance屬性,指向一個(gè)內(nèi)部方法。當(dāng)其他對(duì)象使用instanceof運(yùn)算符,判斷是否為該對(duì)象的實(shí)例時(shí),會(huì)調(diào)用這個(gè)方法。比如,foo instanceof Foo在語言內(nèi)部,實(shí)際調(diào)用的是Foo[Symbol.hasInstance](foo)。
class MyClass { [Symbol.hasInstance](foo) {//instanceof指向了這個(gè)內(nèi)部方法 return foo instanceof Array; } } //調(diào)用了instanceof console.log([1, 2, 3] instanceof new MyClass()) //返回 trueSymbol.isConcatSpreadable
對(duì)象的Symbol.isConcatSpreadable屬性等于一個(gè)布爾值,表示該對(duì)象使用Array.prototype.concat()時(shí),是否可以展開。
數(shù)組的默認(rèn)行為是可以展開
let arr1 = ["c", "d"]; console.log(["a", "b"].concat(arr1, "e")) // ["a", "b", "c", "d", "e"] console.log(arr1[Symbol.isConcatSpreadable]) // undefined let arr2 = ["c", "d"]; arr2[Symbol.isConcatSpreadable] = false; console.log(arr2[Symbol.isConcatSpreadable]);//返回false console.log(["a", "b"].concat(arr2, "e")) // ["a", "b", ["c","d"], "e"]Symbol.species
對(duì)象的Symbol.species屬性,指向當(dāng)前對(duì)象的構(gòu)造函數(shù)。創(chuàng)造實(shí)例時(shí),默認(rèn)會(huì)調(diào)用這個(gè)方法,即使用這個(gè)屬性返回的函數(shù)當(dāng)作構(gòu)造函數(shù),來創(chuàng)造新的實(shí)例對(duì)象。
class MyArray extends Array { // 覆蓋父類 Array 的構(gòu)造函數(shù) //所以他是由當(dāng)前類MyArray創(chuàng)建的 static get [Symbol.species]() { return this; }//靜態(tài)方法并且要用get來獲取Symbol.species屬性 } var a = new MyArray(1,2,3); var mapped = a.map(x => x * x); //雖然是由MyArray來創(chuàng)建的,但是因?yàn)樗彩羌勺訟rray,所以都是true console.log(mapped instanceof MyArray) // true console.log(mapped instanceof Array) // true
//如果改成了返回Array的話,那么結(jié)果的是Array為true static get [Symbol.species]() { return Array; }Symbol.match
對(duì)象的Symbol.match屬性,指向一個(gè)函數(shù)。當(dāng)執(zhí)行str.match(myObject)時(shí),如果該屬性存在,會(huì)調(diào)用它,返回該方法的返回值。
String.prototype.match(regexp) // 等同于 regexp[Symbol.match](this)
class MyMatcher { [Symbol.match](string) { return "hello world".indexOf(string); } } "e".match(new MyMatcher()) // 1(從0開始計(jì)算第一位,1是第二位)Symbol.replace
對(duì)象的Symbol.replace屬性,指向一個(gè)方法,當(dāng)該對(duì)象被String.prototype.replace方法調(diào)用時(shí),會(huì)返回該方法的返回值。
String.prototype.replace(searchValue, replaceValue) // 等同于 searchValue[Symbol.replace](this, replaceValue) 下面是一個(gè)例子。
const obj = {}; obj[Symbol.replace] = (...s) => console.log(s); "Hello".replace(obj, "World") // ["Hello", "World"]
Symbol.replace方法會(huì)收到兩個(gè)參數(shù),第一個(gè)參數(shù)是replace方法正在作用的對(duì)象,上面例子是Hello,第二個(gè)參數(shù)是替換后的值,上面例子是World。
Symbol.search對(duì)象的Symbol.search屬性,指向一個(gè)方法,當(dāng)該對(duì)象被String.prototype.search方法調(diào)用時(shí),會(huì)返回該方法的返回值。
String.prototype.search(regexp) // 等同于 regexp[Symbol.search](this)
class MySearch { constructor(value) { this.value = value; } [Symbol.search](string) { return string.indexOf(this.value); } } console.log("foobar".search(new MySearch("foo"))) // 0Symbol.split
對(duì)象的Symbol.split屬性,指向一個(gè)方法,當(dāng)該對(duì)象被String.prototype.split方法調(diào)用時(shí),會(huì)返回該方法的返回值。
String.prototype.split(separator, limit) // 等同于 separator[Symbol.split](this, limit)
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) ]; } } console.log("foobar".split(new MySplitter("foo"))) // ["", "bar"] console.log("foobar".split(new MySplitter("bar"))) // ["foo", ""] console.log("foobar".split(new MySplitter("baz"))) // "foobar"
上面方法使用Symbol.split方法,重新定義了字符串對(duì)象的split方法的行為
Symbol.iterator對(duì)象的Symbol.iterator屬性,指向該對(duì)象的默認(rèn)遍歷器方法。
var myIterable = {}; myIterable[Symbol.iterator] = function* () { yield 1; yield 2; yield 3; }; [...myIterable] // [1, 2, 3]
對(duì)象進(jìn)行for...of循環(huán)時(shí),會(huì)調(diào)用Symbol.iterator方法,返回該對(duì)象的默認(rèn)遍歷器,
Symbol.toPrimitive對(duì)象的Symbol.toPrimitive屬性,指向一個(gè)方法。該對(duì)象被轉(zhuǎn)為原始類型的值時(shí),會(huì)調(diào)用這個(gè)方法,返回該對(duì)象對(duì)應(yīng)的原始類型值。
Symbol.toPrimitive被調(diào)用時(shí),會(huì)接受一個(gè)字符串參數(shù),表示當(dāng)前運(yùn)算的模式,一共有三種模式。
Number:該場(chǎng)合需要轉(zhuǎn)成數(shù)值
String:該場(chǎng)合需要轉(zhuǎn)成字符串
Default:該場(chǎng)合可以轉(zhuǎn)成數(shù)值,也可以轉(zhuǎn)成字符串
let obj = { [Symbol.toPrimitive](hint) { switch (hint) { case "number": return 123; case "string": return "str"; case "default": return "default"; default: throw new Error(); } } }; console.log(2 * obj) // 246 console.log(3 + obj) // "3default" console.log(obj == "default") // true console.log(String(obj)) // "str"Symbol.toStringTag
對(duì)象的Symbol.toStringTag屬性,指向一個(gè)方法。在該對(duì)象上面調(diào)用Object.prototype.toString方法時(shí),如果這個(gè)屬性存在,它的返回值會(huì)出現(xiàn)在toString方法返回的字符串之中,表示對(duì)象的類型。也就是說,這個(gè)屬性可以用來定制[object Object]或[object Array]中object后面的那個(gè)字符串。
// 例一 console.log(({[Symbol.toStringTag]: "Foo"}.toString())) // "[object Foo]" // 例二 class Collection { get [Symbol.toStringTag]() { return "xxx"; } } var x = new Collection(); console.log(Object.prototype.toString.call(x)) // "[object xxx]"
ES6新增內(nèi)置對(duì)象的Symbol.toStringTag屬性值如下。
JSON[Symbol.toStringTag]:"JSON" Math[Symbol.toStringTag]:"Math" Module對(duì)象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
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/92367.html
摘要:學(xué)習(xí)模塊不是對(duì)象,而是通過命令顯式指定輸出的代碼,輸入時(shí)也采用靜態(tài)命令的形式。的模塊自動(dòng)采用嚴(yán)格模式命令用于規(guī)定模塊的對(duì)外接口,命令用于輸入其他模塊提供的功能。該文件內(nèi)部的所有變量,外部無法獲取。 es6 學(xué)習(xí)-module_v1.0 ES6模塊不是對(duì)象,而是通過export命令顯式指定輸出的代碼,輸入時(shí)也采用靜態(tài)命令的形式。 ES6的模塊自動(dòng)采用嚴(yán)格模式 export命令用于規(guī)定模...
摘要:學(xué)習(xí)筆記頂層對(duì)象雖然是筆記但是基本是抄了一次大師的文章了頂層對(duì)象頂層對(duì)象,在瀏覽器環(huán)境指的是對(duì)象,在指的是對(duì)象。之中,頂層對(duì)象的屬性與全局變量是等價(jià)的。的寫法模塊的寫法上面代碼將頂層對(duì)象放入變量。參考引用頂層對(duì)象實(shí)戰(zhàn) es6學(xué)習(xí)筆記-頂層對(duì)象_v1.0 (雖然是筆記,但是基本是抄了一次ruan大師的文章了) 頂層對(duì)象 頂層對(duì)象,在瀏覽器環(huán)境指的是window對(duì)象,在Node指的是gl...
摘要:考慮到環(huán)境導(dǎo)致的行為差異太大,應(yīng)該避免在塊級(jí)作用域內(nèi)聲明函數(shù)。函數(shù)聲明語句函數(shù)表達(dá)式循環(huán)循環(huán)還有一個(gè)特別之處,就是循環(huán)語句部分是一個(gè)父作用域,而循環(huán)體內(nèi)部是一個(gè)單獨(dú)的子作用域。聲明一個(gè)只讀的常量。 es6學(xué)習(xí)筆記-let,const和塊級(jí)作用域_v1.0 塊級(jí)作用域 javascript 原來是沒有塊級(jí)作用域的,只有全局作用域和函數(shù)作用域 例子1 因?yàn)闆]有塊級(jí)作用域,所以每次的i都是一...
摘要:學(xué)習(xí)筆記數(shù)值的擴(kuò)展有一些不常用或者還不支持的就沒有記錄了總體來說本篇只是一個(gè)備忘而已用來檢查一個(gè)數(shù)值是否為有限的。兩個(gè)新方法只對(duì)數(shù)值有效,非數(shù)值一律返回。參考引用數(shù)值擴(kuò)展 es6學(xué)習(xí)筆記-數(shù)值的擴(kuò)展 有一些不常用或者還不支持的就沒有記錄了,總體來說本篇只是一個(gè)備忘而已 Number.isFinite(), Number.isNaN() Number.isFinite()用來檢查一個(gè)數(shù)值...
摘要:因?yàn)榧^函數(shù)本身沒有所以不可以當(dāng)作構(gòu)造函數(shù),也就是說,不可以使用命令,否則會(huì)拋出一個(gè)錯(cuò)誤。箭頭函數(shù)不可以使用對(duì)象,該對(duì)象在函數(shù)體內(nèi)不存在。 es6學(xué)習(xí)筆記-箭頭函數(shù)_v1.0 箭頭函數(shù)使用方法 var f = v => v; //普通函數(shù)配合箭頭函數(shù)寫法,這里并且是傳參的 //相當(dāng)于 var f = function(v) { return v; }; /*-----------...
閱讀 3177·2021-11-22 13:54
閱讀 3477·2021-11-15 11:37
閱讀 3633·2021-10-14 09:43
閱讀 3539·2021-09-09 11:52
閱讀 3642·2019-08-30 15:53
閱讀 2493·2019-08-30 13:50
閱讀 2086·2019-08-30 11:07
閱讀 913·2019-08-29 16:32