成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

JavaScript 為什么要有 Symbol 類型

lemanli / 3114人閱讀

摘要:也有類似的概念,它是根據(jù)傳遞的數(shù)據(jù)類型推斷的。這個(gè)特性的名稱被稱為私有字段,雖然這不會(huì)使所有對(duì)象受益,但會(huì)使類實(shí)例的對(duì)象受益。警告中有一個(gè)功能會(huì)破壞代理的隱私。

Symbols 是 ES6 引入了一個(gè)新的數(shù)據(jù)類型 ,它為 JS 帶來了一些好處,尤其是對(duì)象屬性時(shí)。 但是,它們能為我們做些字符串不能做的事情呢?

想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著你!

在深入探討 Symbol 之前,讓我們先看看一些 JavaScript 特性,許多開發(fā)人員可能不知道這些特性。

背景

js中的數(shù)據(jù)類型總體來說分為兩種,他們分別是:值類型 和 引用類型

值類型(基本類型):數(shù)值型(Number),字符類型(String),布爾值型(Boolean),null 和 underfined

引用類型(類):函數(shù),對(duì)象,數(shù)組等

值類型理解:變量之間的互相賦值,是指開辟一塊新的內(nèi)存空間,將變量值賦給新變量保存到新開辟的內(nèi)存里面;之后兩個(gè)變量的值變動(dòng)互不影響,例如:

var a=10; //開辟一塊內(nèi)存空間保存變量a的值“10”;
var b=a; //給變量 b 開辟一塊新的內(nèi)存空間,將 a 的值 “10” 賦值一份保存到新的內(nèi)存里;
//a 和 b 的值以后無論如何變化,都不會(huì)影響到對(duì)方的值;

一些語言,比如 C,有引用傳遞和值傳遞的概念。JavaScript 也有類似的概念,它是根據(jù)傳遞的數(shù)據(jù)類型推斷的。如果將值傳遞給函數(shù),則重新分配該值不會(huì)修改調(diào)用位置中的值。但是,如果你修改的是引用類型,那么修改后的值也將在調(diào)用它的地方被修改。

引用類型理解:變量之間的互相賦值,只是指針的交換,而并非將對(duì)象(普通對(duì)象,函數(shù)對(duì)象,數(shù)組對(duì)象)復(fù)制一份給新的變量,對(duì)象依然還是只有一個(gè),只是多了一個(gè)指引~~;例如:

var a={x:1,y:2} //需要開辟內(nèi)存空間保存對(duì)象,變量 a 的值是一個(gè)地址,這個(gè)地址指向保存對(duì)象的空間;
var b=a; // 將a 的指引地址賦值給 b,而并非復(fù)制一給對(duì)象且新開一塊內(nèi)存空間來保存;
// 這個(gè)時(shí)候通過 a 來修改對(duì)象的屬性,則通過 b 來查看屬性時(shí)對(duì)象屬性已經(jīng)發(fā)生改變;

值類型(神秘的 NaN 值除外)將始終與具有相同值的另一個(gè)值類型的完全相等,如下:

const first = "abc" + "def";
const second = "ab" + "cd" + "ef";
console.log(first === second); // true

但是完全相同結(jié)構(gòu)的引用類型是不相等的:

const obj1 = { name: "Intrinsic" };
const obj2 = { name: "Intrinsic" };
console.log(obj1 === obj2); // false
// 但是,它們的 .name 屬性是基本類型:
console.log(obj1.name === obj2.name); // true

對(duì)象在 JavaScript 語言中扮演重要角色,它們的使用無處不在。對(duì)象通常用作鍵/值對(duì)的集合,然而,以這種方式使用它們有一個(gè)很大的限制: 在 symbol 出現(xiàn)之前,對(duì)象鍵只能是字符串,如果試圖使用非字符串值作為對(duì)象的鍵,那么該值將被強(qiáng)制轉(zhuǎn)換為字符串,如下:

const obj = {};
obj.foo = "foo";
obj["bar"] = "bar";
obj[2] = 2;
obj[{}] = "someobj";
console.log(obj);
// { "2": 2, foo: "foo", bar: "bar",
     "[object Object]": "someobj" }

