JavaScript 之 Symbol 数据类型

news2024/11/17 13:42:08

一、简介

symbol类型是ES6新引入的一种基本数据类型,该类型具有静态属性和静态方法。其中静态属性暴露了几个内建的成员对象,静态方法暴露了全局的symbol注册。

symbol类型具有以下特点:① 唯一性:每个symbol值都是唯一的;② 不可变性:symbol值是不可被修改的;③ 属性标识符:symbol类型的值可以作为对象属性的标识符key;④Symbol可以与其他数据类型进行运算,但不能被强制转换为其他数据类型。利用前两个特性,可以避免属性名的冲突和保证属性不会被意外覆盖。

symbol类型在许多库和框架中被广泛应用,常见的应用场景有创建私有属性、定义常量、定义事件名称以及实现各种标识符相关的功能。

二、创建

1、Symbol([description])

​ 通过Symbol([description])函数创建symbol类型的值,但并不会被添加到全局symbol表中,可选参数description,是字符串类型的数据,表示对当前symbol的描述,该参数只用来作为标识,并不会影响symbol的唯一性。

​ 但该函数并不是一个构造函数,因为该函数不支持new Symbol()的语法,如果使用该语法将会抛出TypeError错误。每个通过Symbol()函数获取的symbol类型的值都是唯一的、独一无二的,即使两个symbol拥有相同的description,它们也属于两个不同的值。

// 创建symbol数据
let s = Symbol();
console.log(s); // Symbol()
console.log(typeof s); // symbol
// 创建symbol数据并添加描述
let s1 = Symbol('one');
console.log(s1); // Symbol(one)
console.log(typeof s1); // symbol
// 创建symbol数据并添加相同的描述
let s2 = Symbol('one');
console.log(s2); // Symbol(one)
console.log(typeof s2); // symbol
// 判断symbol数据是否相等
console.log(s1 === s2); // false
console.log(s1 == s2); // false
// 使用new关键字创建symbol数据
let s3 = new Symbol(); // Uncaught TypeError: Symbol is not a constructor

2、Symbol.for(key)

​ 第四部分,常用方法章节中讲解。

三、常用属性

1、description(只读)

​ 该属性是一个只读属性,用于获取当前symbol值的描述字符串。

案例代码:
// 创建symbol数据 不添加描述
let s = Symbol();
// 创建symbol数据并添加描述
let s1 = Symbol('one');

// 使用description输出symbol数据的描述
console.log(s.description); // undefined
console.log(s1.description); // one

2、hasInstance

​ 该属性是 Symbol 类型的一个内置静态属性,属性值是一个 Symbol 值,它是不可变的,用于定义对象的 @@hasInstance 方法的键,@@hasInstance 方法用于判断一个对象是否为某个构造函数的实例,我们可以利用该属性自定义instanceof操作符在某个类上的行为。

​ 当一个对象被使用 instanceof 运算符检查其原型链时,会调用该对象的 @@hasInstance 方法。换句话说,obj instanceof Constructor 实际上是调用了 Constructor[Symbol.hasInstance](obj)。因此我们可以通过自定义的 @@hasInstance 方法,来自定义判断对象是否为某个构造函数的实例。

案例代码:
class MyArray {
    static [Symbol.hasInstance](instance) {
      return Array.isArray(instance);
    }
}
console.log([] instanceof MyArray); // true
console.log({} instanceof MyArray); // fales

3、isConcatSpreadable

​ 该属性是 Symbol 类型的一个内置静态属性,属性值是一个 Symbol 值,它是不可变的,用于定义对象的 @@isConcatSpreadable 的键。@@isConcatSpreadable 方法是一个内部Symbol,用于确定对象在调用数组的 concat() 方法时是否展开其元素。

​ 如果@@isConcatSpreadable 返回true(默认值),则对象元素被展开合并;如果方法返回false,则将对象作为单个元素添加到数组中,构成多维数组的形式。我们可以通过设置对象的 @@isConcatSpreadable 来自定义对象在 concat() 方法中的展开行为

案例代码:
let arr1 = [1, 2, 3]
let arr2 = ['a', 'b', 'c']
// 默认为true 进行展开合并
console.log(arr1.concat(arr2)); // [1, 2, 3, 'a', 'b', 'c']
// 设置arr2 的值为false
arr2[Symbol.isConcatSpreadable] = false
// 再次合并 此时不会展开
console.log(arr1.concat(arr2)); // [1, 2, 3, ['a', 'b', 'c']]

4、iterator

