日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ES6之Symbol详解

發(fā)布時間:2023/12/16 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ES6之Symbol详解 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

目錄

  • 一、什么是Symbol?
  • 二、Symbol的作用
  • 三、Symbol的語法規(guī)范
    • 1. 基本語法
    • 2. Symbol屬性的遍歷
    • 3. Symbol.for(),Symbol.keyFor()
  • 四、內(nèi)置的Symbol值
    • 1. Symbol.hasInstance
    • 2. Symbol.isConcatSpreadable
    • 3. Symbol.species
    • 4. Symbol.match/replace/search/split
    • 5. Symbol.iterator
    • 6. Symbol.toPrimitive
    • 7. Symbol.toStringTag
    • 8. Symbol.unscopables
  • 總結(jié)

一、什么是Symbol?

Symbol是ES6中引入的一種新的基本數(shù)據(jù)類型,用于表示一個獨一無二的值。它是JavaScript中的第七種數(shù)據(jù)類型,與undefined、null、Number(數(shù)值)、String(字符串)、Boolean(布爾值)、Object(對象)并列。

你可以這樣創(chuàng)建一個Symbol值:

const a = Symbol();console.log(a); //Symbol()

使用Symbol函數(shù)可以生成一個Symbol類型的值,但是你不能在調(diào)用Symbol時使用new關(guān)鍵字,因為Symbol是基本數(shù)據(jù)類型,而不是對象。比如下面的寫法是錯誤的:

//報錯,Symbol is not a constructor const a = new Symbol();

使用Symbol()創(chuàng)建一個Symbol類型的值并賦值給a變量后,你就得到了一個在內(nèi)存中獨一無二的值。現(xiàn)在除了通過變量a,任何人在任何作用域內(nèi)都無法重新創(chuàng)建出這個值。例如當(dāng)你這樣寫:

const b = Symbol();

盡管a和b都是使用Symbol()創(chuàng)建出來的,但是它們在內(nèi)存中看起來卻是這樣的:

實際上,a變量拿到了內(nèi)存中某塊內(nèi)存的唯一引用(這里所說的引用,其實就是該內(nèi)存的地址)。如果不借助a變量,你不可能再得到這個地址。因此:

a !== b; //a和b持有的是兩塊內(nèi)存的引用const c = a; //手動把a(bǔ)里保存的地址保存在c變量中 a === c; //c和a現(xiàn)在指向同一塊內(nèi)存,因為它們保存了同樣的地址


這種行為看似難以理解,但其實它與對象遵循相同的規(guī)則,如:

var a = {}; var b = {};a !== b; //a和b各自被分配了不同的內(nèi)存,因此它們保存了不同的地址//借助變量a,變量c拿到了a指向的那個對象的地址,因此兩者相等 var c = a; a === c;

但是對于同為基本數(shù)據(jù)類型的字符串來說,它不遵循類似的規(guī)則。

比如:

var a = "123"; var b = "123";a === b; //返回true。兩者在常量區(qū)引用同一個字符串


我們首先通過變量a在內(nèi)存中創(chuàng)建了字符串“123”,然后在不借助變量a的情況下,又通過var b = "123"拿到了對“123”這個字符串的引用,兩者指向內(nèi)存中的同一塊內(nèi)存地址。

因此我們說,a無法確保別的變量無法拿到它保存的地址(前提是不通過a)。但是對于var a = Symbol()這樣的語句,a變量內(nèi)保存的值是唯一的,因為除了借助a變量,你永遠(yuǎn)無法得到a中保存的值。這也是Symbol的本質(zhì)。

可能很多人比較奇怪,一個Symbol類型的變量里面到底保存了什么呢?

我們看兩行代碼:

var a = Symbol();console.log(a); //Symbol()

我們試圖輸出a的值,但js引擎輸出了Symbol()。顯然它不能說明a的值是字符串,因為:

typeof a === "symbol";

所以說如果你想問js引擎a的值是多少,引擎只會告訴你它是一個Symbol類型的值。也就是說,Symbol真正存儲了什么并不重要,重要的是它的值永遠(yuǎn)不會與別的值相等。Symbol的中文釋義為“標(biāo)志,符號”,一個Symbol類型的變量只是為了標(biāo)記一塊唯一的內(nèi)存而存在的。也正是因為這樣,Symbol類型的值不參與運算。

