js基本数据新增的Symbol到底是啥呢?

在 JavaScript 中,数据类型可分为基本类型和引用类型。基本类型包括 undefinednullbooleannumberbigintstring,而引用类型包含对象、数组、函数等。随着 JavaScript 的发展,引入了新的基本数据类型------SymbolSymbol 最早在 ECMAScript 6(ES6)中引入,目的是为了解决对象属性命名冲突和更精细的内存控制。由于其独特的属性和应用场景,Symbol 被广泛应用于开发中,尤其是在实现隐式接口、避免命名冲突和实现私有属性等方面。

1. 基本概念

Symbol 是一种新的原始数据类型,表示独一无二的值。每个 Symbol 值都是唯一的,且不可变,即使它们的描述字符串相同,它们的值也不相等。

创建一个 Symbol

js 复制代码
let sym1 = Symbol();    // 创建一个新的 Symbol
let sym2 = Symbol();    // 每次调用 Symbol() 都会得到一个唯一的 Symbol

2. Symbol 的特点

1) 唯一性

Symbol 的最大特点就是它是唯一的,即使描述相同,每次通过 Symbol() 创建的值都是不同的。

js 复制代码
let sym1 = Symbol('foo');
let sym2 = Symbol('foo');
console.log(sym1 === sym2);  // false

2) 不可变性

一旦创建了 Symbol,它的值就无法更改。与其他原始类型(如数字、字符串)类似,Symbol 是不可变的。

js 复制代码
let sym = Symbol('foo');
sym = Symbol('bar');  // 重新赋值是创建了一个新的 Symbol,而不是修改原来的 Symbol

3) 不可转换为基本类型

Symbol 不能直接转换为字符串、数字或布尔值。在需要进行字符串拼接时,可以使用 String().toString() 方法将 Symbol 转换为字符串。

js 复制代码
let sym = Symbol('example');
console.log(String(sym));    // "Symbol(example)"
console.log(sym.toString()); // "Symbol(example)"

但直接使用 Symbol 作为字符串或数字会抛出错误:

js 复制代码
let sym = Symbol('test');
console.log(sym + '');  // TypeError: Cannot convert a Symbol value to a string

3. Symbol 用法

1) 作为对象的属性名

Symbol 通常用于对象的属性名,以确保该属性的唯一性。这样做可以避免属性名冲突,尤其是在大型应用程序中,或者在多方开发的情况下。

js 复制代码
let sym1 = Symbol('name');
let obj = {
  [sym1]: 'John'
};
console.log(obj[sym1]);  // "John"

2) Symbol.for() 和 Symbol.keyFor()

Symbol.for() 允许在全局范围内注册和共享 Symbol。如果已经存在相同的 Symbol,它将返回现有的 Symbol,否则会创建一个新的 Symbol 并将其注册。Symbol.keyFor() 用于查找全局注册的 Symbol

js 复制代码
let sym1 = Symbol.for('app');
let sym2 = Symbol.for('app');
console.log(sym1 === sym2);  // true,Symbol.for 返回的是相同的 Symbol
let sym3 = Symbol('bar');
console.log(Symbol.keyFor(sym3));  // undefined,因为 sym3 是局部 Symbol

3) Symbol 作为常量

Symbol 还可以用于定义常量,特别是用作事件类型、状态等标识符。这可以防止常量值冲突,提高代码的可维护性。

js 复制代码
const EVENT_CLICK = Symbol('click');
const EVENT_HOVER = Symbol('hover');
function handleEvent(event) {
  switch (event) {
    case EVENT_CLICK:
      console.log('Click event');
      break;
    case EVENT_HOVER:
      console.log('Hover event');
      break;
    default:
      console.log('Unknown event');
  }
}
handleEvent(EVENT_CLICK);  // Click event
handleEvent(EVENT_HOVER);  // Hover event

4. 内置的 Symbol 常量

JavaScript 还定义了一些内置的 Symbol 常量,用于实现一些特定的行为。这些常量可以直接通过 Symbol 对象访问。

1) Symbol.iterator

Symbol.iterator 是所有可迭代对象(如数组、字符串、Map、Set)都必须实现的一个 Symbol 属性,用来定义如何通过 for...of 进行迭代。

js 复制代码
let arr = [1, 2, 3];
let iterator = arr[Symbol.iterator]();
console.log(iterator.next());  // { value: 1, done: false }
console.log(iterator.next());  // { value: 2, done: false }
console.log(iterator.next());  // { value: 3, done: false }
console.log(iterator.next());  // { value: undefined, done: true }