Symbol.iterator 是 Symbol 类型的一个内置静态属性,属性值是一个 Symbol 值,用于定义对象的默认的迭代器@@iterator@@iterator 方法是一个特殊的内部方法,用于返回一个迭代器对象。

​ 当一个对象使用 for...of 循环或扩展运算符 (...)进行遍历时,会调用该对象的 @@iterator 方法来获取一个迭代器对象。迭代器对象可以通过调用其 next() 方法来依次访问对象的每个元素。我们可以通过自定义 @@iterator 方法来实现自定义的迭代器行为。自定义的 @@iterator 方法应该返回一个具有 next() 方法的迭代器对象。每次调用 next() 方法时,迭代器对象应该返回一个包含 valuedone 属性的对象,value 表示当前迭代的值,done 表示迭代是否结束。

案例代码:
let arr3 = [1, 2, 3]
// for..of..形式遍历
console.log('for..of..形式遍历');
for (const item of arr3) {
	console.log(item);
}
// ... 扩展运算符形式遍历
console.log('扩展运算符形式遍历');
console.log([...arr3]);
// 自定义默认迭代器
arr3[Symbol.iterator] = function () {
	let index = 0;
 	return {
       next: function () {
         if (index < arr3.length) {
           return { value: arr3[index++] * 3, done: false };
         } else {
           return { value: undefined, done: true };
         }
       }
	};
};
// 输出数组本身
console.log('输出数组本身');
console.log(arr3);
// for..of..形式遍历
console.log('for..of..形式遍历');
for (const item of arr3) {
	console.log(item);
}
// ... 扩展运算符形式遍历
console.log('扩展运算符形式遍历');
console.log([...arr3]);
// forEach 形式遍历
console.log(' forEach 形式遍历');
arr3.forEach(item => {
	console.log(item);
});
// for 形式遍历
console.log('for 形式遍历');
for (let i = 0; i < arr3.length; i++) {
	console.log(arr3[i]);
}
执行结果:

在这里插入图片描述

5、toPrimitive

Symbol.toPrimitive 是 Symbol 类型的一个内置静态属性,用于定义对象的 @@toPrimitive 转换方法的键。 @@toPrimitive 转换方法是一个内部方法,用于将对象转换为一个原始值,它接受一个参数 hint,表示预期的转换类型。hint 参数可以是以下三个值之一:

  • "default":表示该对象可以被转换为任意类型的原始值,例如:"" + x (强制转为原始值,而不是字符串)。
  • "number":表示该对象被期望转换为数字类型的原始值,例如:算术运算(+-*/)。
  • "string":表示该对象被期望转换为字符串类型的原始值,例如模板字符串(${})、String()等。

​ 参数 hint 的值根据上下文和操作的需要等诸多复杂因素来决定,如:调用对象的运算符、隐式类型转换、valueOftoString 方法等等。

​ 当一个对象被用于需要原始值的上下文中,例如进行算术运算或字符串拼接时,JavaScript 引擎会首先查找对象的 Symbol.toPrimitive 属性。如果该属性存在并且是一个函数,引擎将调用该函数,并传入相应的 hint 参数,转换获取对象的原始值,即字符串、数字或布尔值。我们可以通过自定义 @@toPrimitive 方法来自定义对象的转换行为,完全控制原始转换过程,并返回对象的原始值。

案例代码:
// 一个没有提供 Symbol.toPrimitive 属性的对象
const obj1 = {};
console.log(+obj1); // NaN
console.log(`${obj1}`); // "[object Object]"
console.log(obj1 + ""); // "[object Object]"

// 接下面声明一个对象,手动赋予了 Symbol.toPrimitive 属性
const obj2 = {};
obj2[Symbol.toPrimitive] = function (hint) {
	// hint 参数值是 "number"
 	if (hint === "number") {
      // 返回对象有多少条属性
      return Object.keys(obj2).length;
    }
	// hint 参数值是 "string"
	if (hint === "string") {
		// 返回对象转换成的JSON字符串
		return JSON.stringify(obj2);
	}
	// hint 参数值是 "default"
	return true;
};
console.log(+obj2); // 0  
console.log(`${obj2}`); // "{}"  
console.log(obj2 + ""); // "true"

6、match、replace、search、toStringTag等其他属性

​ 请自行了解。。。

四、常用方法

1、for()

Symbol.for(key)方法会根据参数key,从所有声明的全局symbol数据中寻找对应的值(不包括通过Symbol()创建的数据),如果这个值存在,则返回它;如果不存在,则新建一个以这个keydescription的全局symbol数据,并将创建的数据返回。