二、Symbol的作用

上面我們說到,Symbol只是標(biāo)記一塊內(nèi)存,不能與其他數(shù)據(jù)類型進(jìn)行運算,那么新增這樣一種數(shù)據(jù)類型有什么用呢?

舉個例子:

//文件A.js var a = {name: "夕山雨",getName(){return this.name;} } exports default a;//文件B.js var a = require("A.js"); a.getName = function(){return "xxx"; }

由于getName這個鍵本質(zhì)上只是個字符串,而無論在哪個模塊或作用域內(nèi),都可以直接引用到“getName”這個字符串,因此字符串類型的屬性很容易被意外覆蓋。

但是如果a中的屬性是使用Symbol類型的變量作為鍵,那么它就無法被篡改:

//模塊A.js var s = Symbol(); var a = {name: "夕山雨",//s是個變量,因此需要用中括號包裹起來[s]: function(){ return this.name;} } exports default a;//模塊B.js var a = require("A.js"); var s = Symbol();a[s] = function(){... //它不會對A模塊中的[s]屬性造成任何影響,因為兩個模塊的[s]不是同一個屬性 }

現(xiàn)在,我們使用一個Symbol類型的變量作為對象屬性的鍵。由于s是一個變量,而不是字符串,因此需要使用中括號括起來(否則它會被當(dāng)做字符串對待)。

在模塊B中我們使用同樣的語句var s = Symbol();創(chuàng)建了一個同名變量s,“企圖”通過為a[s]重新賦值覆蓋a對象上原來的[s]屬性,但這并不能生效,因為模塊A中的變量s和模塊B中的變量s是各自獨立的Symbol,他們并不相等,因此無法覆蓋。

根本原因在任何情況下都滿足:

"getName" === "getName" //而 Symbol() !== Symbol() //該行為類似{} !== {}

通過把對象的屬性的鍵值設(shè)置為Symbol類型,我們有效避免了對象屬性被修改,在模塊化開發(fā)中,對象本身也就更安全。

現(xiàn)在,在模塊A中,我們可以像訪問普通屬性一樣用a[s]訪問該屬性,在其他模塊中,由于引用不到變量s,因此不可以直接訪問該屬性。此時的內(nèi)存引用情況大致如下:

內(nèi)存形成過程為:

  • var s = Symbol();語句在內(nèi)存中創(chuàng)建了一個Symbol類型的變量,并將地址保存在變量s中。

  • var a = {
    [s]: function(){ … }
    }
    該語句為對象a添加了一個[s]屬性。在內(nèi)存中,js引擎首先需要開辟一塊內(nèi)存保存這個匿名函數(shù),然后在對象a中添加一對鍵值對,它的鍵是變量s所指向的內(nèi)存地址,而值是上述匿名函數(shù)在內(nèi)存中的地址。

  • 通常來說,如果想要修改對象的某個屬性,那么你首先需要獲得這個屬性的鍵,參考上面的內(nèi)存圖,實際上就是獲得這個鍵在內(nèi)存中的地址(也就是變量s指向的那個內(nèi)存區(qū))。

    傳統(tǒng)方式下,我們以字符串作為對象屬性的鍵。這樣,我們只要能得到這個字符串在內(nèi)存中的地址,就可以訪問該屬性。由于同一個字符串只會在常量區(qū)生成一次,因此我們可以在任何時候通過以下方式得到“getName”這個字符串在內(nèi)存中的地址:

    var x = "getName";a[x] //即使不借助中間變量,也可以拿到該字符串在內(nèi)存中的地址 a["getName"] a.getName

    這樣在不同模塊下,修改該屬性就變得很簡單。

    而使用Symbol類型數(shù)據(jù)作為鍵,該鍵的內(nèi)存地址只會被當(dāng)前作用域的變量s引用,在其他作用域由于無法引用到這里的變量s,也就無法訪問對象的這個屬性。

    除了上面最常見的用法,Symbol還可以用于消除“魔術(shù)字符串”。所謂“魔術(shù)字符串”,就是與代碼緊密耦合在一起的某個具體的字符串(或者數(shù)字,因為往往難以解釋它為什么出現(xiàn),以及代表什么含義,所以被稱為魔術(shù)字符串),如:

    ... switch (shape) {case 'Triangle': // 魔術(shù)字符串area = .5 * options.width * options.height;break;/* ... more code ... */}

    這樣的“魔術(shù)字符串”會造成代碼難以維護(hù)。常見的解決辦法是使用一個變量來代替,如:

    const shapeType = {triangle: 'Triangle' };switch (shape) {case shapeType.triangle: //消除了一個魔術(shù)字符串area = .5 * options.width * options.height;break;/* ... more code ... */}

    實際上,變量triangle的值等于什么并不重要,我們的真正目的是比較switch內(nèi)的變量shape與shapeType.triangle是否相等,而不在乎它們的值是否都是“Triangle”。因此上述對象shapeType可以用下面的對象代替:

    const shapeType = {triangle: Symbol() };

    只要shapeType的所有屬性值都不相等,就不需要對代碼做其他修改。

    三、Symbol的語法規(guī)范

    1. 基本語法

    上面介紹到,使用如下語法即可創(chuàng)建一個Symbol變量:

    var s = Symbol();

    由于Symbol不是繼承自O(shè)bject,因此不可以使用new關(guān)鍵字來生成Symbol變量。使用上述語句創(chuàng)建的變量s,在控制臺中進(jìn)行輸出時會顯示為Symbol()。假如有另一個變量:

    var b = Symbol();console.log(s); //Symbol() console.log(b); //Symbol()

    變量s和變量b并不是同一個值,但它們在控制臺的輸出卻是一樣的,這樣不利于我們區(qū)分兩個變量。為此,我們可以在調(diào)用Symbol的時候傳入一個字符串作為對當(dāng)前Symbol變量的描述:

    var s = Symbol("symbol1"); var b = Symbol("symbol2");console.log(s); //Symbol("symbol1") console.log(b); //Symbol("symbol2")

    現(xiàn)在我們可以在控制臺中區(qū)分開變量s和變量b了。

    需要注意的是,使用相同描述符的兩個Symbol并不相等:

    var s = Symbol("s"); var b = Symbol("s");s !== b;

    打個比方,即使兩個碗都被叫做碗,它們?nèi)匀徊皇峭粋€碗。同理,描述符也僅僅是對Symbol變量的一個描述而已。

    如果你希望得到一個Symbol的描述符,可以借助Symbol原型上的description屬性(Symbol.prototype.description):

    const s = Symbol("symbol");console.log(s.description); //symbol

    Symbol還可以顯式的轉(zhuǎn)化為字符串或布爾值,但是不能轉(zhuǎn)化為數(shù)值:

    let sym = Symbol('My symbol'); String(sym) // 'Symbol(My symbol)' sym.toString() // 'Symbol(My symbol)'let sym2 = Symbol(); Boolean(sym2) // true

    2. Symbol屬性的遍歷

    以Symbol類型的變量作為對象屬性時,該屬性不會出現(xiàn)在for … in、for … of循環(huán)中,也不會被Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()返回。

    但該屬性并不是私有屬性,它可以被專門的Object.getOwnPropertySymbols()方法遍歷出來。該方法返回一個數(shù)組,包含了當(dāng)前對象的所有用作屬性名的Symbol值:

    var s1 = Symbol('a'); var s2 = Symbol('b');var a = {name: "夕山雨",[s1]: 24,[s2]: function(){} }var s = Object.getOwnPropertySymbols(a); //[Symbol(a), Symbol(b)] a[s[0]] = 24; //返回的數(shù)組元素不是字符串,而是實際的Symbol值,//因此可以通過它引用到對象的該屬性

    因此遍歷該方法的返回值即可遍歷所有的Symbol屬性。

    另外,ES6新增的Reflect.ownKeys()方法可以遍歷出所有的常規(guī)鍵名和Symbol鍵名。語法為:

    Reflect.ownKeys(a); //["name", Symbol(a), Symbol(b)]

    3. Symbol.for(),Symbol.keyFor()

    Symbol提供了一種可以創(chuàng)建相同Symbol的機(jī)制,就是使用Symbol.for()方法進(jìn)行注冊。通過該方法生成的Symbol會根據(jù)描述符進(jìn)行全局注冊,之后再次通過Symbol.for()傳入相同的描述符時,就可以得到相同的Symbol值。如:

    var s1 = Symbol.for('symbol'); //向全局注冊了以"symbol"為描述符的Symbol //由于描述符"symbol"已被注冊到全局,因此這里創(chuàng)建的Symbol與上面是同一個 var s2 = Symbol.for('symbol'); s1 === s2;

    這里指的全局不單指該變量所在的作用域,它在各個iframe甚至service worker中都是有效的,因此這是一種允許不同作用域創(chuàng)建相同Symbol的機(jī)制。

    如果你想得到一個全局注冊的Symbol的描述符,可以使用Symbol.keyFor()方法:

    Symbol.keyFor(s1); //"symbol"

    它輸出了變量s1的全局注冊標(biāo)識符“symbol”。

    四、內(nèi)置的Symbol值

    上面講到的Symbol的用法都是自定義的Symbol,在ES6中還定義了11個內(nèi)置的Symbol,用于指向語言內(nèi)部使用的方法。

    1. Symbol.hasInstance

    當(dāng)使用instanceof運算符判斷某個對象是否為某個構(gòu)造函數(shù)的實例時,就是在調(diào)用該構(gòu)造函數(shù)上的靜態(tài)方法[Symbol.hasInstance],它是js引擎預(yù)先定義好的。如:

    [] instanceof Array; //true//瀏覽器實際上是在調(diào)用下面的方法 Array[Symbol.hasInstance]([]);

    如果你想要看一下瀏覽器是如何實現(xiàn)該方法的,非常抱歉,你只會得到這樣的輸出:

    < Array[Symbol.hasInstance];> ? [Symbol.hasInstance]() { [native code] }

    native code表示當(dāng)前函數(shù)是使用本地代碼實現(xiàn)的,通常是C++或C,因此瀏覽器不會直接輸出它的源代碼。

    實際上,instanceof右側(cè)不要求一定是構(gòu)造函數(shù),也可以是一個普通的對象,只要該對象實現(xiàn)了[Symbol.hasInstance]方法即可。如:

    //1. 使用構(gòu)造函數(shù) function F(){this[Symbol.hasInstance] = function(obj){return obj.constructor === F;} } var f = new F(); f instanceof new F(); //true//2. 使用class class MyClass {[Symbol.hasInstance](foo) {return foo instanceof Array;} }[1, 2, 3] instanceof new MyClass() // true//3. 直接使用一個實現(xiàn)了Symbol.hasInstance的對象 var a = {[Symbol.hasInstance](foo) {return foo instanceof Array;} }[1, 2, 3] instanceof a // true

    總的來說,instanceof的行為就是,遇到a instanceof b這樣的語句,就調(diào)用b[Symbol.hasInstance](a),該函數(shù)的返回值就是該語句的返回值。這里如果b是構(gòu)造函數(shù),就調(diào)用它的靜態(tài)方法,如果是對象,就調(diào)用它的實例方法或原型方法。

    不過,如果instanceof右側(cè)不包含[Symbol.hasInstance]方法,那么瀏覽器會拋出這樣的錯誤:Right-hand side of ‘instanceof’ is not callable,表示右側(cè)不可被instanceof運算符調(diào)用。

    2. Symbol.isConcatSpreadable

    該屬性決定了當(dāng)前對象作為concat的參數(shù)時是否可以展開。通常:

    var obj = {age: 24}; [1].concat(obj); //[1, {age: 24}]

    obj被傳入concat后會直接作為一個元素添加到數(shù)組中。通過將obj的Symbol.isConcatSpreadable屬性設(shè)置為true,obj會在執(zhí)行concat時嘗試展開,如果該對象無法展開,obj不會被拼接到數(shù)組中去。所謂的可展開,指的是obj是否為數(shù)組或類數(shù)組結(jié)構(gòu)。如果obj是數(shù)組,顯然是可展開的,如果它有l(wèi)ength屬性,并且有"0","1"這樣的屬性鍵,那么它就是類數(shù)組,也是可以展開的:

    //設(shè)置了該對象需要展開,但它無法展開,因此最終結(jié)果為[] var obj = {age: 24, [Symbol.isConcatSpreadable]: true}; [].concat(obj); //[]//這是一個類數(shù)組對象,它是可展開的 var obj = {length: 2, "0": 24, "1": 25, name: "夕山雨",[Symbol.isConcatSpreadable]: true }; //name屬性被丟棄了,因為它無法被obj[index]的方式引用到 [].concat(obj); //[24, 25]

    3. Symbol.species

    該屬性用于在繼承的時候指定一個類的類別。如:

    class T1 extends Promise { }class T2 extends Promise {static get [Symbol.species]() {return Promise;} }new T1() instanceof T1 // true new T2() instanceof T2 // false

    對于T1,由它構(gòu)造出的實例默認(rèn)都是T1的實例。而在T2中我們?yōu)樵擃惗x了[Symbol.species]方法,它始終返回Promise,因此由T2構(gòu)造出的實例都不再被認(rèn)為是T2的實例,而是Promise的實例。

    該方法允許我們在定義衍生對象時,人為指定由它構(gòu)造出的實例的構(gòu)造函數(shù)。

    4. Symbol.match/replace/search/split

    這四個方法允許我們以對象的方式自定義String的match、replace、search、split方法。以match為例,我們通常這樣調(diào)用它:

    var s = "hello"; s.match(RegExp); //匹配一個正則表達(dá)式

    假如我們需要為當(dāng)前的字符串s定制一個自己的match方法,但是又不希望修改String原型上的match方法(因為這樣會影響到其他的字符串調(diào)用match方法)。Symbol.match就為我們提供了這種能力。

    對于上面的例子,如果傳入的對象具有[Symbol.match]方法,那么js引擎就會修改match方法默認(rèn)的行為,去調(diào)用定義的[Symbol.match]方法。如:

    var a = {[Symbol.match](){return true;} }"hello".match(a); //true

    當(dāng)調(diào)用字符串的match方法并傳入具有[Symbol.match]屬性的對象時,js引擎就會調(diào)用對象的這個方法。

    上面的寫法等同于下面的寫法:

    a[Symbol.match]("hello"); //true

    replace、search和split也是相同的原理。下面分別給一個簡單的例子:
    replace:

    const x = {}; x[Symbol.replace] = (...s) => console.log(s);'Hello'.replace(x, 'World') // ["Hello", "World"]

    由于replace的第一個參數(shù)有[Symbol.replace]方法,因此js引擎會調(diào)用這個方法,并把調(diào)用者‘Hello’和第二個參數(shù)‘World’作為參數(shù)傳遞給該方法。這樣,上面的寫法就等同于:

    x[Symbol.replace]("Hello", "world");

    search:

    var a = {[Symbol.match](){return true;} }"hello".search(a); //true

    原理同match。
    split:

    var a = {sep: ",",[Symbol.match](t){return t.split(this.sep);} }"hello,world".split(a); //["hello", "world"]

    原理也與match相同。

    5. Symbol.iterator

    定義一個對象的遍歷器方法。凡是具有[Symbol.iterator]方法的對象都是可遍歷的,可以使用for … of循環(huán)依次輸出對象的每個屬性。數(shù)組和類數(shù)組,以及ES6新增的Map、Set等都原生部署了該方法,因此它們都可遍歷。如:

    for(var item of [1,2,3]){console.log(item); //依次輸出1,2,3 }

    任何一個數(shù)組都具備這個原生的遍歷器方法:

    > [][Symbol.iterator]< ? values() { [native code] } //C++實現(xiàn)

    普通對象默認(rèn)不具有該遍歷器方法,因此無法用for … of循環(huán)遍歷出對象所有的屬性值。如果你希望讓普通對象可遍歷,可以手動為該對象定義遍歷器方法,如:

    var a = {name: "夕山雨",age: 24,[Symbol.iterator]: function* (){yield this.name;yield this.age;} }

    這里為了簡單,使用了ES6的Generator函數(shù),它定義該遍歷器先輸出name屬性,再輸出age屬性。因此當(dāng)你用for … of來輸出a的屬性值時,就可以得到結(jié)果:

    for(var item of a){console.log(item); //依次輸出:"夕山雨" 24 }

    iterator是ES6非常重要的概念,我們后續(xù)會有專門的文章來介紹它。

    6. Symbol.toPrimitive

    該方法定義了一個對象如何被轉(zhuǎn)化為一個基本數(shù)據(jù)類型。通常對象是不能直接與基本數(shù)據(jù)類型的變量進(jìn)行運算的,但是如果你為它定義了[Symbol.toPrimitive]方法,它就可以按照你所指定的規(guī)則轉(zhuǎn)化為基本數(shù)據(jù)類型。它接收一個字符串,表示需要轉(zhuǎn)換成的數(shù)據(jù)類型:

    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'

    這里表示,如果對象需要轉(zhuǎn)化為數(shù)字,就返回123;如果需要轉(zhuǎn)化為字符串,就轉(zhuǎn)化為’str’;如果沒有指定要轉(zhuǎn)化的類型,那就返回字符串’Default’。

    由于乘法運算*只能對數(shù)值操作,因此js引擎會調(diào)用[Symbol.toPrimitive]并傳入"number",將obj轉(zhuǎn)化為數(shù)字。而加法既可以對數(shù)值生效,也可以對字符串生效,因此js引擎?zhèn)魅肓?#34;default"。該方法默認(rèn)只接受number、string和default這三個值。

    7. Symbol.toStringTag

    可以自定義對象的toString()方法。通常對象的toString方法會返回一個類似[object Object]的字符串,表示該對象的類型,如:

    var a = {};a.toString(); //"[object Object]"

    但是如果你修改了對象的Symbol.toStringTag方法,返回值就會發(fā)生變化:

    a[Symbol.toStringTag] = function(){return "xxx"; } a.toString(); //"[object xxx]"

    可以看到,我們定義的返回值覆蓋了之前的字符串中的后半部分“Object”,因此該方法可以用于定制對象的toString()的返回值。

    8. Symbol.unscopables

    該方法用于with語句。它指定在使用with語句時,哪些屬性不屬于with環(huán)境。舉個例子:

    var author = {name: "夕山雨",age: 24,stature: "179",weight: 65 }var name = "張三"; var age = "28";with(author){console.log(name); //“夕山雨”console.log(age); //24console.log(stature); //"179"console.log(weight); //65 }

    默認(rèn)情況下,對于with語句內(nèi)引用的變量,js引擎會優(yōu)先去with的作用對象上查找對應(yīng)的屬性,如果找不到,才認(rèn)為是外部變量。但是你可以人為指定哪些屬性不應(yīng)該去作用對象上查找,如:

    var author = {name: "夕山雨",age: 24,stature: "179",weight: 65,get [Symbol.unscopables](){return { name: true, age: true }} }var name = "張三"; var age = "28"; var stature = "153"; var weight = 80;with(author){console.log(name); //“張三”console.log(age); //28console.log(stature); //"179"console.log(weight); //65 }

    可以看到,由于我們認(rèn)為指定了name和age兩個屬性不作用域with環(huán)境,因此這里的name和age輸出的是外部的變量,而stature和weight輸出的仍然是author的屬性值。

    總結(jié)

    Symbol作為一種新的數(shù)據(jù)類型,有著與String相似的特性,與String不同的是它是獨一無二的,因此適合作為對象屬性的鍵值,防止該屬性被覆蓋。除了自定義的Symbol值外,靈活掌握內(nèi)置的Symbol,對ES6的學(xué)習(xí)有帶來極大幫助,特別是Symbol.iterator,它是ES6中的一個非常重要的概念,之后會繼續(xù)探討。

    總結(jié)

    以上是生活随笔為你收集整理的ES6之Symbol详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。