1. 程式人生 > >面試官:JavaScript 原始資料型別 Symbol 有什麼用?

面試官:JavaScript 原始資料型別 Symbol 有什麼用?

以前提到 JavaScript 原始資料型別時,我們知道有NumberStringNullBooleanUndefined這幾種。ES6 引入了新的基本資料型別SymbolBigInt。今天我們就來了解下Symbol型別。Symbol型別是為了解決屬性名衝突的問題,順帶還具備模擬私有屬性的功能。

簡介

建立symbol變數最簡單的方法是用Symbol()函式。sysmbol變數有兩點比較特別:

  1. 它可以作為物件屬性名。只有字串和 symbol 型別才能用作物件屬性名。
  2. 沒有兩個symbol 的值是相等的。
const symbol1 = Symbol();
const symbol2 = Symbol();

symbol1 === symbol2; // false

const obj = {};
obj[symbol1] = 'Hello';
obj[symbol2] = 'World';

obj[symbol1]; // 'Hello'
obj[symbol2]; // 'World'

儘管呼叫Symbol() 讓它看起來像是物件,實際上symbol是 JavaScript 原始資料型別。把Symbol當作建構函式來用 new會報錯。

const symbol1 = Symbol();

typeof symbol1; // 'symbol'
symbol1 instanceof Object; // false

// Throws "TypeError: Symbol is not a constructor"
new Symbol();

描述資訊

Symbol()函式只有一個引數,字串description。這個字串引數的唯一作用是輔助除錯,也就是它的toString()

值。但是請注意,兩個具有相同descriptionsymbol也是不相等的。

const symbol1 = Symbol('my symbol');
const symbol2 = Symbol('my symbol');

symbol1 === symbol2; // false
console.log(symbol1); // 'Symbol(my symbol)'

有一個全域性的symbol註冊中心,用Symbol.for()建立的symbol會新增到這個註冊中心,並用它的 description作為索引鍵。也就是說,如果你用Symbol.for()建立帶有相同 description的兩個 symbol

,它們就是相等的。

const symbol1 = Symbol.for('test');
const symbol2 = Symbol.for('test');

symbol1 === symbol2; // true
console.log(symbol1); // 'Symbol(test)'

通常來說,除非你有非常好的理由,否則不應該使用全域性註冊中心,因為這會造成命名衝突。

命名衝突

JavaScript 內建了一個 symbol ,那就是 ES6 中的Symbol.iterator  。擁有Symbol.iterator函式的物件被稱為可迭代物件,就是說你可以在物件上使用for/of 迴圈。

const fibonacci = {
  [Symbol.iterator]: function*() {
    let a = 1;
    let b = 1;
    let temp;

    yield b;

    while (true) {
      temp = a;
      a = a + b;
      b = temp;
      yield b;
    }
  }
};

// Prints every Fibonacci number less than 100
for (const x of fibonacci) {
  if (x >= 100) {
    break;
  }
  console.log(x);
}

為什麼這裡要用Symbol.iterator 而不是字串?假設不用Symbol.iterator ,可迭代物件需要有一個字串屬性名'iterator',就像下面這個可迭代物件的類:

class MyClass {
  constructor(obj) {
    Object.assign(this, obj);
  }

  iterator() {
    const keys = Object.keys(this);
    let i = 0;
    return (function*() {
      if (i >= keys.length) {
        return;
      }
      yield keys[i++];
    })();
  }
}

MyClass 的例項是可迭代物件,可以遍歷物件上面的屬性。但是上面的類有個潛在的缺陷,假設有個惡意使用者給 MyClass 建構函式傳了一個帶有iterator屬性的物件:

const obj = new MyClass({ iterator: 'not a function' });

這樣你在obj上使用for/of的話,JavaScript 會丟擲TypeError: obj is not iterable異常。可以看出,傳入物件的 iterator函式覆蓋了類的 iterator屬性。這有點類似原型汙染的安全問題,無腦複製使用者資料會對一些特殊屬性,比如__proto__constructor帶來問題。

這裡的核心在於,symbol讓物件的內部資料和使用者資料井水不犯河水。由於sysmbol無法在 JSON 裡表示,因此不用擔心給 Express API 傳入帶有不合適的Symbol.iterator屬性的資料。另外,對於那種混合了內建函式和使用者資料的物件,比如 Mongoose model,你可以用symbol來確保使用者資料不會跟內建屬性衝突。