​ 如果先使用Symbol()创建了以这个keydescription的数据,然后再使用该方法进行查找,则也不会被查找到,因为该方法只在全局symbol数据中进行查找,而Symbol()创建的是非全局的数据。

// 第一次使用for()方法 由于之前不存在以foo为key的symbol 所以创建一个 symbol 并放入 symbol 注册表中,键为 "foo"
const a = Symbol.for("aaa");
// 第二次使用for()方法 由于之前注册过 所以直接从 symbol 注册表中读取键为"foo"的 symbol
const b = Symbol.for("aaa");
// 验证两者是否为同一个symbol
console.log(a === b); // true

// 如果是通过Symbol()方法创建 则会创建两个key相同的symbol 但并不是同一个symbol
const c = Symbol("bbb");
const d = Symbol("bbb");
// 验证两者是否为同一个symbol
console.log(c === d); // false

2、keyFor()

Symbol.keyFor(symbol)方法会根据参数symbol,从所有声明的全局symbol数据中寻找该数据,并返回该数据的key。如果从全局symbol数据中查找到该 symbol,则返回该 symbolkey 值,返回值为字符串类型;如果不存在该symbol或者该symbol对应的key为空,则返回 undefined

​ 如果参数symbol是通过Symbol()创建的数据,则也不会被查找到,因为该方法创建symbol数据并非全局的,返回值为undefined

// 创建一个全局 Symbol 且有key
const a = Symbol.for("aaa");
console.log(Symbol.keyFor(a)); // "aaa"
// 创建一个全局 Symbol 但没有key
const b = Symbol.for();
console.log(Symbol.keyFor(b)); // undefined
// 创建一个非全局 Symbol 且有key
const c = Symbol("ccc");
// 创建一个全局的 Symbol 且有相同的key
const c2 = Symbol.for("ccc");
console.log(Symbol.keyFor(c)); // undefined
console.log(Symbol.keyFor(c2)); // "ccc"
// 验证两者是否为同一个symbol
console.log(c === c2); // false
// 下面的 原生Symbol 没有保存在全局 Symbol 注册表中
console.log(Symbol.keyFor(Symbol.iterator)); // undefined

3、toString()

Symbol对象拥有自己的toString()方法,覆盖了原型链上的Object.prototype.toString()方法。

​ symbol数据不能隐式转换为字符串,因此需要toString()方法,将数据转换成字符串。

// 创建一个symbol
const a = Symbol("aaa");
console.log(a.toString()); // Symbol(aaa)
// 创建一个全局symbol
const b = Symbol.for("bbb");
console.log(b.toString()); // Symbol(bbb)

4、valueOf()等其他方法

​ 请自行了解。。。

五、相关应用

1、作为对象唯一的属性键

Symbol可以用作对象属性的键,确保属性key的唯一性,避免属性被意外覆盖或冲突。

// 声明一个 symbol 数据
const a = Symbol('aaa');
// 声明一个全局 symbol 数据
const b = Symbol.for('bbb')
// 声明对象 利用 symbol 数据作为 key
const obj = {
    [a]: '11111111',
    a: '22222222',
    [b]: '33333333'
};
// 再次声明一个 symbol 数据 与 a 有相同的description
// 以该symbol作为key 修改数据 虽然声明时的 description 相同 
// 但是是两个不同的 symbol 数据 所以obj[Symbol('aaa')] 与 obj[a] 是两个不同的属性
// 修改其中一个不会影响另一个
obj[Symbol('aaa')] = '44444444';
// 输出属性数据 
console.log(obj[a]); // 11111111
// 此处相当于又声明了一个symbol
console.log(obj[Symbol('aaa')]); // undefined
// 再次输出对象的数据
console.log(obj);

// 可通过 symbol 数据作为 key 修改数据
// obj[a] = '44444444';
执行结果:

在这里插入图片描述

2、声明唯一的常量

​ 借助Symbol数据的唯一性,我们可以声明常量,并确保常量值的唯一性,而且不会被意外修改或覆盖。

// 声明一个常量 且常量值为 symbol 数据
const a = Symbol("aaa");
// 在函数中匹配常量值
function myFunction(value) {
    // 判断传入的值是否与常量值相等
    if (value === a) {
      console.log("常量值被匹配");
    } else {
      console.log("常量值未匹配");
    }
}
// 传入定义的变量
myFunction(a); // 输出 "常量值被匹配"
// 传入一个新的 symbol 数据 且 description 与常量值相同
myFunction(Symbol("aaa")); // 输出 "常量值未匹配"

3、改写对象的内置方法

​ 通过利用Symbol和内置属性,我们可以改写对象的内置方法,以适应特定的业务场景,并自定义对象的行为。但在改写内置方法时不能破坏原有的语言规范和其他代码的预期行为。

