ES6 中的 Symbol — 多的是你不知道的事

一、Symbol 类型

Symbol 是 ES6 中新增的基本数据类型,它属于原始值,代表唯一的、不可变. 【目的】是保证对象属性的唯一性,也是为了解决属性冲突.

二、基本用法

  • 使用 Symbol() 函数初始化,由于它是属于原始类型,所以 typeof 会直接返回 symbol
let sym = Symbol();
console.log(typeof sym); // symbol
  • 初始化可传参,如 Symbol(‘hello’) 那么 hello 就会作为这个 symbol 的描述,为了方便未来调试程序时有更直观的展示
let sym1 = Symbol();
console.log(sym1); // Symbol()let sym2 = Symbol('this is sym2');
console.log(sym2); // Symbol(this is sym2)
  • 通过 Symbol() 初始化同样参数的变量,也不会相等,可以理解为调用一次 Symbol() 就得到一个新的标识,不论参数是否相同
let sym1 = Symbol();
let sym2 = Symbol();
console.log(sym1 == sym2); // falselet sym3 = Symbol('foo');
let sym4 = Symbol('foo');
console.log(sym3 == sym4); // false
  • Symbol()函数不能与 new 关键字一起作为构造函数使用,目的是为了避免和 Boolean、String 或 Number 的实例那样,存在包装对象
// 1. 实例化例子
let myBoolean = new Boolean();
console.log(typeof myBoolean); // "object"let myString = new String();
console.log(typeof myString); // "object"let myNumber = new Number();
console.log(typeof myNumber); // "object"let mySymbol = new Symbol(); // TypeError: Symbol is not a constructor// 2. 包装对象例子
let myBoolean = new Boolean();
let myString = new String();
let myNumber = new Number();
let mySymbol = Symbol();myBoolean.add = ()=>{};
myString.add = ()=>{};
myNumber.add = ()=>{};
mySymbol.add = ()=>{};console.log(myBoolean.add);// ()=>{}
console.log(myString.add);// ()=>{}
console.log(myNumber.add);// ()=>{}
console.log(mySymbol.add);// undefind// 3. Symbol 转包装对象
let mySymbol = Object(Symbol());
mySymbol.add = ()=>{};
console.log(mySymbol.add);// ()=>{}
  • 全局符号注册表
    • Symbol() 每初始化一次,就会生成一个新 symbol
    • Symbol.for() 初始化时可以传递字符串参数,并检查注册表是否存在对应的 symbol,如果没有则直接存储在注册表中,下一次在调用 Symbol.for() 并且传参一致时,直接返回注册表中存在的 symbol
    let sym1 = Symbol.for('foo'); // 创建新符号
    let sym2 = Symbol.for('foo'); // 重用已有符号 
    console.log(sym1 === sym2); // trueconsole.log(Symbol('foo') === Symbol('foo')); // false
    console.log(Symbol('foo') === Symbol.for('foo')); // false
    
  • Symbol.keyFor() 查询注册表,参数为全局 symbol,即用 Symbol.for() 创建得到的 symbol
// 创建全局符号
let sym1 = Symbol.for('foo');
console.log(Symbol.keyFor(s1)); // foo// 创建普通符号
let sym2 = Symbol('bar');
console.log(Symbol.keyFor(sym2)); // undefined

三、使用 Symbol 作为属性正常用于对象的【属性】

    // 1. symbol 定义let s1 = Symbol('1'),s2 = Symbol('2'),s3 = Symbol('3'),s4 = Symbol('4'),s5 = Symbol('5');// 2. 多种赋值方式,和普通对象属性一致let o = {[s1]: 's1',num: 1,str: 'hello'}o[s2] = 's2'Object.defineProperty(o, s3, { value: 's3' });Object.defineProperties(o, {[s4]: { value: 's4' },[s5]: { value: 's5' },});console.log(o); // {num: 1, str: 'hello', Symbol(1): 's1', Symbol(2): 's2', Symbol(3): 's3', Symbol(4): 's4', Symbol(5): 's5',}// 3. 获取 key 的方式不一致,getOwnPropertyNames 和 getOwnPropertySymbols 方法是互斥的console.log(Object.getOwnPropertyNames(o)); // ['num', 'str']console.log(Object.getOwnPropertySymbols(o)); // [Symbol(1), Symbol(2), Symbol(3), Symbol(4), Symbol(5)]// 4. 获取所有的对象自身上的属性(普通属性 + symbol属性)console.log(Object.getOwnPropertyDescriptors(o)); // {num: {…}, str: {…}, Symbol(1): {…}, Symbol(2): {…}, Symbol(3): {…}, …}console.log(Reflect.ownKeys(o)); // ['num', 'str', Symbol(1), Symbol(2), Symbol(3), Symbol(4), Symbol(5)]