私有屬性

由於任何兩個symbol都是不相等的,在 JavaScript 裡可以很方便地用來模擬私有屬性。symbol不會出現在 Object.keys()的結果中,因此除非你明確地export 一個symbol,或者用 Object.getOwnPropertySymbols() 函式獲取,否則其他程式碼無法訪問這個屬性。

function getObj() {
  const symbol = Symbol('test');
  const obj = {};
  obj[symbol] = 'test';
  return obj;
}

const obj = getObj();

Object.keys(obj); // []

// 除非有這個 symbol 的引用,否則無法訪問該屬性
obj[Symbol('test')]; // undefined

// 用 getOwnPropertySymbols() 依然可以拿到 symbol 的引用
const [symbol] = Object.getOwnPropertySymbols(obj);
obj[symbol]; // 'test'

還有一個原因是symbol不會出現在JSON.stringify()的結果裡,確切地說是JSON.stringify()會忽略symbol屬性名和屬性值:

const symbol = Symbol('test');
const obj = { [symbol]: 'test', test: symbol };

JSON.stringify(obj); // "{}"

總結

Symbol 表示物件內部狀態,可以很好地隔離使用者資料和程式狀態。有了它,我們就不再需要某些命名約定了,比如內部屬性用'$'開頭。下次碰到需要定義私有屬性的時候,試試Symbol型別吧!


看到這個頗有氣質的 logo,不來關注下嗎?

相關推薦

面試JavaScript 原始資料型別 Symbol 有什麼用?

以前提到 JavaScript 原始資料型別時,我們知道有Number,String,Null,Boolean,Undefined這幾種。ES6 引入了新的基本資料型別Symbol和BigInt。今天我們就來了解下Symbol型別。Symbol型別是為了解決屬性名衝突的問題,順帶還具備模擬私有屬性的功能。 簡

面試指南」解讀JavaScript原始資料型別

#### **JavaScript 有 7 種原始資料型別:** - **String(字元型)** - **Number(數值型)** - **Boolean(布林值型)** - **Undefined** - **Null** - **Object(物件型)** - **Symbol(符號型,ES6 中

ES6的原始資料型別Symbol

