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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

js之Symbol类型

發布時間:2023/12/16 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 js之Symbol类型 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • ECMA2019資料
  • 引入Symbol類型的背景
  • Symbol類型簡介
    • Symbol特性(所有數據類型都需要探討的問題)
      • 創建
      • Symbol值的描述
      • Symbol值的類型轉換與運算
        • 類型轉換
        • 類型轉換舉例
        • 涉及Symbol類型的運算: +-*/%
  • 全局Symbol表
    • Symbol.for() 與 Symbol.keyFor()
    • 全局Symbol表模型
    • 注意事項
    • 全局的測試
  • Symbol類型的辨析和理解
  • Symbol值作為屬性名
  • 含Symbol名屬性的對象的遍歷
  • ES6內置Symbol值
    • 說明:
    • 簡介
      • instanceof相關--Symbol.hasInstance
      • 類數組連接展開--Symbol.isConcatSpreadable
      • 指定生成衍生對象的構造函數--訪問器屬性Symbol.species
      • String.prototype.match匹配相關--Symbol.match
      • ToPrimitive抽象操作--Symbol.toPrimitive
      • 類型字符串--Symbol.toStringTag

ECMA2019資料

官方文檔地址

ECMA2019離線版(html版+pdf版+ES6入門)

引入Symbol類型的背景

  • ES5 的對象屬性名都是字符串,這容易造成屬性名沖突的問題

    舉例: 使用別人的模塊/對象, 又想為之添加新的屬性,這就容易使得新屬性名與原有屬性名沖突

Symbol類型簡介

  • Symbol是一種原始數據類型

    • 其余原始類型: undefined 、 null 、布爾值(Boolean)、字符串(String)、數值(Number)、對象(Object)
    • Symbol表示獨一無二的值
    • Symbol類型的"真實值"無法獲取,也就是說Symbol類型沒有對應的字面量
    • Symbol類型的意義在于區分彼此和不重復,不在于真實值

Symbol特性(所有數據類型都需要探討的問題)

創建

  • Symbol值只能通過Symbol()函數生成