四、常用内置符号

ECMAScript 6 引入了一批常用内置符号(well-known symbol)

  • 目的是用于暴露语言内部的行为,开发者可以直接访问、重写或模拟这些行为
  • 内置符号都以 Symbol 工厂函数字符串属性的形式存在【例如:Symbol.iterator】
  • 所有内置符号属性都是不可写、不可枚举、不可配置的

1、与遍历有关(Symbol.asyncIterator & Symbol.Iterator)

  • Symbol.asyncIterator 表示实现异步迭代器 API 的函数,可 for-await-of 循环会利用这个函数执行异步迭代操作
    • PS:ES2018 规范中定义的,只有在最新版的浏览器才支持
 class Emitter {constructor(max) {this.max = max;this.asyncIndex = 0;}async *[Symbol.asyncIterator]() {while (this.asyncIndex < this.max) {yield new Promise((resolve) => resolve(this.asyncIndex++));}}}let emitter = new Emitter(5);for await (const x of emitter) {console.log(x);}
  • Symbol.Iterator 表示实现迭代器 API 的函数,可用 for-of 循环这个函数执行迭代操作
    • 能够使用 for-of 遍历的对象,要么自身实现了或者原型上存在键名为 [Symbol.iterator] 的函数,并且这个函数默认会返回一个对象,这个对象带有 next 函数的实现,比如 Generator 函数.
 // 1. 未实现 [Symbol.iterator]let obj = {};// 抛出错误: Uncaught TypeError: obj is not iterablefor(const x of obj) {console.log(x);}// 2. 实现 [Symbol.iterator]let obj = {[Symbol.iterator]: function* () {let i = 0while (i < 5) {yield i++;}}}for (const x of obj) {console.log(x); // 0 1 2 3 4}

2. 与 instanceof 相关(Symblo.hasInstance | Symbol.species)

  • instanceof 操作符可以用来确定一个对象实例的原型链上是否有原型.
  • Symblo.hasInstance 作为一个属性表示“一个方法,该方法决定一个构造器对象是否认可一个对象是它的实例,由 instanceof 操作符使用.
  // 1. 通过 instanceof 判断class Foo {}let f = new Foo();console.log(f instanceof Foo) // true// 2. 通过 [Symbol.hasInstance] 的判断class Foo {}let f = new Foo();console.log(Foo[Symbol.hasInstance]) // ƒ [Symbol.hasInstance]() { [native code] }console.log(Foo[Symbol.hasInstance](f)) // true// 3. 自定义 instanceof 的返回值class Foo {static [Symbol.hasInstance] = function () {return false;}}let f = new Foo();console.log(f instanceof Foo); // false
  • Symbol.species 作为一个属性表示一个函数值,该函数作为创建派生对象的构造函数.
     class Arr1 extends Array {}class Arr2 extends Array {static get [Symbol.species]() {return Array}}let arr1 = new Arr1();arr1 = arr1.concat('arr111'); console.log(arr1 instanceof Arr1); // trueconsole.log(arr1 instanceof Array); // truelet arr2 = new Arr2();arr2 = arr2.concat('arr222');console.log(arr2 instanceof Arr2); // falseconsole.log(arr2 instanceof Array); // true

