ES6-Symbol

ES6 中的 Symbol: 独特的数据类型与强大应用

        引言

        在 JavaScript 的发展长河中,ES6(ECMAScript 2015)无疑是一座重要的里程碑,带来了诸多令人瞩目的新特性。其中,Symbol 类型的引入,为 JavaScript 开发者们开启了一扇全新的大门,它为解决传统开发中遇到的一系列棘手问题提供了创新的方案。Symbol 作为一种独一无二的基本数据类型,以其独特的性质和广泛的应用场景,极大地提升了代码的健壮性、可维护性以及安全性。在这篇博客中,我们将全方位、深层次地探索 ES6 中的 Symbol,从其基础概念到复杂的高级应用,力求为大家呈现一个完整而清晰的 Symbol 知识体系。

        Symbol 的作用

解决属性名冲突难题

        在传统的 JavaScript 开发过程中,尤其是在多人协作或者使用第三方库的复杂项目里,对象属性名冲突是一个令人头疼的常见问题。不同的模块或者代码片段可能会不经意地使用相同的属性名,这往往会导致意想不到的错误和难以调试的问题。Symbol 的出现,为这个难题提供了完美的解决方案。由于每个 Symbol 都是独一无二的,无论在何处创建,它都不会与其他 Symbol 重复。这就意味着,当我们使用 Symbol 作为对象的属性名时,能够确保属性名的唯一性,从而有效避免属性名冲突。例如,在一个大型项目中,多个模块可能都需要向某个公共对象添加特定的属性,使用 Symbol 作为属性名,各个模块之间就不会因为属性名相同而产生冲突,极大地提高了代码的稳定性。

实现对象的有效封装

        封装是面向对象编程中的重要概念,它能够将对象的内部状态和实现细节隐藏起来,只对外提供必要的接口。在 JavaScript 中,利用 Symbol 可以实现对象的内部属性封装。由于 Symbol 属性不能通过常规的对象遍历方法(如for...in、Object.keys())获取,我们可以创建一些不希望被外部随意访问或修改的对象内部属性。这些内部属性只有在对象内部的方法中才能被访问和操作,从而增强了对象的封装性和安全性。比如,我们可以创建一个包含敏感信息的对象,将这些敏感信息的属性名设置为 Symbol 类型,这样外部代码就无法轻易地获取或修改这些敏感信息,保护了对象的内部状态。

拓展对象功能与增强代码可读性

        Symbol 还为拓展对象的功能提供了一种优雅的方式。在不修改现有对象原型的前提下,我们能够使用 Symbol 为对象添加额外的功能。这在处理第三方库的对象时尤为有用,我们可以在不影响其原有结构和其他代码使用的情况下,为对象增添新的行为。同时,使用 Symbol 作为事件类型或者模块间的唯一标识等,可以使代码更加清晰、易读。例如,在事件驱动编程中,使用 Symbol 作为事件类型,能够避免与其他可能定义的事件类型发生冲突,并且从代码中可以直观地看出事件的唯一性,增强了代码的可读性和可维护性。

        Symbol () 方法

创建唯一的 Symbol 值

创建一个 Symbol 非常简单,通过调用Symbol()函数即可。如下所示:

let sym = Symbol();console.log(typeof sym); // "symbol"

        这里,Symbol()函数返回一个全新的、唯一的 Symbol 值。typeof操作符用于检测数据类型,当应用于通过Symbol()创建的变量时,会返回"symbol",表明它是一种新的数据类型

注意:Symbol是一种原始数据类型,并不是一种构造函数,不能new

使用描述参数增加可读性

        Symbol()函数可以接受一个可选的字符串参数,这个参数作为对该 Symbol 的描述,主要用于在调试或打印输出时进行区分,方便开发者理解该 Symbol 的用途。例如:        