Symbol 是什么

Symbol() 函數(shù)會(huì)返回 symbol 類型的值,該類型具有靜態(tài)屬性和靜態(tài)方法。它的靜態(tài)屬性會(huì)暴露幾個(gè)內(nèi)建的成員對(duì)象;它的靜態(tài)方法會(huì)暴露全局的 symbol 注冊(cè),且類似于內(nèi)建對(duì)象類,但作為構(gòu)造函數(shù)來說它并不完整,因?yàn)樗恢С终Z法:"new Symbol()"。所以使用 Symbol 生成的值是不相等:

const s1 = Symbol();
const s2 = Symbol();
console.log(s1 === s2); // false

實(shí)例化 symbol 時(shí),有一個(gè)可選的第一個(gè)參數(shù),你可以選擇為其提供字符串。 此值旨在用于調(diào)試代碼,否則它不會(huì)真正影響symbol 本身。

const s1 = Symbol("debug");
const str = "debug";
const s2 = Symbol("xxyy");
console.log(s1 === str); // false
console.log(s1 === s2); // false
console.log(s1); // Symbol(debug)
symbol 作為對(duì)象屬性

symbol 還有另一個(gè)重要的用途,它們可以用作對(duì)象中的鍵,如下:

const obj = {};
const sym = Symbol();
obj[sym] = "foo";
obj.bar = "bar";
console.log(obj); // { bar: "bar" }
console.log(sym in obj); // true
console.log(obj[sym]); // foo
console.log(Object.keys(obj)); // ["bar"]

乍一看,這看起來就像可以使用 symbol 在對(duì)象上創(chuàng)建私有屬性,許多其他編程語言在其類中有自己的私有屬性,私有屬性遺漏一直被視為 JavaScript 的缺點(diǎn)。

不幸的是,與該對(duì)象交互的代碼仍然可以訪問其鍵為 symbol 的屬性。 在調(diào)用代碼尚不能訪問 symbol 本身的情況下,這甚至是可能的。 例如,Reflect.ownKeys() 方法能夠獲取對(duì)象上所有鍵的列表,包括字符串和 symbol :

function tryToAddPrivate(o) {
  o[Symbol("Pseudo Private")] = 42;
}
const obj = { prop: "hello" };
tryToAddPrivate(obj);
console.log(Reflect.ownKeys(obj));
// [ "prop", Symbol(Pseudo Private) ]
console.log(obj[Reflect.ownKeys(obj)[1]]); // 42
注意:目前正在做一些工作來處理在JavaScript中向類添加私有屬性的問題。這個(gè)特性的名稱被稱為私有字段,雖然這不會(huì)使所有對(duì)象受益,但會(huì)使類實(shí)例的對(duì)象受益。私有字段從 Chrome 74開始可用。
防止屬性名稱沖突

符號(hào)可能不會(huì)直接受益于JavaScript為對(duì)象提供私有屬性。然而,他們是有益的另一個(gè)原因。當(dāng)不同的庫希望向?qū)ο筇砑訉傩远淮嬖诿Q沖突的風(fēng)險(xiǎn)時(shí),它們非常有用。

Symbol 為 JavaScrit 對(duì)象提供私有屬性還有點(diǎn)困難,但 Symbol 還有別外一個(gè)好處,就是避免當(dāng)不同的庫向?qū)ο筇砑訉傩源嬖诿麤_突的風(fēng)險(xiǎn)。

考慮這樣一種情況:兩個(gè)不同的庫想要向一個(gè)對(duì)象添加基本數(shù)據(jù),可能它們都想在對(duì)象上設(shè)置某種標(biāo)識(shí)符。通過簡單地使用 id 作為鍵,這樣存在一個(gè)巨大的風(fēng)險(xiǎn),就是多個(gè)庫將使用相同的鍵。

function lib1tag(obj) {
  obj.id = 42;
}
function lib2tag(obj) {
  obj.id = 369;
}

通過使用 Symbol,每個(gè)庫可以在實(shí)例化時(shí)生成所需的 Symbol。然后用生成 Symbol 的值做為對(duì)象的屬性:

const library1property = Symbol("lib1");
function lib1tag(obj) {
  obj[library1property] = 42;
}
const library2property = Symbol("lib2");
function lib2tag(obj) {
  obj[library2property] = 369;
}

出于這個(gè)原因,Symbol 似乎確實(shí)有利于JavaScript。

但是,你可能會(huì)問,為什么每個(gè)庫在實(shí)例化時(shí)不能簡單地生成隨機(jī)字符串或使用命名空間?

const library1property = uuid(); // random approach
function lib1tag(obj) {
  obj[library1property] = 42;
}
const library2property = "LIB2-NAMESPACE-id"; // namespaced approach
function lib2tag(obj) {
  obj[library2property] = 369;
}

這種方法是沒錯(cuò)的,這種方法實(shí)際上與 Symbol 的方法非常相似,除非兩個(gè)庫選擇使用相同的屬性名,否則不會(huì)有沖突的風(fēng)險(xiǎn)。

在這一點(diǎn)上,聰明的讀者會(huì)指出,這兩種方法并不完全相同。我們使用唯一名稱的屬性名仍然有一個(gè)缺點(diǎn):它們的鍵非常容易找到,特別是當(dāng)運(yùn)行代碼來迭代鍵或序列化對(duì)象時(shí)。考慮下面的例子:

const library2property = "LIB2-NAMESPACE-id"; // namespaced
function lib2tag(obj) {
  obj[library2property] = 369;
}
const user = {
  name: "Thomas Hunter II",
  age: 32
};
lib2tag(user);
JSON.stringify(user);
// "{"name":"Thomas Hunter II","age":32,"LIB2-NAMESPACE-id":369}"

如果我們?yōu)閷?duì)象的屬性名使用了 Symbol,那么 JSON 輸出將不包含它的值。這是為什么呢? 雖然 JavaScript 獲得了對(duì) Symbol 的支持,但這并不意味著 JSON 規(guī)范已經(jīng)改變! JSON 只允許字符串作為鍵,JavaScript 不會(huì)嘗試在最終 JSON 有效負(fù)載中表示 Symbol 屬性。

const library2property = "f468c902-26ed-4b2e-81d6-5775ae7eec5d"; // namespaced approach
function lib2tag(obj) {
  Object.defineProperty(obj, library2property, {
    enumerable: false,
    value: 369
  });
}
const user = {
  name: "Thomas Hunter II",
  age: 32
};
lib2tag(user);
console.log(user); // {name: "Thomas Hunter II", age: 32, f468c902-26ed-4b2e-81d6-5775ae7eec5d: 369}
console.log(JSON.stringify(user)); // {"name":"Thomas Hunter II","age":32}
console.log(user[library2property]); // 369

通過將 enumerable 屬性設(shè)置為 false 而“隱藏”的字符串鍵的行為非常類似于 Symbol 鍵。它們通過 Object.keys() 遍歷也看不到,但可以通過 Reflect.ownKeys() 顯示,如下的示例所示:

const obj = {};
obj[Symbol()] = 1;
Object.defineProperty(obj, "foo", {
  enumberable: false,
  value: 2
});
console.log(Object.keys(obj)); // []
console.log(Reflect.ownKeys(obj)); // [ "foo", Symbol() ]
console.log(JSON.stringify(obj)); // {}

在這點(diǎn)上,我們幾乎重新創(chuàng)建了 Symbol。隱藏的字符串屬性和 Symbol 都對(duì)序列化器隱藏。這兩個(gè)屬性都可以使用Reflect.ownKeys()方法讀取,因此它們實(shí)際上不是私有的。假設(shè)我們?yōu)閷傩悦淖址姹臼褂媚撤N名稱空間/隨機(jī)值,那么我們就消除了多個(gè)庫意外發(fā)生名稱沖突的風(fēng)險(xiǎn)。

但是,仍然有一個(gè)微小的區(qū)別。由于字符串是不可變的,而且 Symbol 總是保證惟一的,所以仍然有可能生成字符串組合會(huì)產(chǎn)生沖突。從數(shù)學(xué)上講,這意味著 Symbol 確實(shí)提供了我們無法從字符串中得到的好處。