// 声明一个对象 并改写其内置toString方法的返回值
const myObj = {
    // 该Symbol的内置属性 决定了toString方法的返回值
    [Symbol.toStringTag]: "MyObject",
};
// 输出对象的toString方法的返回值
console.log(Object.prototype.toString.call(myObj)); // 输出 "[object MyObject]"

4、定义类的私有成员

Symbol可以在类中作为私有成员的标识符。

// 定义一个symbol数据
const _privateMember = Symbol("private");
// 定义一个类
class MyClass {
  constructor() {
      // 定义类的私有成员 以symbol数据作为标识符
    this[_privateMember] = "私有成员";
  }
  // 私有成员的get方法
  getPrivateMember() {
    return this[_privateMember];
  }
}

// new 一个实体对象
const instance = new MyClass();
// 通过get方法正常访问
console.log(instance.getPrivateMember()); // 输出 "私有成员"
// 直接访问 无法访问
console.log(instance[_privateMember]); // 输出 undefined

5、其他用法。。。

六、参考资料

Symbol

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

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

相关文章

大厂面试题之影响项目测试进度的因素有哪些?如何解决?

测试进度&#xff0c;是领导层非常关心的问题&#xff0c;测试同学把控好项目测试进度&#xff0c;必然能让面试官为你加分。 在日常测试过程中&#xff0c;要把控好测试进度&#xff0c;了解影响测试进度的因素是必不可少的&#xff0c;那么&#xff0c;影响项目测试进度的因…

【牛客刷题】bfs和dfs (二叉树层序遍历、矩阵最长递增路径、被围绕的区域)