3. 与数组有关(Symbol.isConcatSpreadable)

  • Symbol.isConcatSpreadable 表示一个布尔值,如果是 true,则意味着对象应 该用 Array.prototype.concat() 打平其(数组 || 伪数组)的元素.
    // 1. 正常使用 Array.prototype.concatlet arr1 = [1,2];let arr2 = [3,4];// 直接访问得到 undefinded ,但合并的结果是被拍平的,因此,可以认为默认情况下需要被拍平console.log(arr2[Symbol.isConcatSpreadable]); // undefindedlet newArr = arr1.concat(arr2); console.log(newArr);// [1, 2, 3, 4]// 2. 修改不需要被拍平的元素let arr1 = [1,2];let arr2 = [3,4];console.log(arr2[Symbol.isConcatSpreadable]); // undefinedarr2[Symbol.isConcatSpreadable] = false;console.log(arr2[Symbol.isConcatSpreadable]); // falselet newArr = arr1.concat(arr2);console.log(newArr); // [1, 2, Array(2)]// 3. 伪数组也具备这个属性let likeObj = { length: 2, 0: 'name', 1: 'age' }; let  arr = [];console.log(likeObj[Symbol.isConcatSpreadable]); // undefined console.log(arr.concat(likeObj)); // [{ length: 1, 0: 'name', 1: 'age' }] likeObj[Symbol.isConcatSpreadable] = true; console.log(arr.concat(likeObj)); // ['name', 'age']

4. 与字符串相关 Symbol.[match | replace | search | split]

PS:以上这些方法,在正则表达式 RegExp.protype 上都有默认实现,因此字符串的大多数 api 都和 RegExp 对象一起使用,默认情况下,即便传递的不是 RegExp 类型,也会被强制转换为 RegExp 类型去使用.

  • Symbol.match 作为一个属性表示一个正则表达式方法,该方法用正则表达式 去匹配字符串。由 String.prototype.match()方法使用
    • string.match(exp),如果传入的参数(exp) 是非 RegExp 类型,就会被转成 RegExp 类型,如:‘11’.macth( {num:11} ) ==> ‘11’.macth(new RegExp( {num:11} ))
    • string.match(exp) ,如果不想让参数(exp) 被强转成 RegExp 类型,可以给传入的 exp 实现 Symbol.match 方法