在 Node.js 中,檢查對(duì)象時(shí)(例如使用 console.log() ),如果遇到名為 inspect 的對(duì)象上的方法,將調(diào)用該函數(shù),并將打印內(nèi)容??梢韵胂?,這種行為并不是每個(gè)人都期望的,通常命名為 inspect 的方法經(jīng)常與用戶創(chuàng)建的對(duì)象發(fā)生沖突。

現(xiàn)在 Symbol 可用來實(shí)現(xiàn)這個(gè)功能,并且可以在 equire("util").inspect.custom 中使用。inspect 方法在Node.js v10 中被廢棄,在 v1 1中完全被忽略, 現(xiàn)在沒有人會(huì)偶然改變檢查的行為。

模擬私有屬性

這里有一個(gè)有趣的方法,我們可以用來模擬對(duì)象上的私有屬性。這種方法將利用另一個(gè) JavaScript 特性: proxy(代理)。代理本質(zhì)上封裝了一個(gè)對(duì)象,并允許我們對(duì)與該對(duì)象的各種操作進(jìn)行干預(yù)。

代理提供了許多方法來攔截在對(duì)象上執(zhí)行的操作。我們可以使用代理來說明我們的對(duì)象上可用的屬性,在這種情況下,我們將制作一個(gè)隱藏我們兩個(gè)已知隱藏屬性的代理,一個(gè)是字符串 _favColor,另一個(gè)是分配給 favBook 的 S ymbol :

let proxy;

{
  const favBook = Symbol("fav book");

  const obj = {
    name: "Thomas Hunter II",
    age: 32,
    _favColor: "blue",
    [favBook]: "Metro 2033",
    [Symbol("visible")]: "foo"
  };

  const handler = {
    ownKeys: (target) => {
      const reportedKeys = [];
      const actualKeys = Reflect.ownKeys(target);

      for (const key of actualKeys) {
        if (key === favBook || key === "_favColor") {
          continue;
        }
        reportedKeys.push(key);
      }

      return reportedKeys;
    }
  };

  proxy = new Proxy(obj, handler);
}

console.log(Object.keys(proxy)); // [ "name", "age" ]
console.log(Reflect.ownKeys(proxy)); // [ "name", "age", Symbol(visible) ]
console.log(Object.getOwnPropertyNames(proxy)); // [ "name", "age" ]
console.log(Object.getOwnPropertySymbols(proxy)); // [Symbol(visible)]
console.log(proxy._favColor); // "blue"

使用 _favColor 字符串很簡單:只需閱讀庫的源代碼即可。 另外,通過蠻力找到動(dòng)態(tài)鍵(例如前面的 uuid 示例)。但是,如果沒有對(duì) Symbol 的直接引用,任何人都不能 從proxy 對(duì)象訪問"Metro 2033"值。

Node.js警告:Node.js中有一個(gè)功能會(huì)破壞代理的隱私。 JavaScript語 言本身不存在此功能,并且不適用于其他情況,例 如Web 瀏覽器。 它允許在給定代理時(shí)獲得對(duì)底層對(duì)象的訪問權(quán)。 以下是使用此功能打破上述私有屬性示例的示例:

const [originalObject] = process
  .binding("util")
  .getProxyDetails(proxy);
const allKeys = Reflect.ownKeys(originalObject);
console.log(allKeys[3]); // Symbol(fav book)

現(xiàn)在,我們需要修改全局 Reflect 對(duì)象,或者修改 util 流程綁定,以防止它們?cè)谔囟ǖ?Node.js 實(shí)例中使用。但這是一個(gè)可怕的兔子洞。如果你對(duì)掉進(jìn)這樣一個(gè)兔子洞感興趣,請(qǐng)查看我們的其他博客文章: Protecting your JavaScript APIs。

代碼部署后可能存在的BUG沒法實(shí)時(shí)知道,事后為了解決這些BUG,花了大量的時(shí)間進(jìn)行l(wèi)og 調(diào)試,這邊順便給大家推薦一個(gè)好用的BUG監(jiān)控工具 Fundebug。