let s1 s1 = = Symbol Symbol( ('foo' 'foo') ); ; let let s2 s2 = = Symbol Symbol( ('bar' 'bar') ); ; s1 s1 // Symbol(foo) // Symbol(foo) s2 s2 // Symbol(bar) // Symbol(bar) s1 s1. .toString toString( () ) // "Symbol(foo)" // "Symbol(foo)" s2 s2. .toString toString( () ) // "Symbol(bar)"
  • Symbol()函數前不能使用new命令(Symbol類型是原始值,不是對象)
  • Symbol類型不能添加屬性(不是對象)
  • Symbol類型是一種類似字符串的類型(可用作屬性名

ECMA2019標準相關:
?

? 解讀: (NewTarget是使用new命令調用函數時會創建的原生對象)

? (1) 如果NewTarget不為undefined(也就是使用了new命令), 拋出錯誤==>Symbol函數前不能使用new

? (2) 描述字符串description保存在symbol值內部的[[Description]]中

Symbol值的描述

  • Symbol 函數可以接受一個字符串作為參數

  • Symbol 函數的參數只是表示對當前 Symbol 值的描述

  • Symbol值的描述: 幫助開發者區分Symbol值

    • 在控制臺打印兩Symbol值時,能區分開來
    • 轉為字符串時,能區分開來

Symbol值的類型轉換與運算

類型轉換

根據ECMA2019,:

  • 類型轉換是用一些抽象操作來描述的
  • 隱式轉換是直接調用某一個抽象操作
  • 顯示轉換是抽象操作的包裝(加入判斷和控制等)
    • 類型的構造函數 當做 函數使用(例如: String())
    • 一些可能調用抽象操作的方法(例如: toString()或valueOf())

主要的類型轉換抽象操作:

1.對象轉原始類型:

2.轉Boolean:

3.轉Number:

4.轉字符串:

5.原始類型轉對象:

如上圖, 直接對symbol值應用抽象操作(隱式轉換):

  • ToBoolean(Symbol)==>true
  • ToString(Symbol)==>報錯
  • ToNumber==>報錯

類型轉換舉例

let sym = Symbol('My symbol');"your symbol is" + sym; //TypeError: can't convert symbol to string `your symbol is ${sym}`; //TypeError: can't convertString(sym); //'Symbol(My symbol)' sym.toString(); //'Symbol(My symbol)'

為什么上述代碼中String()和sym.toString()可以成功將symbol轉換為字符串?

ECMA2019:

(1)Symbol作為原始類型, 有對應的包裝對象類型, 所以我們可以用sym.toString() 調用方法而不出錯.

更進一步,我們測試一下Symbol類型的實例是否可改變和添加屬性:

let s = Symbol('s');//實例對象的對象保護檢測 console.log(`s實例是否可擴展: ${Object.isExtensible(s)}`); console.log(`s實例是否被凍結: ${Object.isFrozen(s)}`); console.log(`s實例是否被封閉: ${Object.isSealed(s)}`); //輸出: // s實例是否可擴展: false // s實例是否被凍結: true // s實例是否被seal: true//實例對象是否存在toString實例方法與[[Symbol.toStringTag]] console.log(`s實例是否有自定義toString方法: ${s.hasOwnProperty('toString')}`); console.log(`s實例是否有[[Symbol.toStringTag]]: ${s.hasOwnProperty(Symbol.toStringTag)}`); //輸出: // s實例是否有自定義toString方法: false // s實例是否有[[Symbol.toStringTag]]: false//----------------------------------------------------------------------------------------------------------//Symbol.prototype的對象保護檢測 console.log(`Symbol.prototype是否可擴展: ${Object.isExtensible(Symbol.prototype)}`); console.log(`Symbol.prototype是否被凍結: ${Object.isFrozen(Symbol.prototype)}`); console.log(`Symbol.prototype是否被封閉: ${Object.isSealed(Symbol.prototype)}`); //輸出: // Symbol.prototype是否可擴展: true // Symbol.prototype是否被凍結: false // Symbol.prototype是否被封閉: false

總結:

  • Symbol類的實例是不可擴展的,不能添加,修改和刪除屬性
  • 因此, sym.toString()所調用的,必定是Symbol.prototype.toString()原型方法
  • 而Symbol.prototype.toString()調用Symbol.DescriptiveString(sym),返回一個組合字符串: "Symbol(" + 描述字符串 + ")"

(2)String(sym)可行的原因:

可見, 在進行ToString抽象操作之前,對參數進行了判斷, 并對為Symbol類型的情況另行處理(調用Symbol.DescriptiveString(value)),故而沒有發生我們預期中的報錯.

涉及Symbol類型的運算: ±*/%

結論: 一律報錯

原因:(ECMA2019)

加法:

減法:

乘除余:

可見, 進行運算,必然逃不過要進行抽象操作 ToNumber,而對Symbol值進行ToNumber抽象操作就會報錯.因此,Symbol值無法參與運算.


全局Symbol表

Symbol.for() 與 Symbol.keyFor()

  • 應用背景:

    • Symbol值解決了對象屬性不被覆蓋問題
    • 但Symbol值只能作用在局部
      • node環境: 不同時導出Symbol值, 導出對象的對應屬性無法被其他模塊主動訪問
      • 瀏覽器屬性: 不同時傳遞Symbol值, 對象的對應屬性不能被其他iframe主動訪問
    • 為此,出現了全局Symbol表及管理它的兩個函數Symbol.for()與Symbol.keyFor()
  • Symbol.for()函數

    ECMA2019:

  • Symbol.keyFor()函數

    ECMA2019:

全局Symbol表模型

注意事項

? Symbol.for()與Symbol.keyFor()都是針對全局Symbol表進行查詢和新建的, 其余位置的Symbol值不會被訪問或影響.

全局的測試

  • node: 全局環境–js程序的運行環境(在各個模塊之上)
//主文件.js const symMain = Symbol.for('Maintest');const {sym,IsSameSymbol} = require('./附文件.js');console.log(sym === Symbol.for('test')); //true console.log(IsSameSymbol(symMain)); //true //附文件.js const sym = Symbol.for('test'); const symMain = Symbol.for('Maintest');function IsSameSymbol(s){return s===symMain; }module.exports = {sym: sym,IsSameSymbol: IsSameSymbol };
  • 瀏覽器端: 全局環境–在各個frame之上
<!DOCTYPE html><html lang="zh-cn"><head><meta charset="UTF-8"><title>全局Symbol測試</title></head><body>hello world!<script>var iframe = document.createElement('iframe');iframe.src = String(window.location);document.body.appendChild(iframe);window.onload = function(){alert(iframe.contentWindow.Symbol.for('test')===Symbol.for('test'));};</script></body></html>

Symbol類型的辨析和理解

  • symbol數據類型的真實值

    • 與symbol值關聯的字符串其實是 它的描述,方便控制臺打印時區分各個symbol值,而不是symbol值的真實值

    • symbol類型的真實值是無法獲取和訪問的,也并不重要,因為不會被開發者用到

    • symbol類型的意義在于它的唯一不可重復性,而不在于其真實值

  • 如何理解symbol類型的值

    • symbol類型值的特點

      • 新的原始數據類型(不同于number,string,boolean等)
      • 可以充當屬性名和變量名(類似string)
      • 除非是同一句symbol()生成的值,否則不相等(類似object)

      根據以上特點,這個symbol類型的值特別像我們日常生活中的一種東西–二維碼. 二維碼的特點如下:

      • 屬于圖片(不是數值,也不是字符串)(新的類型)

      • 可以完成字符串的一些功能

        • 掃碼得文本,掃碼獲得網址(文本和網址等都屬于字符串)

          舉例: 二維碼生成網站

    • 我們可以把symbol類型理解成 包含特定信息的二維碼

      它包含的特定信息有三個:

      • 生成它的symbol()代碼調用的行號
      • 生成它的symbol()代碼調用的起始列號
      • 該symbol()代碼調用所在文件的完整文件路徑(包含文件名)

      通過這三個特定信息,我們不難看出每一個symbol()調用生成的symbol值(二維碼)都一定是唯一且不可重復的: (舉例說明)

      完整文件路徑

      行號與起始列號

      行號起始列號
      t1110
      t2129
      t3210

      由此可見: 要想三個信息都符合, 那必須是同一個文件中的同一句Symbol()生成的同一個symbol值.因此是唯一不可重復的.

      接著,我們將這三個信息生成二維碼:

      接著,我們用t1代表的symbol值作為屬性名給obj對象添加屬性:

      var t1 = Symbol(); var t2 = Symbol(); var t3 = Symbol();console.log(`t1==t2: ${t1==t2} t1==t3: ${t1==t3} t2==t3: ${t2==t3}`); //全是falsevar obj = {[t1]: function(){console.log('hello_world');} };obj[t1]();

    我們把symbol類型值比作二維碼的話, obj[t1](); 就相當于:


Symbol值作為屬性名

  • 概述:

    • 每個Symbol值均不相等,獨一無二
    • Symbol值作為標識符
      • 完全避免屬性同名問題
      • 防止方法/屬性被不小心覆蓋
  • 定義Symbol屬性名的三種方法:

    • 當做普通字符串使用(不必加引號)

      let mySymbol = Symbol();let a ={}; a[mySymbol] = 'hello';
    • 字面量對象內的方括號定義

      let mySymbol = Symbol();let a = {[mySymbol]:'hello' };
    • 用Object.defineProperty定義(實質上還是直接當做普通字符串)

      let mySymbol = Symbol();let a = {}; Object.defineProperty(a, mySymbol, {value: 'hello'});
  • 注意事項

    • 不能用點運算符訪問Symbol名的屬性

    • 字面量方括號定義法中,方括號是必須的,否則仍是一個普通字符串名屬性

    • 因為每一個symbol值都是獨一無二的
      要使用以symbol為名的屬性,只能在定義symbol值的模塊內使用
      要想將該對象的該屬性開放給其模塊訪問,必須同時導出: 對象 + symbol值

      (該特性可以用于分配和限制屬性的訪問權限?)

      • 有權限的,導出symbol值
      • 無權限的,不導出symbol值

含Symbol名屬性的對象的遍歷

  • Symbol名屬性的鍵值無法被常規遍歷方法發現,包括:
    • for…in
    • for…of
    • Object.keys()
    • Object.getOwnPropertyNames()
    • JSON.stringify()
  • 獲取方法:
    • Object.getOwnPropertySymbols()
    • Reflect.ownKeys()–常規鍵名+Symbol鍵名

ES6內置Symbol值

說明:

  • 這些值保存在Symbol的Constructor函數對象的屬性之中

  • 這些值通常指向對象的內部方法

  • 通過這些Symbol值修改對象內部方法不一定有效:

    • 對象可能被凍結或封閉(例如: Symbol類型值)

    • Symbol名屬性的配置項可能被設為不可配置與不可修改

      (即屬性描述符為:{configurable:false,writable:false})

簡介

instanceof相關–Symbol.hasInstance

foo instanceof Foo; ==> Foo[Symbol.hasInstance](foo)

下例展示了一個對instanceof的欺騙:

class Myclass{[Symbol.hasInstance](foo){return foo instanceof Array;} }[1,2,3] instanceof new Myclass; //true

類數組連接展開–Symbol.isConcatSpreadable

? 該屬性規定了該對象用于Array.prototype.concat() 時,是否可以展開

let arr1 = ['c','d']; ['a','b'].concat(arr1,'e'); //['a','b','c','d','e'] arr1[Symbol.isConcatSpreadable]; //undefinedlet arr2 = ['c','d']; arr1[Symbol.isConcatSpreadable] = false; ['a','b'].concat(arr2,'e'); //['a','b',['c','d'],'e']

ECMA2019相關資料:

(1)Array.prototype.concat

(2)IsConcatSpreadable(O)

解讀:

  • 只在調用Array.prorotype.concat()函數中生效,

    • 其他concat函數中無效(沒有針對該symbol名屬性的操作)
  • 根據上述資料:

    • 不僅可用于數組,也可以用于類數組對象(有length屬性,有數字鍵名)

    • 不設置該symbol名屬性時(undefined值),數組與類數組表現相反:

      這是因為IsConcatSpreadble(o)資料中的最后一句:return ?IsArray(o)

      也就是說: 無定義時,數組默認展開,類數組默認不展開

    • 數組/類數組之中的空位(數字鍵不連續)保留到合并后的新數組

指定生成衍生對象的構造函數–訪問器屬性Symbol.species

  • Symbol.species

    • 是一個構造函數(類)的訪問器屬性,需要用get設置

    • 存在于一些可進行衍生的類中

      ECMA2019資料:
      21.2.4.2get RegExp [ @@species ]
      22.1.2.5get Array [ @@species ]
      22.2.2.4get %TypedArray% [ @@species ]
      23.1.2.2get Map [ @@species ]
      23.2.2.2get Set [ @@species ]
      24.1.3.3get ArrayBuffer [ @@species ]
      24.2.3.2get SharedArrayBuffer [ @@species ]
      25.6.4.6get Promise [ @@species ]

    • 該Symbol值代表的屬性(訪問器的返回值)是一個構造函數,被用于構造衍生對象

    • 默認值: this.constructor,也就是用于創建衍生對象的原對象實例的構造函數

  • 舉例說明: (以數組的map方法為例)

    代碼:

    class MyArray extends Array {static get [Symbol.species](){return Array;}introduce(){console.log('I am a MyArray instance');} }const a = new MyArray(); const b = a.map(x=>x); //利用Array.prototype.map()創建衍生對象bconsole.log(a.constructor); //[Function: MyArray] console.log(b instanceof MyArray); //false console.log(b.introduce); //undefined

    資料及知識補充:

    Array.prototype.map

    ArraySpeciesCreate

由上圖,可見, 確實是在創建衍生對象時,獲取了原對象的構造函數的Symbol.species屬性作為衍生對象的構造函數.結合示例代碼:

(1)原對象: 對象a–MyArray類的實例

(2)原對象的構造函數: MyArray類的構造函數

(3)原對象的構造函數的Symbol.species: 返回值為數組的構造函數–Array

String.prototype.match匹配相關–Symbol.match

因此: str.match(obj)等同于obj[Symbol.match](str)

class MyMatcher {[Symbol.match](str){return 'hello world'.indexOf(str);} }'e'.match(new MyMatcher()); //1

同系列的其他Symbol:

  • Symbol.search
  • Symbol.replace
  • Symbol.split

ToPrimitive抽象操作–Symbol.toPrimitive

? 其值是一個將對象類型轉換為原始類型值的方法, 被抽象操作ToPrimitive調用.

如上圖, 可見該函數接收一個字符串參數hint(三種取值對應三種模式):

  • Number:需要轉成數值
  • String:需要轉成字符串
  • Default: 數值,字符串皆可(number)

示例代碼:

let obj = {[Symbol.toPrimitive](hint){switch(hint){case 'number': return 123;case 'default': return 'default';case 'string': return 'str';default: throw new Error();}} };console.log(2 * obj); //246 console.log(3 + obj); //3default, 此時hint為default,詳見ECMA2019 加號(addtion operator) console.log(String(obj)); //str

類型字符串–Symbol.toStringTag

對象的 Symbol.toStringTag 屬性的值是一個字符串

在該對象上調用 Object.prototype.toString 方法,若該屬性存在,其值會出現在 toString 方法返回的字符串之中,表示對象的類型。也就是說,這個屬性可以用來定制[object Object] 或 [object Array] 中 object 后面的那個字符串.

ECMA2019資料:

總結

以上是生活随笔為你收集整理的js之Symbol类型的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。