2) Symbol.toPrimitive

Symbol.toPrimitive 允许对象定义如何转换为原始值。可以通过自定义此方法来定义对象如何被转换成字符串、数字或布尔值。

js 复制代码
let obj = {
  [Symbol.toPrimitive](hint) {
    if (hint === 'string') {
      return 'Custom string';
    }
    return 42;
  }
};
console.log(String(obj));  // "Custom string"
console.log(Number(obj));  // 42

3) Symbol.hasInstance

Symbol.hasInstance 用于定义一个对象的 instanceof 检查行为。通过此方法可以自定义一个对象的 instanceof 判断逻辑。

js 复制代码
class MyClass {
  static [Symbol.hasInstance](obj) {
    return obj.hasOwnProperty('myProp');
  }
}
let obj = { myProp: true };
console.log(obj instanceof MyClass);  // true

4) Symbol.isConcatSpreadable

Symbol.isConcatSpreadable 是一个用于控制 Array.prototype.concat() 方法行为的 Symbol。默认情况下,concat 会将数组中的元素合并,但如果某个对象有这个 Symbol 属性,并设置为 false,则 concat 时不会将该对象展开。

js 复制代码
let arr1 = [1, 2];
let arr2 = [3, 4];
let obj = {
  0: 5,
  1: 6,
  length: 2,
  [Symbol.isConcatSpreadable]: false
};

let result = arr1.concat(arr2, obj);
console.log(result);  // [1, 2, 3, 4, { 0: 5, 1: 6, length: 2 }]

5) Symbol.toStringTag

Symbol.toStringTag 用于定义对象的默认字符串表示(Object.prototype.toString() 的返回值)。

js 复制代码
let obj = {
  [Symbol.toStringTag]: 'MyObject'
};
console.log(Object.prototype.toString.call(obj));  // "[object MyObject]"

5. 应用场景

  • 避免命名冲突: 在对象字面量中,Symbol 可以避免不同模块间属性名的冲突。特别是在大型应用程序中,多个模块可能会定义类似的属性或方法,使用 Symbol 可以确保这些属性在全局范围内是唯一的。

  • 模拟私有属性: Symbol 可以用来模拟私有属性,避免外部访问和修改。尽管 JavaScript 没有原生的私有字段,但使用 Symbol 可以提供一种更安全的方式。

  • 作为对象属性的唯一标识符: 在某些情况下,你可能需要一个唯一的标识符来区分对象的不同属性,这时 Symbol 是一个很好的选择。

  • 作为 Map 的键 :由于 Symbol 的唯一性,它非常适合作为 Map 对象的键,因为 Map 对象可以存储多个具有相同值的键。

6. 总结

  • 唯一性 :每个 Symbol 都是唯一的,即使描述相同。
  • 不可变性Symbol 一旦创建,无法修改。
  • 作为对象属性名:用于确保属性名的唯一性,避免冲突。
  • 内置常量Symbol 提供了一些内置的常量,帮助实现特殊功能,例如 Symbol.iteratorSymbol.toPrimitive 等。
  • 全局共享Symbol.for()Symbol.keyFor() 允许跨多个脚本共享 Symbol
  • 适用场景**Symbol 适用于模拟私有属性、避免命名冲突、实现自定义迭代器等场景。
相关推荐
炒毛豆13 分钟前
vue3+echarts+ant design vue实现进度环形图
javascript·vue.js·echarts
AlgorithmAce2 小时前
Live2D嵌入前端页面
前端
nameofworld2 小时前
前端面试笔试(六)
前端·javascript·面试·学习方法·递归回溯
流着口水看上帝3 小时前
JavaScript完整原型链
开发语言·javascript·原型模式
guokanglun3 小时前
JavaScript数据类型判断之Object.prototype.toString.call() 的详解
开发语言·javascript·原型模式
GISer_Jing3 小时前
从0开始分享一个React项目:React-ant-admin
前端·react.js·前端框架
川石教育3 小时前
Vue前端开发子组件向父组件传参
前端·vue.js·前端开发·vue前端开发·vue组件传参
Embrace9243 小时前
为什么 Vue2会出现数据更新视图不更新 Vue3不会出现
javascript·vue.js·ecmascript
qq_415628173 小时前
bpmn.js显示流程图
javascript·vue.js·流程图