你的點(diǎn)贊是我持續(xù)分享好東西的動(dòng)力,歡迎點(diǎn)贊!

交流

干貨系列文章匯總?cè)缦拢X得不錯(cuò)點(diǎn)個(gè)Star,歡迎 加群 互相學(xué)習(xí)。

https://github.com/qq44924588...

我是小智,公眾號(hào)「大遷世界」作者,對(duì)前端技術(shù)保持學(xué)習(xí)愛好者。我會(huì)經(jīng)常分享自己所學(xué)所看的干貨,在進(jìn)階的路上,共勉!

關(guān)注公眾號(hào),后臺(tái)回復(fù)福利,即可看到福利,你懂的。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/102651.html

相關(guān)文章

  • 重新鞏固JS(一)——JavaScript基本類型

    摘要:對(duì)象類型除了原始類型的值以外,其他都是對(duì)象。例如對(duì)象有屬性,值為,還有屬性,值為重要的特殊對(duì)象特殊對(duì)象函數(shù)數(shù)組日期正則全局錯(cuò)誤。重新鞏固JS系列,都是比較基礎(chǔ)的東西,可以進(jìn)行查漏補(bǔ)缺,很快看完,這是第一篇。 其他JS重新鞏固系列: 重新鞏固JS(二)——JavaScript操作符與表達(dá)式 重新鞏固JS(三)——JavaScript語句 JavaScript基本類型 在編程領(lǐng)域中,數(shù)據(jù)類型...

    pingan8787 評(píng)論0 收藏0
  • ECMAScript6學(xué)習(xí)筆記

    摘要:筆記和和是塊作用域的,是聲明常量用的。一個(gè)對(duì)象如果要有可被循環(huán)調(diào)用的接口,就必須在的屬性上部署遍歷器生成方法原型鏈上的對(duì)象具有該方法也可。這種方式會(huì)訪問注冊(cè)表,其中存儲(chǔ)了已經(jīng)存在的一系列。這種方式與通過定義的獨(dú)立不同,注冊(cè)表中的是共享的。 ECMAScript6 筆記 let 和 const let和const是塊作用域的 ,const是聲明常量用的。 {let a = 10;} a ...

    CODING 評(píng)論0 收藏0
  • 重學(xué)前端學(xué)習(xí)筆記(六)--JavaScript類型有哪些你不知道的細(xì)節(jié)?

    摘要:的碼點(diǎn)被稱為基本字符區(qū)域。關(guān)于的介紹,我準(zhǔn)備用文檔阮一峰來做一些介紹,具體的可以參考文檔引入的原因的對(duì)象屬性名都是字符串,這容易造成屬性名的沖突。其他的一些屬性可以去看文檔阮一峰注意函數(shù)前不能使用命令,否則會(huì)報(bào)錯(cuò)。 筆記說明 重學(xué)前端是程劭非(winter)【前手機(jī)淘寶前端負(fù)責(zé)人】在極客時(shí)間開的一個(gè)專欄,每天10分鐘,重構(gòu)你的前端知識(shí)體系,筆者主要整理學(xué)習(xí)過程的一些要點(diǎn)筆記以及感悟,完...

    Lsnsh 評(píng)論0 收藏0
  • 重學(xué)前端學(xué)習(xí)筆記(六)--JavaScript類型有哪些你不知道的細(xì)節(jié)?

    摘要:的碼點(diǎn)被稱為基本字符區(qū)域。關(guān)于的介紹,我準(zhǔn)備用文檔阮一峰來做一些介紹,具體的可以參考文檔引入的原因的對(duì)象屬性名都是字符串,這容易造成屬性名的沖突。其他的一些屬性可以去看文檔阮一峰注意函數(shù)前不能使用命令,否則會(huì)報(bào)錯(cuò)。 筆記說明 重學(xué)前端是程劭非(winter)【前手機(jī)淘寶前端負(fù)責(zé)人】在極客時(shí)間開的一個(gè)專欄,每天10分鐘,重構(gòu)你的前端知識(shí)體系,筆者主要整理學(xué)習(xí)過程的一些要點(diǎn)筆記以及感悟,完...

    joyvw 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<