二叉树层序遍历 vector<vector<int> > levelOrder(TreeNode* root) {// write code herevector<int> res;vector<vector<int>> result;if (root nullptr) return result;queue<TreeNode*> que;que.push(root);while (!que.empty()) {int …

LeetCode刷题笔记【25】:贪心算法专题-3(K次取反后最大化的数组和、加油站、分发糖果)

文章目录 前置知识1005.K次取反后最大化的数组和题目描述分情况讨论贪心算法 134. 加油站题目描述暴力解法贪心算法 135. 分发糖果题目描述暴力解法贪心算法 总结 前置知识 参考前文 参考文章&#xff1a; LeetCode刷题笔记【23】&#xff1a;贪心算法专题-1&#xff08;分发饼…

Java8实战-总结19

Java8实战-总结19 使用流映射对流中每一个元素应用函数流的扁平化 使用流 映射 一个非常常见的数据处理套路就是从某些对象中选择信息。比如在SQL里&#xff0c;你可以从表中选择一列。Stream API也通过map和flatMap方法提供了类似的工具。 对流中每一个元素应用函数 流支持…

回归预测 | MATLAB实现PCA-BP主成分降维结合BP神经网络多输入单输出回归预测

回归预测 | MATLAB实现PCA-BP主成分降维结合BP神经网络多输入单输出回归预测 目录 回归预测 | MATLAB实现PCA-BP主成分降维结合BP神经网络多输入单输出回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 MATLAB实现PCA-BP主成分降维算法结合BP神经网络多输入单输出回…

java八股文面试[设计模式]——行为型模式

目录 策略模式 观察者模式 责任链模式 模板方法模式 状态模式 行为型模式关注的是各个类之间的相互作用&#xff0c;将职责划分清楚&#xff0c;使得我们的代码更加地清晰。 策略模式 策略模式太常用了 下面设计的场景是&#xff0c;我们需要画一个图形&#xff0c;可选…

leetcode872. 叶子相似的树(java)

叶子相似的树 题目描述递归 题目描述 难度 - 简单 leetcode - 872. 叶子相似的树 请考虑一棵二叉树上所有的叶子&#xff0c;这些叶子的值按从左到右的顺序排列形成一个 叶值序列 。 举个例子&#xff0c;如上图所示&#xff0c;给定一棵叶值序列为 (6, 7, 4, 9, 8) 的树。 如果…

cuda显存访问耗时

背景&#xff1a; 项目中有个数据量大小为5195 * 512 * 128float 1.268G的显存&#xff0c;发现有个函数调用很耗时&#xff0c;函数里面就是对这个显存进行128个元素求和&#xff0c;得到一个5195 * 512的图像 分析 1. 为什么耗时 直观上感觉这个流程应该不怎么耗时才对&a…

TDengine 官网换了新“皮肤”,来看看这个风格是不是你的菜

改版升级&#xff0c;不同以“网”&#xff01;为了更好地服务客户&#xff0c;让大家能够更便捷、清晰地了解我们的产品和功能&#xff0c;我们决定给 TDengine 官网换个新“皮肤”~精心筹备下&#xff0c;新官网终于成功与大家见面啦——https://www.taosdata.com/。TDengine…

《向量数据库指南》——AI原生向量数据库Milvus Cloud 2.3稳定性

在当今的互联网时代,稳定性是所有系统和应用程序的关键要素。无论是大型数据中心还是个人电脑,稳定性都是保证正常运行和用户体验的基础。在这个背景下,我们来谈谈 Milvus,一个开源的向量数据库,它在 2.1.0 版本中引入了内存多副本的概念。 Milvus 是一个开源的向量数据库…

9:00面试,9:08就出来了,问的实在有点变态

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到8月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降30%,…

osgEarth::ElevationQuery的setMaxTilesToCache函数的作用

【引子】 osgEarth::ElevationQuery类用于查询高程&#xff0c;自测效率比较低&#xff08;大概每个点需要四五十毫秒&#xff09;&#xff0c;成员函数setMaxTilesToCache(int)&#xff0c;之前看说明仍然不是很确认其具体的意义&#xff0c;想尝试设置是否能加速下。 看之前的…

MATLAB实现TopSis优劣解距离法——分析《世界征服者3》将领排名

问题背景 世界征服者3游戏中有150的将领角色&#xff0c;每个将领都有自己的兵种优势、军阶、技能等不同的属性&#xff0c;如何教务客观、综合全面地选拔出其中排名前50的将领&#xff1f;基于TOPSIS优劣解距离法以及聚类算法&#xff0c;给出大家较为客观的排名。 一.问题描…

使用树莓派搭建个人网站,并发布到外网可访问:实用步骤解析

文章目录 前言使用 Raspberry Pi Imager 安装 Raspberry Pi OS测试 web 站点安装静态样例站点 将web站点发布到公网安装 Cpolarcpolar进行token认证生成cpolar随机域名网址生成cpolar二级子域名将参数保存到cpolar配置文件中测试修改后配置文件配置cpolar服务开机自启动 前言 …

Day_81-87 CNN卷积神经网络

目录 一. CNN卷积神经网络与传统神经网络的不同 1. 模型图 2. 参数分布情况 3. 卷积神经网络和传统神经网络的层次结构 4. 传统神经网络的缺点&#xff1a; 二. CNN的基本操作 1. 卷积 2. 池化 三. CNN实现过程 1. 算法流程图 2. 输入层 3. 卷积层 4. 激活层 5. 池化层 6. 全连…

LINE自动回复:快速回复提升客服效率

2023年&#xff0c;LINE在其4个主要市场&#xff1a;对话、日本、台湾和泰国拥有约1.78亿月活跃用户。 LINE不仅是一个通讯软件&#xff0c;更提供广泛的服务&#xff0c;包括语音和视讯通话、群组、发布社交帖子及商务功能。近年来&#xff0c;越来越多的企业在客户服务中使用…

搭建花店小程序商城的详细步骤

首先&#xff0c;你需要找一个专业成熟的小程序商城制作平台。一个优秀的小程序商城制作平台应该具备丰富的行业模板、简便的设计工具和强大的功能模块。在这里&#xff0c;我们推荐乔拓云平台&#xff0c;其后台管理页面友好&#xff0c;设计搭建页面功能齐全&#xff0c;且针…

画流程图大家都用哪些工具?

流程图工具是一个用于创建图表和图形功能的应用程序。这些流程图工具允许团队成员在绘图软件上合作&#xff0c;制作的流程图可以提供清晰的视觉效果、即时沟通、有效的协调和有效的分析&#xff0c;从而提高设计的工作效率。下面将推荐6个流程图工具&#xff0c;我相信总有一个…

C高级第2天

写一个1.sh脚本&#xff0c;将以下内容放到脚本中&#xff1a; 在家目录下创建目录文件&#xff0c;dir 在dir下创建dir1和dir2 把当前目录下的所有文件拷贝到dir1中&#xff0c; 把当前目录下的所有脚本文件拷贝到dir2中 把dir2打包并压缩为dir2.tar.xz 再把dir2.tar.xz…

Vue的props配置项

简介&#xff1a;Vue中的组件模板是可以复用的&#xff0c;但是模板中的数据是可以改变的。props配置项用于解决组件迁移复用时接受和保存传递给组件的数据的问题。 1.如何给组件传递数据&#xff1f; 答&#xff1a;按照key:value的形式进行传递。 2.如何保存传递给组件的数…