学长突然问我用过Symbol吗,我哽咽住了(准备挨骂)

简介: 本文将带读者从基本使用,特性应用到内置Symbol三个方面,带大家深入Symbol这个神奇的类型!

Symbol对于一些前端小白(比如我)来讲,没有特别使用过,只是在学习JS的时候了解了大概的概念,当时学习可能并没有感觉到Symbol在开发中有什么特别的作用,而在学习一段时间后回头看一遍,顿悟!

而本文将带读者从基本使用特性应用内置Symbol三个方面,带大家深入Symbol这个神奇的类型!


什么是Symbol😶‍🌫️

Symbol作为原始数据类型的一种,表示独一无二的值,在之前,对象的键以字符串的形式存在,所以极易引发键名冲突问题,而Symbol的出现正是解决了这个痛点,它的使用方式也很简单。

Symbol的使用

创建一个Symbol与创建Object不同,只需要a = Symbol()即可

let a = Symbol()
typeof a
复制代码

使用时需要注意的是:不可以使用new来搭配Symbol()构造实例,因为其会抛出错误

let a = new Symbol()
typeof a // Symbol is not a constructor
复制代码

通常使用new来构造是想要得到一个包装对象,而Symbol不允许这么做,那么如果我们想要得到一个Symbol()的对象形式,可以使用Object()函数

let a = Symbol()
let b = Object(a)
typeof b // object
复制代码

介绍到这里,问题来了,Symbol看起来都一样,我们怎么区分呢?我们需要传入一个字符串的参数用来描述Symbol()

let a = Symbol()
let b = Symbol()
复制代码

上面看来ab的值都是Symbol,代码阅读上,两者没区分,那么我们调用Symbol()函数的时候传入字符串用来描述我们构建的Symbol()

let a = Symbol("a")
let b = Symbol("b")
复制代码

Symbol的应用✌️

Symbol的应用其实利用了唯一性的特性。

作为对象的属性

大家有没有想过,如果我们在不了解一个对象的时候,想为其添加一个方法或者属性,又怕键名重复引起覆盖的问题,而这个时候我们就需要一个唯一性的键来解决这个问题,于是Symbol出场了,它可以作为对象的属性的键,并键名避免冲突。

let a = Symbol()
let obj = {}
obj[a] = "hello world"
复制代码

我在上面创建了一个symbol作为键的对象,其步骤如下

  1. 创建一个Symbol
  2. 创建一个对象
  3. 通过obj[]Symbol作为对象的键

值得注意的是我们无法使用.来调用对象的Symbol属性,所以必须使用[]来访问Symbol属性

降低代码耦合

我们经常会遇到这种代码

if (name === "猪痞恶霸") {
    console.log(1)
}
复制代码

又或者

switch (name) {
        case "猪痞恶霸"
        console.log(1)
        case "Ned"
        console.log(2)
}
复制代码

在这两段段代码中作为判断控制语句的"猪痞恶霸""Ned"被称为魔术字符串,即与代码强耦合的字符串,可以理解为:与我们的程序代码强制绑定在一起,然而这会导致一个问题,在条件判断复杂的情况下,我们想要更改我们的判断条件,就需要更改每一个判断控制,维护起来非常麻烦,所以我们可以换一种形式来解决字符串与代码强耦合。

const judge = {
    name_1:"猪痞恶霸"
    name_2:"Ned"
}
switch (name) {
        case judge.name_1
        console.log(1)
        case judge.name_2
        console.log(2)
}
复制代码

我们声明了一个存储判断条件字符串的对象,通过修改对象来自如地控制判断条件,当然本小节的主题是Symbol,所以还能继续优化!

const judge = {
    rectangle:Symbol("rectangle"),
    triangle:Symbol("triangle")
}
function getArea(model, size) {
    switch (model) {
        case judge.rectangle:
            return size.width * size.height
        case judge.triangle:
            return size.width * size.height / 2
    }
}
let area = getArea(judge.rectangle ,{width:100, height:200})
console.log(area)
复制代码