概述 保證每個屬性的名字都是獨一無二就從根本上防止屬性名的衝突。 ES6 引入了一種新的原始資料型別Symbol,表示獨一無二的值。它是 JavaScript 語言的第七種資料型別,前六種是:undefined、null、布林值(Boolean)、字串(String)、數值(N

面試問我redis資料型別,我回答了8種

> **面試官**:小明呀,redis 有幾種資料結構呀? > > **小明**:8 種 > > **面試官**:那你說一下分別是什麼? > > **小明**:raw,int,ht,zipmap,linkedlist,ziplist,intset,skiplist,embstr > > **面試官**:額,你

JavaScript學習(二)原始資料型別-字串、數字、布林值、null、undefined

資料型別 --- 能夠表示並操作值的型別,程式語言的最基本特性就是能夠支援多種資料型別。 JavaScript的資料型別分為兩種: 原始型別 (primitive type)  物件型別(object type) 原始資料型別包括:  數字   字串  布林值   ,

javaScript 學習零散筆記(2) ---- 新的資料型別Symbol

此文是在學習《ECMAScript 6 入門》-- 阮一峰 過程中的記錄,在此非常感謝他的分享。《ECMAScript 6 入門》讓我獲益匪淺1: Symbol 不是物件,不能new Symbol();2: Symbol() 每次返回的值是不同的{ let x1 = Sym

javascript第七種資料型別Symbol

Symbol 是什麼? Symbols 不是圖示,也不是指在程式碼中可以使用小圖片: 也不是指代其他一些東西的語法。那麼,Symbol 到究竟是什麼呢? 七種資料型別 JavaScript 在 1997 年被標準化時,就有 6 種資料型

面試你看過Redis資料結構底層實現嗎?

面試中,redis也是很受面試官親睞的一部分。我向在這裡講的是redis的底層資料結構,而不是你理解的五大資料結構。你有沒有想過redis底層是怎樣的資料結構呢,他們和我們java中的HashMap、List、等使用的資料結構有什麼區別呢。 1. 字串處理(string) 我們都知道redis是用C語言寫

阿里面試HashMap資料結構之道

    問題1:HashMap的資料結構是什麼樣的? 同學1:嗯...陣列+連結串列 同學2:陣列

面試兄弟,說說基本型別和包裝型別的區別吧

六年前,我從蘇州回到洛陽,抱著一幅“海歸”的心態,投了不少簡歷,也“約談”了不少面試官,但僅有兩三個令我感到滿意。其中有一位叫老馬,至今還活在我的手機通訊錄裡。他當時扔了一個面試題把我砸懵了:說說基本型別和包裝型別的區別吧。 我當時二十三歲,正值青春年華,從事

面試"準備用HashMap存1w條資料,構造時傳10000還會觸發擴容嗎?"

// 預計存入 1w 條資料,初始化賦值 10000,避免 resize。 HashMap<String,String> map = new HashMap<>(10000) // for (int i = 0; i < 10000; i++) Java 集合的擴容

面試怎麼設計大檔案、大資料場景下的傳輸加密方案?

某年某月某一天,冷冽寒風中,姚小毛走進了某家公司,開始了新一輪的面試。 一陣寒暄後。 面試官:“你好,看你的專案經驗中有做過資料加密的工作,你是使用什麼加密演算法加解密的?” 姚小毛:“嗯,我是採用的 非對稱加密 + 對稱加密 的混合加密演算法。” 面試官:“為什麼要用混合加密的方式?” 姚小毛:“非對稱

面試系列-面試你能給我解釋一下javascript中的this嗎?

一.前言   關於javascript中的this物件,可能已經被大家說爛了。   即使是這樣,我依然決定將這篇文章給水出來。畢竟全國在新型肺炎的影響下,公司沒法正常復工。   除了刷刷手機,還是要適當的學習一下。   廢柴是真不好當,勞逸結合才是王道。 二.正戲開始   面試官:你能給我解釋一下javasc

面試能解釋一下javascript中bind、apply和call這三個函式的用法嗎

一.前言     不知道大家還記不記得前一篇文章:《面試官:能解釋一下javascript中的this嗎》   那今天這篇文章雖然是介紹javascript中bind、apply和call函式,但是多少也和this有點關聯。   假如在前面那場面試末尾,面試官不依不饒繼續問你javascr

JavaScript語言入門教程》記錄整理入門和資料型別

[toc] 本系列基於阮一峰老師的[《JavaScrip語言入門教程》](https://wangdoc.com/javascript/index.html)或《JavaScript教程》記錄整理,教程採用[知識共享 署名-相同方式共享 3.0協議](https://creativecommons.org/

面試分散式事務講下 程式設計師不清楚 然後結果就涼涼了

java、後端開發、程式設計師、分散式事務 分散式事務應該是面試官最喜歡問的題目之一 我對分散式事務的基本思路整理總結了一下,其實還有很多細節沒研究。 基礎知識準備 資料庫事務、分散式、微服務、分庫分表 資料庫事務的特性:原子性(Atomicity )、一致性( Cons

Javascript資料型別&深淺拷貝二

資料型別: Javascript中有5種基本資料型別(簡單資料型別),分別為:Undefined, Null, Boolean, Number和String; 同時還含有一種複雜資料型別,即物件(雖然js中一切皆為物件) 其中Undefined和Null的區別為: U

JavaScript引用資料型別

JavaScript引用資料型別 引用型別 在ECMAScript中,引用型別是一種資料結構,用於將資料和功能組織在一起(它也常被稱為類)。 Object型別 建立Object例項的方式有兩種。第一種是使用new操作符後跟Object建構函式,例如; var person = new Obje

JavaScript資料型別如何判斷

JavaScript的資料型別如何判斷 使用 Javascript 的軟體專案 JavaScript資料型別一共有7種: Undefined Null Boolean String Symbol Number Object

03-JavaScript基礎—資料型別

文章配套視訊 https://study.163.com/course/introduction/1005973001.htm 資料 資料是指所有能輸入到計算機並被計算機程式處理的符號的介質的總稱,是具有一定意義的數字、字母、符號和模擬量等的通稱。 資料型