2021-03-09 【阿里面試】有關symbol的一些知識(1)-基礎知識

相關文章:【阿里面試】有關symbol的一些知識(1)-基礎知識
【阿里面試】有關symbol的一些知識(2)-實例
【阿里面試】有關symbol的一些知識(3)-內置方法

作用

ES5 的對象屬性名都是字符串定欧,這容易造成屬性名的沖突守谓。比如拓哟,你使用了一個他人提供的對象鬓催,但又想為這個對象添加新的方法(mixin 模式),新方法的名字就有可能與現(xiàn)有方法產生沖突。如果有一種機制赚瘦,保證每個屬性的名字都是獨一無二的就好了祟牲,這樣就從根本上防止屬性名的沖突。這就是 ES6 引入Symbol的原因榛搔。

簡介

ES6 引入了一種新的原始數(shù)據類型Symbol诺凡,表示獨一無二的值。它是 JavaScript 語言的第七種數(shù)據類型践惑,前六種是:undefined腹泌、null、布爾值(Boolean)尔觉、字符串(String)凉袱、數(shù)值(Number)、對象(Object)侦铜。

let s = Symbol();

typeof s;
// "symbol"

注意专甩,Symbol函數(shù)前不能使用new命令,否則會報錯钉稍。這是因為生成的 Symbol 是一個原始類型的值涤躲,不是對象。

也就是說贡未,由于 Symbol 值不是對象种樱,所以不能添加屬性蒙袍。基本上嫩挤,它是一種類似于字符串的數(shù)據類型害幅。

Symbol 函數(shù)可以接受一個字符串作為參數(shù),表示對 Symbol 實例的描述岂昭,主要是為了在控制臺顯示矫限,或者轉為字符串時,比較容易區(qū)分佩抹。

let s1 = Symbol("foo");
let s2 = Symbol("bar");

s1; // Symbol(foo)
s2; // Symbol(bar)

s1.toString(); // "Symbol(foo)"
s2.toString(); // "Symbol(bar)"

如果 Symbol 的參數(shù)是一個對象叼风,就會調用該對象的 toString 方法,將其轉為字符串棍苹,然后才生成一個 Symbol 值无宿。

const obj = {
  toString() {
    return "abc";
  },
};
const sym = Symbol(obj);
sym; // Symbol(abc)

注意,Symbol 函數(shù)的參數(shù)只是表示對當前 Symbol 值的描述枢里,因此相同參數(shù)的 Symbol 函數(shù)的返回值是不相等的孽鸡。

// 沒有參數(shù)的情況
let s1 = Symbol();
let s2 = Symbol();

s1 === s2; // false

// 有參數(shù)的情況
let s1 = Symbol("foo");
let s2 = Symbol("foo");

s1 === s2; // false

Symbol 值不能與其他類型的值進行運算,會報錯栏豺。

let sym = Symbol("My symbol");

"your symbol is " + sym;
// TypeError: can't convert symbol to string
`your symbol is ${sym}`;
// TypeError: can't convert symbol to string

但是彬碱,Symbol 值可以顯式轉為字符串。

let sym = Symbol("My symbol");

String(sym); // 'Symbol(My symbol)'
sym.toString(); // 'Symbol(My symbol)'

另外奥洼,Symbol 值也可以轉為布爾值巷疼,但是不能轉為數(shù)值。

let sym = Symbol();
Boolean(sym); // true
!sym; // false

if (sym) {
  // ...
}

Number(sym); // TypeError
sym + 2; // TypeError

Symbol.prototype.description

ES2019 提供了一個實例屬性 description灵奖,直接返回 Symbol 的描述嚼沿。

const sym = Symbol("foo");

sym.description; // "foo"

作為屬性名的 Symbol

由于每一個 Symbol 值都是不相等的,這意味著 Symbol 值可以作為標識符瓷患,用于對象的屬性名骡尽,就能保證不會出現(xiàn)同名的屬性。這對于一個對象由多個模塊構成的情況非常有用擅编,能防止某一個鍵被不小心改寫或覆蓋攀细。

let mySymbol = Symbol();

// 第一種寫法
let a = {};
a[mySymbol] = "Hello!";

// 第二種寫法
let a = {
  [mySymbol]: "Hello!",
};

// 第三種寫法
let a = {};
Object.defineProperty(a, mySymbol, { value: "Hello!" });

// 以上寫法都得到同樣結果
a[mySymbol]; // "Hello!"

注意,Symbol 值作為對象屬性名時爱态,不能用點運算符