let sym1 = Symbol('description');let sym2 = Symbol('description');console.log(sym1 === sym2); // false

        尽管sym1和sym2的描述相同,但它们仍然是两个完全不同的 Symbol 值。这个描述参数并不会影响 Symbol 的唯一性,只是为了在开发过程中提供更多的上下文信息,帮助开发者更好地理解代码的意图。

        注意:Symbol的描述只是对于这个Symbol值的描述,并不是它的值,真正的值存在内存里面,无法被看到

        Symbol 与其它数据类型的转换关系和运算关系

与字符串的转换

        Symbol 不能直接转换为字符串,这是为了保持其唯一性和特殊性。如果尝试将 Symbol 与字符串进行连接操作,会导致类型错误。例如:

let sym = Symbol('test');let result = sym + 'string'; // TypeError: can't convert symbol to string

        然而,如果确实需要将 Symbol 转换为字符串形式进行显示,可以使用Symbol.prototype.toString()方法。该方法会返回一个包含 Symbol 描述的字符串。例如:

let sym = Symbol('test');let str = sym.toString();console.log(str); // "Symbol(test)"

与数字的转换

        Symbol 与数字之间也不存在直接的转换关系。不能将 Symbol 当作数字进行数学运算,如加法、减法等。例如:

let sym = Symbol('test');let num = 5 + sym; // TypeError: can't convert symbol to number

        这是因为 Symbol 表示的是独一无二的值,其语义与数字完全不同,不适合进行常规的数学运算。

与布尔值的关系

        在 JavaScript 中,Symbol 类型的值在布尔运算中被视为真值。也就是说,当 Symbol 类型的值出现在需要布尔值的上下文中(如if语句、while循环条件等)时,会被当作true处理。例如:

let sym = Symbol('test');if (sym) {console.log('Symbol is truthy');}

        上述代码会输出Symbol is truthy,表明 Symbol 在布尔运算中表现为真值。

        如何在对象里面使用 Symbol 类型的变量作为属性名

避免属性名冲突的应用

        在对象中使用 Symbol 作为属性名是其最常见的应用之一,主要用于避免属性名冲突。例如:

let obj = {};let sym1 = Symbol('prop1');let sym2 = Symbol('prop2');obj[sym1] = 'value1';obj[sym2] = 'value2';console.log(obj[sym1]); // "value1"console.log(obj[sym2]); // "value2"

        在这个例子中,我们创建了一个空对象obj,然后使用两个不同的 Symbol 作为属性名,分别为其赋值。由于 Symbol 的唯        一性,即使有其他代码也尝试向obj对象添加名为prop1或prop2的属性(假设使用的也是 Symbol 类型),也不会与现有的属性产生冲突。这样,在复杂的项目中,不同模块对同一对象进行属性添加时,使用 Symbol 可以确保各个属性的独立性和唯一性。

实现对象内部属性的封装

        通过将 Symbol 类型的变量作为对象的属性名,还可以实现对象内部属性的封装。例如:

let myObject = (function () {let internalProp = Symbol('internal');let obj = {setValue(value) {this[internalProp] = value;},getValue() {return this[internalProp];}};return obj;})();myObject.setValue(42);console.log(myObject.getValue()); // 42// 无法直接访问内部属性console.log(myObject[Symbol('internal')]); // undefined

        在上述代码中,我们在一个立即执行函数表达式内部创建了一个 Symbol 类型的变量internalProp,并将其作为obj对象的内部属性名。obj对象提供了setValue和getValue方法来间接操作这个内部属性。由于外部代码无法直接获取到internalProp这个 Symbol,所以不能直接访问或修改该内部属性,从而实现了对象内部属性的封装,提高了对象的安全性和可维护性。

        Symbol 的几个常用属性和方法

description 属性

        每个 Symbol 实例都有一个只读的description属性,它返回创建 Symbol 时传入的描述字符串。如果创建 Symbol 时没有提供描述字符串,description属性的值为undefined。例如:

let sym1 = Symbol('test description');console.log(sym1.description); // "test description"let sym2 = Symbol();console.log(sym2.description); // undefined

        description属性主要用于在调试和日志记录中,帮助开发者快速了解 Symbol 的用途和含义。通过查看description属性的值,开发者可以更清晰地理解代码中各个 Symbol 的作用,尤其是在复杂的项目中,众多的 Symbol 可能会让代码阅读变得困难,description属性能够提供关键的上下文信息。(描述只是描述!!)

Object.getOwnPropertySymbols () 方法

        由于正常的遍历方法无法获取Symbol数据类型的属性,故有如下方法获取

        Object.getOwnPropertySymbols()方法用于获取一个对象的所有 Symbol 类型的自有属性(即直接在该对象上定义的属性,而不是从原型链继承的属性)。该方法返回一个包含所有 Symbol 属性的数组。例如:

let obj = {};let sym1 = Symbol('prop1');let sym2 = Symbol('prop2');obj[sym1] = 'value1';obj[sym2] = 'value2';let symbols = Object.getOwnPropertySymbols(obj);console.log(symbols); // [Symbol(prop1), Symbol(prop2)]

        在这个例子中,我们首先创建了一个对象obj,并使用两个 Symbol 作为属性名向其添加了属性。然后,通过Object.getOwnPropertySymbols()方法获取obj对象的所有 Symbol 类型的自有属性,返回的数组包含了我们之前定义的sym1和sym2。这个方法在需要遍历对象的所有 Symbol 属性时非常有用,比如在进行对象的深度克隆或者对对象的所有属性(包括 Symbol 属性)进行统一处理时。

Reflect.ownKeys () 方法

        Reflect.ownKeys()方法返回一个包含对象自身所有属性键(包括字符串类型和 Symbol 类型)的数组。与Object.getOwnPropertyNames()方法不同,Object.getOwnPropertyNames()方法只能获取对象的字符串类型的自有属性键,而Reflect.ownKeys()方法可以获取所有类型的自有属性键。例如:

let obj = {};let sym1 = Symbol('prop1');let sym2 = Symbol('prop2');obj[sym1] = 'value1';obj[sym2] = 'value2';obj.stringProp = 'string value';let keys = Reflect.ownKeys(obj);console.log(keys); // [Symbol(prop1), Symbol(prop2), "stringProp"]

        在上述代码中,我们创建了一个对象obj,包含两个 Symbol 类型的属性和一个字符串类型的属性。通过Reflect.ownKeys()方法,我们获取到了obj对象的所有自有属性键,包括 Symbol 类型和字符串类型

        Symbol.for () 和 Symbol.keyFor () 方法

                Symbol.for () 方法

        Symbol.for()方法用于在全局 Symbol 注册表中搜索具有指定键的 Symbol。如果找到了匹配的 Symbol,则返回该 Symbol;如果没有找到,则在全局 Symbol 注册表中创建一个新的 Symbol,并返回它。与直接使用Symbol()函数创建 Symbol 不同,Symbol.for()创建的 Symbol 是全局共享的,只要键相同,无论在何处调用Symbol.for(),返回的都是同一个 Symbol。例如:

let sym1 = Symbol.for('globalSymbol');let sym2 = Symbol.for('globalSymbol');console.log(sym1 === sym2); // true

        在这个例子中,我们两次调用Symbol.for('globalSymbol'),尽管是在不同的代码位置,但由于使用了相同的键'globalSymbol',所以sym1和sym2指向的是同一个 Symbol。这种全局共享的特性使得 Symbol 在不同模块或者不同作用域之间能够实现统一的标识,在大型项目中,当需要在多个地方使用相同的唯一标识时,Symbol.for()非常有用。

此时描述就不再只是起描述的作用了,还起到值的作用

                Symbol.keyFor () 方法

        Symbol.keyFor()方法用于返回一个已登记的 Symbol 在全局 Symbol 注册表中的键。它接受一个 Symbol 作为参数,如果该 Symbol 是通过Symbol.for()方法在全局 Symbol 注册表中创建的,则返回其对应的键;如果该 Symbol 不是通过Symbol.for()方法创建的(例如直接使用Symbol()函数创建的),则返回undefined。例如:

let sym1 = Symbol.for('globalSymbol');let key = Symbol.keyFor(sym1);console.log(key); // "globalSymbol"let sym2 = Symbol('localSymbol');let key2 = Symbol.keyFor(sym2);console.log(key2); // undefined

        在上述代码中,对于通过Symbol.for()创建的sym1,Symbol.keyFor()方法返回了其在全局 Symbol 注册表中的键'globalSymbol'。而对于直接使用Symbol()创建的sym2,由于它不在全局 Symbol 注册表中,所以Symbol.keyFor()方法返回undefined。Symbol.keyFor()方法在需要根据 Symbol 获取其对应的全局键时非常有用,比如在进行全局 Symbol 的管理或者在不同模块之间进行基于键的 Symbol 查找时。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

K8S学习之基础五十:k8s中pod时区问题并通过kibana查看日志

k8s中pod默认时区不是中国的,挂载一个时区可以解决 vi pod.yaml apiVersion: v1 kind: Pod metadata:name: counter spec:containers:- name: countimage: 172.16.80.140/busybox/busybox:latestimagePullPolicy: IfNotPresentargs: [/bin/sh,-c,i0;while true;do …

创新前沿 | 接管主机即刻增量CDP备份,高效保障接管期间业务安全!

科力锐创新前沿系列 接管主机增量CDP备份 高效保障接管业务安全 当核心系统遭遇系统故障或误操作导致数据逻辑损毁等,往往需要将生产业务主机接管起来,继续对外提供服务,保障业务连续性。 然而,你的接管主机真的安全吗?一旦接…

Android平台毫秒级低延迟HTTP-FLV直播播放器技术探究与实现

一、前言 在移动互联网蓬勃发展的今天,视频播放功能已成为众多Android应用的核心特性之一。面对多样化的视频格式和传输协议,开发一款高效、稳定的视频播放器是许多开发者追求的目标。FLV(Flash Video)格式,尽管随着H…

STL之list

1. list的介绍和使用 1.1 list的介绍 list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。list的底层是带头双向循环链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向 其…

26考研——查找_树形查找_二叉排序树(BST)(7)

408答疑 文章目录 三、树形查找二叉排序树(BST)二叉排序树中结点值之间的关系二叉树形查找二叉排序树的查找过程示例 向二叉排序树中插入结点插入过程示例 构造二叉排序树的过程构造示例 二叉排序树中删除结点的操作情况一:被删除结点是叶结点…

C++异常处理完全指南:从原理到实战

文章目录 异常的基本概念基本异常抛出与捕获多类型异常捕获异常重新抛出异常安全异常规范(noexcept)栈展开与析构标准库异常总结 异常的基本概念 异常是程序运行时发生的非预期事件(如除零、内存不足)。C通过try、catch和throw提…

汽车方向盘开关功能测试的技术解析

随着汽车智能化与电动化的发展,方向盘开关的功能日益复杂化,从传统的灯光、雨刷控制到智能语音、自动驾驶辅助等功能的集成,对开关的可靠性、耐久性及安全性提出了更高要求。本文结合北京沃华慧通测控技术有限公司(以下简称“慧通…

matplotlib——南丁格尔玫瑰

南丁格尔玫瑰图(Nightingale Rose Chart),是一种特殊形式的柱状图,它以南丁格尔(Florence Nightingale)命名,她在1858年首次使用这种图表来展示战争期间士兵死亡原因的数据。 它将数据绘制在极坐…

【大模型基础_毛玉仁】4.4 低秩适配方法

目录 4.4 低秩适配方法4.4.1 LoRA1)方法实现2)参数效率 4.4.2 LoRA 变体1)打破低秩瓶颈(例ReLoRA)2)动态秩分配(例AdaLoRA)3)训练过程优化(例DoRA&#xff09…

融合YOLO11与行为树的人机协作智能框架:动态工效学优化与自适应安全决策

人工智能技术要真正发挥其价值,必须与生产生活深度融合,为产业发展和人类生活带来实际效益。近年来,基于深度学习的机器视觉技术在工业自动化领域取得了显著进展,其中YOLO(You Only Look Once)算法作为一种…

Java为什么要使用线程池?

前言1.对线程的管理更加的规范化2.降低创建线程和销毁线程的开销 前言 之前对于Java线程池的理解,一直停留在:对于Java中的多线程机制来说,如果不使用线程池的话,线程的使用就会变得杂乱无章。这一步。一直没有深入去理解为什么其…

告别分库分表,时序数据库 TDengine 解锁燃气监控新可能

达成效果: 从 MySQL 迁移至 TDengine 后,设备数据自动分片,运维更简单。 列式存储可减少 50% 的存储占用,单服务器即可支撑全量业务。 毫秒级漏气报警响应时间控制在 500ms 以内,提升应急管理效率。 新架构支持未来…

TDengine 3.3.2.0 集群报错 Post “http://buildkitsandbox:6041/rest/sql“

原因: 初始化时处于内网环境下,Post “http://buildkitsandbox:6041/rest/sql“ 无法访问 修复: vi /etc/hosts将buildkitsandbox映射为本机节点 外网环境下初始化时没有该问题

【Linux】POSIX信号量与基于环形队列的生产消费者模型

目录 一、POSIX信号量: 接口: 二、基于环形队列的生产消费者模型 环形队列: 单生产单消费实现代码: RingQueue.hpp: main.cc: 多生产多消费实现代码: RingQueue.hpp: main.…

【13】Ajax爬取案例实战

目录 一、准备工作 二、爬取目标 三、初步探索:如何判断网页是经js渲染过的? 四、爬取列表页 4.1 分析Ajax接口逻辑 4.2 观察响应的数据 4.3 代码实现 (1)导入库 (2)定义一个通用的爬取方法…

嵌入式八股RTOS与Linux---网络系统篇

前言 关于计网的什么TCP三次握手 几层模型啊TCP报文啥的不在这里讲,会单独分成一个计算机网络模块   这里主要介绍介绍lwip和socket FreeRTOS下的网络接口–移植LWIP 实际上FreeRTOS并不自带网络接口,我们一般会通过移植lwip协议栈让FreeRTOS可以通过网络接口收发数据,具体可…

全分辨率免ROOT懒人精灵-自动化编程思维-设计思路-实战训练

全分辨率免ROOT懒人精灵-自动化编程思维-设计思路-实战训练 1.2025新版懒人精灵-实战红果搜索关键词刷视频:https://www.bilibili.com/video/BV1eK9kY7EWV 2.懒人精灵-全分辨率节点识别(红果看广告领金币小实战):https://www.bili…

【更新中】【React】基础版React + Redux实现教程(Vite + React + Redux + TypeScript)

本项目是一个在react中,使用 redux 管理状态的基础版实现教程,用简单的案例练习redux的使用,旨在帮助学习 redux 的状态管理机制,包括 store、action、reducer、dispatch 等核心概念。 项目地址:https://github.com/Yv…

【MySQL】从零开始:掌握MySQL数据库的核心概念(四)

人们之所以不愿改变,是因为害怕未知。但历史唯一不变的事实,就是一切都会改变。 前言 这是我自己学习mysql数据库的第四篇博客总结。后期我会继续把mysql数据库学习笔记开源至博客上。 上一期笔记是关于mysql数据库的表格约束,没看的同学可以…

AP 场景架构设计(一) :OceanBase 读写分离策略解析

说明:本文内容对应的是 OceanBase 社区版,架构部分不涉及企业版的仲裁副本功能。OceanBase社区版和企业版的能力区别详见: 官网链接。 概述​ 当两种类型的业务共同运行在同一个数据库集群上时,这对数据库的配置等条件提出了较高…