为了更加直观地了解我们优化的过程,上面我创建了一个求面积的工具函数,利用Symbol的特性,我们使我们的条件判断更加精确,而如果是字符串形式,没有唯一的特点,可能会出现判断错误的情况。

全局共享Symbol

如果我们想在不同的地方调用已经同一Symbol即全局共享的Symbol,可以通过Symbol.for()方法,参数为创建时传入的描述字符串,该方法可以遍历全局注册表中的的Symbol,当搜索到相同描述,那么会调用这个Symbol,如果没有搜索到,就会创建一个新的Symbol

为了更好地理解,请看下面例子

let a = Symbol.for("a")
let b = Symbol.for("a")
a === b // true
复制代码

如上创建Symbol

  1. 首先通过Symbol.for()在全局注册表中寻找描述为aSymbol,而目前没有符合条件的Symbol,所以创建了一个描述为aSymbol
  2. 当声明b并使用Symbol.for()在全局注册表中寻找描述为aSymbol,找到并赋值
  3. 比较ab结果为true反映了Symbol.for()的作用

再来看看下面这段代码

let a = Symbol("a")
let b = Symbol.for("a")
a === b // false
复制代码

woc,结果竟然是false,与上面的区别仅仅在于第一个Symbol的创建方式,带着惊讶的表情,来一步一步分析一下为什么会出现这样的结果

  1. 使用Symbol("a")直接创建,所以该Symbol("a")不在全局注册表中
  2. 使用Symbol.for("a")在全局注册表中寻找描述为aSymbol,并没有找到,所以在全局注册表中又创建了一个描述为a的新的Symbol
  3. 秉承Symbol创建的唯一特性,所以ab创建的Symbol不同,结果为false

问题又又又来了!我们如何去判断我们的Symbol是否在全局注册表中呢?

Symbol.keyFor()帮我们解决了这个问题,他可以通过变量名查询该变量名对应的Symbol是否在全局注册表中

let a = Symbol("a")
let b = Symbol.for("a")
Symbol.keyFor(a) // undefined
Symbol.keyFor(b) // 'a'
复制代码

如果查询存在即返回该Symbol的描述,如果不存在则返回undefined

以上通过使用Symbol.for()实现了Symbol全局共享,下面我们来看看Symbol的另一种应用

内置Symbol值又是什么❔

上面的Symbol使用是我们自定义的,而JS有内置了Symbol值,个人的理解为:由于唯一性特点,在对象内,作为一个唯一性的键并对应着一个方法,在对象调用某方法的时候会调用这个Symbol值对应的方法,并且我们还可以通过更改内置Symbol值对应的方法来达到更改外部方法作用的效果。

为了更好地理解上面这一大段话,咱们以Symbol.hasInstance作为例子来看看内置Symbol到底是个啥!

class demo {
    static [Symbol.hasInstance](item) {
        return item === "猪痞恶霸"
    }
}
"猪痞恶霸" instanceof demo // true
复制代码

Symbol.hasInstance对应的外部方法是instanceof,这个大家熟悉吧,经常用于判断类型。而在上面的代码片段中,我创建了一个demo类,并重写了Symbol.hasInstance,所以其对应的instanceof行为也会发生改变,其内部的机制是这样的:当我们调用instanceof方法的时候,内部对应调用Symbol.hasInstance对应的方法即return item === "猪痞恶霸"