const mySymbol = Symbol();
const a = {};

a.mySymbol = "Hello!";
a[mySymbol]; // undefined
a["mySymbol"]; // "Hello!"

上面代碼中谭贪,因為點運算符后面總是字符串,所以不會讀取 mySymbol 作為標識名所指代的那個值肢藐,導致 a 的屬性名實際上是一個字符串故河,而不是一個 Symbol 值。

在對象的內部吆豹,使用 Symbol 值定義屬性時鱼的,Symbol 值必須放在方括號之中。

let s = Symbol();

let obj = {
  [s]: function (arg) { ... }
};

obj[s](123);

上面代碼中痘煤,如果 s 不放在方括號中凑阶,該屬性的鍵名就是字符串 s,而不是 s 所代表的那個 Symbol 值衷快。

采用增強的對象寫法宙橱,上面代碼的 obj 對象可以寫得更簡潔一些。

let obj = {
  [s](arg) { ... }
};

Symbol 類型還可以用于定義一組常量蘸拔,保證這組常量的值都是不相等的师郑。

const log = {};

log.levels = {
  DEBUG: Symbol("debug"),
  INFO: Symbol("info"),
  WARN: Symbol("warn"),
};
console.log(log.levels.DEBUG, "debug message"); // Symbol(debug) "debug message"
console.log(log.levels.INFO, "info message"); // Symbol(info) "info message"
const COLOR_RED = Symbol();
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");
  }
}

常量使用 Symbol 值最大的好處,就是其他任何值都不可能有相同的值了调窍,因此可以保證上面的 switch 語句會按設計的方式工作宝冕。

還有一點需要注意,Symbol 值作為屬性名時邓萨,該屬性還是公開屬性地梨,不是私有屬性。

屬性名的遍歷

Symbol 作為屬性名缔恳,遍歷對象的時候宝剖,該屬性不會出現(xiàn)在 for...infor...of 循環(huán)中歉甚,也不會被 Object.keys()万细、Object.getOwnPropertyNames()JSON.stringify()返回纸泄。

但是雅镊,它也不是私有屬性,有一個 Object.getOwnPropertySymbols()方法刃滓,可以獲取指定對象的所有 Symbol 屬性名仁烹。該方法返回一個數(shù)組,成員是當前對象的所有用作屬性名的 Symbol 值咧虎。

const obj = {};
let a = Symbol("a");
let b = Symbol("b");

obj[a] = "Hello";
obj[b] = "World";

const objectSymbols = Object.getOwnPropertySymbols(obj);

objectSymbols;
// [Symbol(a), Symbol(b)]
const obj = {};
const foo = Symbol("foo");

obj[foo] = "bar";

for (let i in obj) {
  console.log(i); // 無輸出
}

Object.getOwnPropertyNames(obj); // []
Object.getOwnPropertySymbols(obj); // [Symbol(foo)]

另一個新的 API卓缰,Reflect.ownKeys()方法可以返回所有類型的鍵名,包括常規(guī)鍵名和 Symbol 鍵名砰诵。

let obj = {
  [Symbol("my_key")]: 1,
  enum: 2,
  nonEnum: 3,
};

Reflect.ownKeys(obj);
//  ["enum", "nonEnum", Symbol(my_key)]

由于以 Symbol 值作為鍵名征唬,不會被常規(guī)方法遍歷得到。我們可以利用這個特性茁彭,為對象定義一些非私有的总寒、但又希望只用于內部的方法。

let size = Symbol("size");

class Collection {
  constructor() {
    this[size] = 0;
  }

  add(item) {
    this[this[size]] = item;
    this[size]++;
  }

  static sizeOf(instance) {
    return instance[size];
  }
}

let x = new Collection();
Collection.sizeOf(x); // 0

x.add("foo");
Collection.sizeOf(x); // 1

Object.keys(x); // ['0']
Object.getOwnPropertyNames(x); // ['0']
Object.getOwnPropertySymbols(x); // [Symbol(size)]

對象 x 的 size 屬性是一個 Symbol 值理肺,所以 Object.keys(x)摄闸、Object.getOwnPropertyNames(x)都無法獲取它善镰。這就造成了一種非私有的內部方法的效果。

Symbol.for()年枕,Symbol.keyFor()

我們希望重新使用同一個 Symbol 值炫欺,Symbol.for()方法可以做到這一點。它接受一個字符串作為參數(shù)熏兄,然后搜索有沒有以該參數(shù)作為名稱的 Symbol 值品洛。如果有,就返回這個 Symbol 值摩桶,否則就新建一個以該字符串為名稱的 Symbol 值桥状,并將其注冊到全局。