// 1. string.match(RegExp)
console.log('hello'.match(/llo/)); // ["llo", index: 2, input: "hello", groups: undefined]// 2. string.match(非 RegExp 类型)
var obj = {};
var result = 'hello'.match(obj);// 这里相当于 'hello'.match( new RegExp(obj) ) 
console.log(result); // ["e", index: 1, input: "hello", groups: undefined]// 3. 自定义 string.match 的匹配规则
var obj = {[Symbol.match]: (target) => { // target 是 foobarlet index = target.indexOf('oo');return {value: target.slice(index,3),index};}
};
console.log('foobar'.match(obj)); // {value: "oo", index: 1}
  • Symbol.replace 作为一个属性表示一个正则表达式方法,该方法替换一个字符 串中匹配的子串。由 String.prototype.replace()方法使用
    • string.replace(exp, str),如果传入的参数(exp) 是非 RegExp 类型,就会被转成 RegExp 类型
    • string.replace(exp, str) ,如果不想让参数(exp) 被强转成 RegExp 类型,可以给传入的 exp 实现 Symbol.replace 方法
// 1. Symbol.replace(exp, str)
var target = 'hello';
var newStr = target.replace(/o/,'66');
console.log(newStr); // hell66// 2. Symbol.replace(非 RegExp 类型, str)
var target = 'hello';
var newStr = target.replace(['o'],'77'); // 相当于 target.replace(new RegExp(['o']),'77')
console.log(newStr); // hell77// 3. 实现自定义 Symbol.replace 替换规则
var target = 'hello';
var obj = {[Symbol.replace]: function(target){var index = target.indexOf('e');return 'haha' + target.slice(index);}
};
var newStr = target.replace(obj,'66');
console.log(newStr); // hahaello
  • Symbol.search 作为一个属性表示一个正则表达式方法,该方法返回字符串中匹配正则表达式的索引。由 String.prototype.search()方法使用
    • string.search(exp),如果传入的参数(exp) 是非 RegExp 类型,就会被转成 RegExp 类型
    • string.search(exp) ,如果不想让参数(exp) 被强转成 RegExp 类型,可以给传入的 exp 实现 Symbol.search 方法
// 1. string.search(exp)
var target = 'hello';
var index = target.search('ll');
console.log(index); // 2// 2. string.search(非 RegExp 类型)
var target = 'hello';
var obj =  {};
var index = target.search(obj); // target.search(new RegExp(obj))
console.log(index); // 1// 3. 实现自定义 string.search 查找规则
var target = 'hello';
var obj = {[Symbol.search]: function(target){var index = target.indexOf('ll');return index;}
};
var index = target.search(obj);
console.log(index); // 2
  • Symblo.split 作为一个属性表示一个正则表达式方法,该方法在匹配正则表达式的索引位置拆分字符串。由 String.prototype.split()方法使用.
    • string.split(exp),如果传入的参数(exp) 是非 RegExp 类型,就会被转成 RegExp 类型
    • string.split(exp) ,如果不想让参数(exp) 被强转成 RegExp 类型,可以给传入的 exp 实现 Symbol.split 方法
// 1. string.split(RegExp)
var target = 'hello-world';
var arr = target.split('-');
console.log(arr); // ["hello", "world"]// 2. string.split(非 RegExp 类型)
var target = 'hello-world';
var arr = target.split(['-']);// 相当于 target.split(new RegExp(['-']))
console.log(arr); // ["hello", "world"]// 3. 实现自定义 string.split 拆分规则
var target = 'hello-world';
var obj = {[Symbol.split]: function(target){var length = target.length;var arr = [];for(var index = 0; index < length; index++){arr[index] = target[index];}return arr;}
};
var arr = target.split(obj);
console.log(arr); // ["h", "e", "l", "l", "o", "-", "w", "o", "r", "l", "d"]

5. 与复杂类型相关 Symbol.[toPrimitive | toStringTag | unscopables]

  • Symbol.toPrimitive表示一个方法,该方法将对象转换为相应的原始值. 由 ToPrimitive 抽象操作使用
// 1. 复杂类型 转 基本类型
var obj = {};// 1.1 obj 转 string
console.log(obj + ' & hello'); // [object Object] & hello// 1.2 obj 转 number
console.log(obj - 1); // NaN// 1.3 obj 转 boolean
console.log(!!obj); // true// 2. 实现自定义的转换规则
var obj = {[Symbol.toPrimitive]: function (type) {switch (type) {case 'string':return '666';case 'number':return 889;case 'default':default:return 0;}}
};
// 2.1 obj 转 string
console.log(String(obj)); // 666 & hello
// 2.2 obj 转 number
console.log(obj - 1); // 888
// 2.3 obj 转 boolean
console.log(Boolean(obj)); // true
  • Symbol.toStringTag 表示一个字符串,该字符串用于创建对象的默认字符串描述。由内置方法 Object.prototype.toString()使用
// 1. Object.prototype.toString() 用于返回数据类型
var arr = [1],obj = { name: 'zs' },func = function getName() { console.log(obj.name); };
// 1.1 调用 Object.prototype.toString()
console.log(obj.toString()); // [object Object]
// 1.2 这两个调用的都不是 Object.prototype.toString 方法// 1.2.1 Array.prototype.toString()
console.log(arr.toString()); // 1// 1.2.2 Function.prototype.toString()
console.log(func.toString()); // function getName() { console.log(obj.name); }// 3. 通过 Symbol.toStringTag 更改 Object 类型
obj[Symbol.toStringTag] = 'Array';
console.log(obj.toString()); // [object Array]// typeof 返回类型不会受影响
console.log(typeof obj); // object
  • Symbol.unscopables 表示一个对象,该对象所有的以及继承的属性,都会从关联对象的 with 环境绑定中排除
    • 不推荐使用 with,因此也不推荐使用 Symbol.unscopables
// 1. 正常可见
let o = { foo: 'bar' };
with (o) {console.log(foo); // bar
}// 2. 设置不可见
o[Symbol.unscopables] = {foo: true
};
with (o) {console.log(foo); // ReferenceError
}

EDN

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/13851.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

CSS 技巧:如何让 div 完美填充 td 高度

引言 一天哈比比突然冒出一个毫无理头的一个问题: 本文就该问题进行展开… 原文链接: 昆仑虚F2E 一、需求说明 大致需求如下, 当然这里做了些简化 有如下初始代码: 一个自适应的表格每个单元格的宽度固定 200px每个单元格高度则是自适应每个单元格内是一个 div 标签, div 标签…

清华、国科大、智谱团队提出LongReward:利用AI反馈改进长文本大语言模型

长文本&#xff08;Long-context&#xff09;大模型性能的优劣&#xff0c;在很大程度上取决于其能否全面理解长上下文场景下的复杂信息。 然而&#xff0c;现有的合成有监督微调&#xff08;SFT&#xff09;数据由于缺少人类核验&#xff0c;往往会影响长文本大模型的性能&am…

2024 年 10 款替代 Postman 的工具,有免费有开源

10 款替代 Postman 的工具&#xff0c;有免费有开源&#xff1a; 工具名称支持的系统是否免费是否开源ApifoxWindows, macOS, Linux免费否Yapi无限制是是InsomniaWindows, macOS, Linux免费版付费版是Hoppscotch浏览器是是SoapUIWindows, macOS, Linux免费版付费版是Katalon S…

IDEA报包不存在,但实际存在

IDEA版本2024.2.1 现象 在IDEA里启动运行项目&#xff0c;报某个类有问题&#xff0c;引入的包不存在。 点击这个引入的包&#xff0c;可以看到它在左侧外部库里存在。 试过的无效方法 双击ctrl&#xff0c;在弹出框中mvn idea:idea在文件里&#xff0c;清空缓存并重启在右…

从词向量到多模态嵌入:大型语言模型的技术、应用及未来方向

索引词—大型语言模型、词嵌入、上下文嵌入、多模态表示、自然语言处理 摘要—词嵌入和语言模型通过将语言元素表示在连续向量空间中&#xff0c;彻底改变了自然语言处理&#xff08;NLP&#xff09;。本综述回顾了分布假设和上下文相似性等基础概念&#xff0c;追溯了从稀疏表…

超越传统:探索ONLYOFFICE的革命性办公新纪元

目录 &#x1f341;引言 &#x1f341;一、ONLYOFFICE产品简介 &#xff08;一&#xff09;、介绍 &#xff08;二&#xff09;、基本功能简介 &#x1f341;二、核心功能具体介绍 1、编辑操作 2、文本与段落&#xff1a; 3、样式与图形&#xff1a; 4、表格与图表&…

【GESP】C++一级真题(202406)luogu-B4001,立方数

2024年6月GESP一级真题。循环类问题。 题目题解详见&#xff1a;【GESP】C一级真题(202406)luogu-B4001&#xff0c;立方数 | OneCoder https://www.coderli.com/gesp-1-luogu-b4001/https://www.coderli.com/gesp-1-luogu-b4001/ C GESP专项交流频道&#xff1a;GESP学习交…

SAP 创建物料主数据报错:估价范围3010还没有生产式的物料帐簿

通过接口创建物料主数据&#xff08;模拟MM01&#xff09;&#xff0c;报错如图&#xff1a; 处理方案1&#xff1a;&#xff08;我的不行&#xff0c;提示已经是生产的&#xff09; 将评估范围的物料分类账设置为生产 事务码: CKMSTART - 物料分类帐的生产开始 处理方案2&a…

Python | Leetcode Python题解之第560题和为K的子数组

题目&#xff1a; 题解&#xff1a; class Solution:def subarraySum(self, nums: List[int], k: int) -> int:dic{0:1}sums,res0,0for num in nums:sumsnumresdic.get(sums-k,0)dic[sums]dic.get(sums,0)1return res

sql注入之二次注入(sqlilabs-less24)

二阶注入&#xff08;Second-Order Injection&#xff09;是一种特殊的 SQL 注入攻击&#xff0c;通常发生在用户输入的数据首先被存储在数据库中&#xff0c;然后在后续的操作中被使用时&#xff0c;触发了注入漏洞。与传统的 SQL 注入&#xff08;直接注入&#xff09;不同&a…

AOA-LSTM多输入回归预测|算术优化算法-长短期神经网络|Matlab

目录 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 亮点与优势&#xff1a; 二、实际运行效果&#xff1a; 三、方法原理介绍&#xff1a; 四、完整代码数据下载&#xff1a; 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 本代码基于Matlab平…

认知战认知作战:郑成功收复台湾的认知作战分析与策略

认知战认知作战&#xff1a;郑成功收复台湾的认知作战分析与策略 认知战认知作战&#xff1a;郑成功收复台湾的认知作战分析与策略 关键词&#xff1a;认知作战,新质生产力,人类命运共同体,认知战,认知域,认知战研究中心,认知战争,认知战战术,认知战战略,认知域作战研究,认知…

单细胞技术加持,扩增子测序重回高大上| 一区微生物多组学研究新思路!

俗称“万金油”的扩增子技术已经再难发出好文章了&#xff1f;实则不然!关联高端霸气上档次的单细胞转录组技术&#xff0c;扩增子研究依旧可以焕发新生机&#xff0c;重回高大上。 近日&#xff0c;檀国大学在《communications biology》上最新发表的文章打破了传统微生物组分…

遗传算法与深度学习实战(23)——利用遗传算法优化深度学习模型

遗传算法与深度学习实战&#xff08;23&#xff09;——利用遗传算法优化深度学习模型 0. 前言1. 神经进化2. 使用遗传算法作为深度学习优化器小结系列链接 0. 前言 神经进化涵盖了所有用于改进深度学习的进化算法。更具体地说&#xff0c;神经进化用来定义应用于深度学习的特…

Hbase入门

目录 Hbase逻辑结构 一、基础知识 1. Hbase逻辑结构 行键(Rowkey)&#xff1a;唯一标识一行数据&#xff0c;按照字典序(row_key1 < row_key11 < rowkey2)排列.列Col&#xff1a;数据记录的一条属性列族&#xff1a;将多列划分为一类&#xff0c;视为一个列族。例如上图…

三维模型-管道-建模规范

一、阀门模型处理 Max中的阀门模型,备份之前可拆分的阀门模型。 将需要选择的阀门,合并(不打组)成一个模型。 材质在不同模型上,按照需求分好不同的材质 例如:阀门、管道,需要分成不同的材质和相对应的不同模型。 二、管道模型处理 1) 普通管道 默认展开UV ;2) 流…

