Symbol内置属性概述
Symbol作为JavaScript的第七种原始数据类型,除了可以通过Symbol()函数创建自定义符号外,还内置了一系列具有特殊用途的属性。这些内置属性通常以Symbol.xxx的形式存在,每一个都对应着ECMAScript规范中定义的对象内部行为,通过重写这些属性对应的方法,可以定制对象的默认行为。

常见的Symbol内置属性分类
Symbol的内置属性按照用途可以分为迭代相关、类型转换相关、对象行为定制相关等几大类,下面我们逐一介绍这些属性的高级用法。
迭代相关的Symbol内置属性
Symbol.iterator
Symbol.iterator是最常用的内置属性之一,它定义了对象的默认迭代器。当一个对象需要被for...of循环遍历时,会自动调用该对象的Symbol.iterator方法,返回一个迭代器对象。
我们可以通过重写对象的Symbol.iterator属性,让普通对象支持for...of遍历:
// 定义一个自定义的可迭代对象
const customIterable = {
data: [1, 2, 3, 4],
// 重写Symbol.iterator属性
[Symbol.iterator]: function() {
let index = 0;
const self = this;
// 返回迭代器对象,包含next方法
return {
next: function() {
if (index < self.data.length) {
return { value: self.data[index++], done: false };
} else {
return { value: undefined, done: true };
}
}
};
}
};
// 使用for...of遍历自定义对象
for (const item of customIterable) {
console.log(item); // 依次输出1,2,3,4
}
Symbol.asyncIterator
Symbol.asyncIterator用于定义对象的异步迭代器,支持for await...of语法,常用于异步数据流的遍历场景,比如异步生成器函数返回的对象。
// 定义异步可迭代对象
const asyncIterable = {
data: [1, 2, 3],
[Symbol.asyncIterator]: async function* () {
for (const item of this.data) {
// 模拟异步操作
await new Promise(resolve => setTimeout(resolve, 100));
yield item;
}
}
};
// 使用for await...of遍历异步对象
(async () => {
for await (const item of asyncIterable) {
console.log(item); // 每隔100ms依次输出1,2,3
}
})();
类型转换相关的Symbol内置属性
Symbol.toPrimitive
Symbol.toPrimitive用于定制对象在转换为原始值时的行为,当对象参与运算、比较或者需要转换为原始值时,会调用该属性对应的方法。方法接收一个hint参数,提示需要转换的目标类型,可选值为number、string、default。
const obj = {
value: 10,
[Symbol.toPrimitive](hint) {
if (hint === 'number') {
return this.value;
}
if (hint === 'string') {
return `obj_value_${this.value}`;
}
// default场景通常用于运算,默认返回数字
return this.value;
}
};
console.log(obj + 5); // 15,hint为default,返回10+5
console.log(String(obj)); // "obj_value_10",hint为string
console.log(Number(obj)); // 10,hint为number
Symbol.toStringTag
Symbol.toStringTag用于定制Object.prototype.toString.call()方法返回的对象类型标签,默认情况下普通对象调用该方法会返回[object Object],通过重写该属性可以修改返回的类型标识。
class CustomClass {
get [Symbol.toStringTag]() {
return 'CustomClass';
}
}
const instance = new CustomClass();
console.log(Object.prototype.toString.call(instance)); // [object CustomClass]
对象行为定制相关的Symbol内置属性
Symbol.hasInstance
Symbol.hasInstance用于定制instanceof运算符的行为,默认情况下instanceof会检查对象的原型链,重写该属性可以改变instanceof的判断逻辑。
class EvenNumber {
static [Symbol.hasInstance](obj) {
// 判断传入的值是否为偶数
return typeof obj === 'number' && obj % 2 === 0;
}
}
console.log(2 instanceof EvenNumber); // true
console.log(3 instanceof EvenNumber); // false
console.log(4 instanceof EvenNumber); // true
Symbol.isConcatSpreadable
Symbol.isConcatSpreadable用于定制数组或类数组对象在调用concat方法时是否展开,默认情况下数组会被展开,设置为false可以阻止展开。
const arr1 = [1, 2]; const arr2 = [3, 4]; arr2[Symbol.isConcatSpreadable] = false; const result = arr1.concat(arr2); console.log(result); // [1, 2, [3, 4]],arr2没有被展开
Symbol.species
Symbol.species用于定制派生对象时使用的构造函数,比如数组的map、filter等方法返回新数组时,默认会使用原数组的构造函数,通过重写该属性可以指定返回对象的构造函数。
class MyArray extends Array {
static get [Symbol.species]() {
// 指定派生对象使用普通Array构造函数
return Array;
}
}
const myArr = new MyArray(1, 2, 3);
const mappedArr = myArr.map(item => item * 2);
console.log(mappedArr instanceof MyArray); // false
console.log(mappedArr instanceof Array); // true
使用Symbol内置属性的注意事项
- Symbol内置属性都是不可枚举的,无法通过for...in或者Object.keys()获取到
- 内置Symbol属性的值是只读的,不能修改其指向,但是可以修改对象上该属性对应的方法
- 使用内置Symbol属性定制对象行为时,需要遵循ECMAScript规范的要求,避免实现不符合预期的逻辑
- 不要随意重写内置对象的Symbol属性,比如Array.prototype的Symbol.iterator,可能会导致全局代码出现异常
通过合理运用Symbol的内置属性,我们可以实现很多常规方式难以完成的功能,比如自定义迭代逻辑、修改对象的默认类型转换行为等,在框架开发、工具库编写等场景中非常实用。掌握这些高级用法,能够帮助我们写出更灵活、更符合需求的JavaScript代码。
SymbolJavaScript内置属性符号类型修改时间:2026-07-03 05:06:27