let s1 = Symbol.for("foo");
let s2 = Symbol.for("foo");

s1 === s2; // true

Symbol.for()Symbol()這兩種寫法硝清,都會生成新的 Symbol辅斟。

它們的區(qū)別是,前者會被登記在全局環(huán)境中供搜索耍缴,后者不會砾肺。Symbol.for()不會每次調用就返回一個新的 Symbol 類型的值,而是會先檢查給定的 key 是否已經存在防嗡,如果不存在才會新建一個值变汪。

比如,如果你調用 Symbol.for("cat")30 次蚁趁,每次都會返回同一個 Symbol 值裙盾,但是調用 Symbol("cat")30 次,會返回 30 個不同的 Symbol 值他嫡。

Symbol.keyFor()方法返回一個已登記Symbol 類型值的 key番官。

let s1 = Symbol.for("foo");
Symbol.keyFor(s1); // "foo"

let s2 = Symbol("foo");
Symbol.keyFor(s2); // undefined

注意,Symbol.for()Symbol 值登記的名字钢属,是全局環(huán)境的徘熔,不管有沒有在全局環(huán)境運行

function foo() {
  return Symbol.for("bar");
}

const x = foo();
const y = Symbol.for("bar");
console.log(x === y); // true

Symbol.for()的這個全局登記特性,可以用在不同的 iframe 或 service worker 中取到同一個值淆党。

iframe = document.createElement("iframe");
iframe.src = String(window.location);
document.body.appendChild(iframe);

// iframe 窗口生成的 Symbol 值酷师,可以在主頁面得到。
iframe.contentWindow.Symbol.for("foo") === Symbol.for("foo");
// true

原文:# ECMAScript 6 入門

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末染乌,一起剝皮案震驚了整個濱河市山孔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌荷憋,老刑警劉巖台颠,帶你破解...
    沈念sama閱讀 212,599評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異勒庄,居然都是意外死亡串前,警方通過查閱死者的電腦和手機瘫里,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來酪呻,“玉大人减宣,你說我怎么就攤上這事盐须⊥孳” “怎么了?”我有些...
    開封第一講書人閱讀 158,084評論 0 348
  • 文/不壞的土叔 我叫張陵贼邓,是天一觀的道長阶冈。 經常有香客問我,道長塑径,這世上最難降的妖魔是什么女坑? 我笑而不...
    開封第一講書人閱讀 56,708評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮统舀,結果婚禮上匆骗,老公的妹妹穿的比我還像新娘。我一直安慰自己誉简,他們只是感情好碉就,可當我...
    茶點故事閱讀 65,813評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著闷串,像睡著了一般瓮钥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上烹吵,一...
    開封第一講書人閱讀 50,021評論 1 291
  • 那天碉熄,我揣著相機與錄音,去河邊找鬼肋拔。 笑死锈津,一個胖子當著我的面吹牛,可吹牛的內容都是我干的凉蜂。 我是一名探鬼主播琼梆,決...
    沈念sama閱讀 39,120評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼跃惫!你這毒婦竟也來了叮叹?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,866評論 0 268
  • 序言:老撾萬榮一對情侶失蹤爆存,失蹤者是張志新(化名)和其女友劉穎蛉顽,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體先较,經...
    沈念sama閱讀 44,308評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡携冤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,633評論 2 327
  • 正文 我和宋清朗相戀三年悼粮,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片曾棕。...
    茶點故事閱讀 38,768評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡扣猫,死狀恐怖,靈堂內的尸體忽然破棺而出翘地,到底是詐尸還是另有隱情申尤,我是刑警寧澤,帶...
    沈念sama閱讀 34,461評論 4 333
  • 正文 年R本政府宣布衙耕,位于F島的核電站昧穿,受9級特大地震影響,放射性物質發(fā)生泄漏橙喘。R本人自食惡果不足惜时鸵,卻給世界環(huán)境...
    茶點故事閱讀 40,094評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望厅瞎。 院中可真熱鬧饰潜,春花似錦、人聲如沸和簸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,850評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽比搭。三九已至冠跷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間身诺,已是汗流浹背蜜托。 一陣腳步聲響...
    開封第一講書人閱讀 32,082評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留霉赡,地道東北人橄务。 一個月前我還...
    沈念sama閱讀 46,571評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像穴亏,于是被迫代替她去往敵國和親蜂挪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,666評論 2 350