golang go语言 组建微服务架构详解 - 代码基于开源框架grpc+nacos服务管理配置平台

整体介绍&#xff1a; 本文主要介绍如何用go语言 来组建微服务的框架&#xff0c;grpc服务管理 示例框架 代码由grpcnacos go sdk 组成。 grpc负责将调用序列化并传递到远端&#xff0c;nacos负责服务发现和服务管理。 grpc和nacos都是开源产品。代码复制下来就能跑。 微服…

open3d

open3d open3d用于 3D 数据处理的现代库。 简介 Open3D 是一个开源库&#xff0c;支持快速开发处理 3D 数据的软件。Open3D 前端公开了一组精心挑选的 C 和 Python 数据结构和算法。后端经过高度优化&#xff0c;并设置为并行化。Open3D 是从零开始开发的&#xff0c;具有一更…

一个轻量级RAG文本切块项目Chonkie

**Chonkie&#xff1a;**实用的RAG分块库&#xff0c;轻量级、速度快&#xff0c;可随时对文本进行分块 支持的方法 Chonkie 提供了多个分块器&#xff0c;可高效地为RAG应用程序拆分文本。以下是可用分块器的简要概述&#xff1a; TokenChunker&#xff1a;将文本分割成固定大…

如何通过AB测试找到最适合的Yandex广告内容

想要在Yandex上找到最能吸引目标受众的广告内容&#xff0c;A/B测试是一个不可或缺的步骤。通过对比不同版本的广告&#xff0c;我们可以发现哪些元素最能引起用户的共鸣。首先&#xff0c;设计两个或多个广告版本&#xff0c;确保每个版本在标题、文案、图片等关键元素上有所不…