相关文章
|
小程序 索引 容器
微信小游戏制作工具中的滚动列表插件如何使用?
微信小游戏制作工具中的滚动列表插件如何使用?
417 1
|
8月前
|
人工智能 算法 机器人
《探秘移动游戏的物理魔法:引擎应用与性能进阶指南》
物理引擎是移动游戏中实现真实感和趣味性的核心技术,通过模拟重力、碰撞、惯性等物理现象,增强沉浸感。在赛车游戏、解谜游戏等不同类型中,物理引擎发挥着独特作用,如《愤怒的小鸟》利用Box2D精准模拟碰撞效果。然而,移动设备性能有限,需优化物理模拟,包括控制物体数量、调整更新频率、简化模型等。未来,随着硬件升级和AI融合,物理引擎将实现更复杂的效果,如流体模拟和智能动态调整,同时工具的易用性提升也将助力开发者创造更精彩的游戏体验。
316 13
|
测试技术 Python
手动解决Python模块和包依赖冲突的具体步骤是什么?
需要注意的是,手动解决依赖冲突可能需要一定的时间和经验,并且需要谨慎操作,避免引入新的问题。在实际操作中,还可以结合使用其他方法,如虚拟环境等,来更好地管理和解决依赖冲突😉。
|
API 数据安全/隐私保护 开发者
商品订单接口获取及作用详解
在电商平台的后台管理中,订单接口至关重要。本文介绍了如何获取商品订单接口及其作用,包括注册开发者账号、创建应用、申请API权限和调用接口获取订单及物流信息的详细步骤,并提供了Python示例代码。同时,强调了遵守平台规则、数据安全和接口维护的重要性。
|
人工智能 边缘计算 监控
边缘AI计算技术应用-实训解决方案
《边缘AI计算技术应用-实训解决方案》提供完整的实训体系,面向高校和科研机构的AI人才培养需求。方案包括云原生AI平台、百度AIBOX边缘计算硬件,以及8门计算机视觉实训课程与2门大模型课程。AI平台支持大规模分布式训练、超参数搜索、标注及自动化数据管理等功能,显著提升AI训练与推理效率。硬件涵盖多规格AIBOX服务器,支持多种推理算法及灵活部署。课程涵盖从计算机视觉基础到大模型微调的完整路径,通过真实商业项目实操,帮助学员掌握前沿AI技术和产业应用。
591 2
|
机器学习/深度学习 人工智能 自然语言处理
探索人工智能的未来:技术革新与趋势展望
【10月更文挑战第23天】探索人工智能的未来:技术革新与趋势展望
|
机器学习/深度学习 算法 PyTorch
计算机视觉快速入门:探索图像处理
本文介绍了计算机视觉的基本概念和学习路径,包括图像处理、特征提取、目标检测、图像分类与分割以及深度学习在该领域的应用。初学者应从图像处理基础开始,学习数字图像概念、处理技术及开源库如OpenCV。接着,探索特征提取与描述方法,如SIFT和HOG,以及目标检测的算法,如Haar级联和YOLO。进一步,掌握图像分类和分割技术,涉及深度学习模型如CNN。通过实践项目深化理解,并关注最新研究,持续学习和探索,以在计算机视觉领域不断进步。
|
SQL 存储 Java
Sharding-JDBC 如何实现分片
以上是V 哥在教学过程中实现分片的示例步骤,Sharding-JDBC能够实现SQL的分片操作,将请求路由到正确的数据库和表中,从而实现数据的水平扩展,这是在使用例如 MySQL作为数据库的场景中经常会使用到的,但如果你的企业正在考虑分布式数据库迁移,V 哥建议可以考虑 TiDB 或 OceanBase 这样的分布式数据库,因为它们天然就支持分布式,而不需要考虑这些。
308 0
|
Cloud Native Java Nacos
Spring Cloud Nacos:概念与实战应用
【4月更文挑战第28天】Spring Cloud Nacos 是一个基于 Spring Cloud 构建的服务发现和配置管理工具,适用于微服务架构。Nacos 提供了动态服务发现、服务配置、服务元数据及流量管理等功能,帮助开发者构建云原生应用。
306 0
|
机器学习/深度学习 人工智能 算法
AI大模型学习理论基础
本文探讨了AI大模型学习的理论基础,包括深度学习(模拟神经元工作原理,通过多层非线性变换提取特征)、神经网络结构(如前馈、循环和卷积网络)、训练方法(监督、无监督、强化学习)、优化算法(如SGD及其变种)、正则化(L1、L2和dropout防止过拟合)以及迁移学习(利用预训练模型加速新任务学习)。这些理论基础推动了AI大模型在复杂任务中的应用和人工智能的发展。