阿里云主机折上折
  • 微信号
Current Site:Index > Introduction to commonly used built-in Symbols

Introduction to commonly used built-in Symbols

Author:Chuan Chen 阅读数:18505人阅读 分类: JavaScript

ECMAScript 6 introduced the Symbol primitive type, along with a series of built-in Symbol values (known as "Well-known Symbols") to extend object behavior. These built-in Symbols serve as keys for object properties, allowing control over how objects behave in specific operations, such as iteration, type conversion, and more.

Symbol.iterator

Symbol.iterator defines the default iterator for an object, used in scenarios like for...of loops or the spread operator. Objects that implement this method are called iterable objects.

const myIterable = {
  *[Symbol.iterator]() {
    yield 1;
    yield 2;
    yield 3;
  }
};

console.log([...myIterable]); // [1, 2, 3]
for (const value of myIterable) {
  console.log(value); // Outputs 1, 2, 3 sequentially
}

Built-in types like Array, String, Map, and Set already implement Symbol.iterator by default. Custom classes can also make their instances iterable by implementing this method:

class Range {
  constructor(start, end) {
    this.start = start;
    this.end = end;
  }

  *[Symbol.iterator]() {
    for (let i = this.start; i <= this.end; i++) {
      yield i;
    }
  }
}

const range = new Range(1, 5);
console.log([...range]); // [1, 2, 3, 4, 5]

Symbol.toStringTag

Symbol.toStringTag customizes the string tag returned when Object.prototype.toString() is called on an object. By default, plain objects return [object Object], but this property allows modifying that behavior.

const myObj = {
  [Symbol.toStringTag]: 'MyCustomObject'
};

console.log(Object.prototype.toString.call(myObj)); // "[object MyCustomObject]"

Built-in types use this feature to return more meaningful tags:

console.log(Object.prototype.toString.call(new Map())); // "[object Map]"
console.log(Object.prototype.toString.call(new Set())); // "[object Set]"

Symbol.hasInstance

Symbol.hasInstance customizes the behavior of the instanceof operator. When executing obj instanceof Constructor, it actually calls Constructor[Symbol.hasInstance](obj).

class MyArray {
  static [Symbol.hasInstance](instance) {
    return Array.isArray(instance);
  }
}

console.log([] instanceof MyArray); // true
console.log({} instanceof MyArray); // false

Symbol.species

Symbol.species specifies the constructor for derived objects. When creating derived objects (e.g., when Array.prototype.map() returns a new array), it uses the constructor specified by this property instead of the default one.

class MyArray extends Array {
  static get [Symbol.species]() {
    return Array; // Overrides the default MyArray return
  }
}

const myArr = new MyArray(1, 2, 3);
const mapped = myArr.map(x => x * 2);

console.log(mapped instanceof MyArray); // false
console.log(mapped instanceof Array);   // true

Symbol.toPrimitive

Symbol.toPrimitive allows objects to customize type conversion behavior. When an object needs to be converted to a primitive value (e.g., via the + operator or String() function), this method is called.

const temperature = {
  value: 25,
  unit: '°C',
  [Symbol.toPrimitive](hint) {
    switch (hint) {
      case 'string':
        return `${this.value}${this.unit}`;
      case 'number':
        return this.value;
      default:
        return this.value.toString();
    }
  }
};

console.log(String(temperature)); // "25°C"
console.log(+temperature);        // 25
console.log(temperature + 5);     // 30

Symbol.isConcatSpreadable

Symbol.isConcatSpreadable determines whether an array or array-like object spreads its elements in Array.prototype.concat(). By default, arrays spread, while array-like objects do not.

const arr1 = [1, 2];
const arr2 = [3, 4];
arr2[Symbol.isConcatSpreadable] = false;

console.log(arr1.concat(arr2)); // [1, 2, [3, 4]]

const arrayLike = { 0: 'a', 1: 'b', length: 2 };
arrayLike[Symbol.isConcatSpreadable] = true;
console.log(['x'].concat(arrayLike)); // ['x', 'a', 'b']

Symbol.match

Symbol.match allows objects to customize the behavior of String.prototype.match(). Regular expressions implement this method by default.

const customMatcher = {
  [Symbol.match](string) {
    return string.includes('hello') ? ['hello'] : null;
  }
};

console.log('world hello'.match(customMatcher)); // ["hello"]
console.log('world'.match(customMatcher));      // null

Symbol.replace

Symbol.replace defines the behavior of String.prototype.replace(). It takes the original string and the replacement value as arguments.

const customReplacer = {
  [Symbol.replace](string, replacement) {
    return string.split(' ').join(replacement);
  }
};

console.log('a b c'.replace(customReplacer, '-')); // "a-b-c"

Symbol.search

Symbol.search customizes the String.prototype.search() method. It returns the index of the match.

const customSearcher = {
  [Symbol.search](string) {
    return string.indexOf('ECMAScript');
  }
};

console.log('Learn ECMAScript 6'.search(customSearcher)); // 6

Symbol.split

Symbol.split controls the splitting logic of String.prototype.split().

const customSplitter = {
  [Symbol.split](string) {
    return string.toUpperCase().split('');
  }
};

console.log('hello'.split(customSplitter)); // ["H", "E", "L", "L", "O"]

Symbol.unscopables

Symbol.unscopables excludes certain properties from being bound by the with statement. Although with is deprecated in modern JavaScript, this feature remains.

const myObject = {
  foo: 1,
  bar: 2,
  [Symbol.unscopables]: {
    foo: true // Excludes the foo property
  }
};

with (myObject) {
  console.log(bar); // 2
  console.log(foo); // ReferenceError: foo is not defined
}

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.