シンボル(Symbol)は、ES2015(ES6) で追加された新たなプリミティブ型です。
シンボルを作成します。description にはデバッグの際にシンボルを見分けるための文字列を指定します。new は使用しません。
const sym1 = Symbol(); const sym2 = Symbol("foo");
description に同じ名前を指定しても、Symbol() は毎回新しいオブジェクトを生成します。下記の実行結果は false となります。
const sym1 = Symbol("foo");
const sym2 = Symbol("foo");
console.log(sym1 === sym2); // => false
シンボルは辞書のキーとして使用することができます。
const sym1 = Symbol("foo");
const sym2 = Symbol("baa");
var obj = {
[sym1]: "Yamada",
[sym2]: function() {
console.log(this[sym1]);
}
}
obj[sym2](); // => "Yamada"
シンボルは互換性を維持したまま、オブジェクトに新たな機能やプロパティを追加するために考案されました。例えば、ES2015(ES6) で追加された for ... of ... 構文は、オブジェクトのイテレータメソッドを呼び出します。イテレータメソッドを iterator() としてしまうと、すでに iterator() という名前のメソッドを独自実装しているプログラムに影響を与えてしまいます。PHP では、__ で始まるメソッドは将来の言語拡張で利用される可能性があると明示しているので、__iterator() とすればよいのですが、JavaScript ではそのようなルールを決めていなかったため、シンボルを用いて [Symbol.iterator]() とあらわすようにしました。
JavaScript の仕様追加によって作成されたシンボルは 「Well-knownシンボル」 として定義されます。
自己開発しているオブジェクトでシンボルを利用するケースはほとんど無いのですが、下記の様に、標準オブジェクト String に hello() メソッドを追加する場合など、メソッドをシンボルで識別することにより、メソッド名の重複を気にすることなく、拡張が可能となります。
const hello = Symbol("hello"); String.prototype[hello] = function() { console.log("Hello " + this); } export { hello as default };
const hello = Symbol("hello"); String.prototype[hello] = function() { console.log("Hello " + this + "!!!"); } export { hello as default };
import hello1 from "./hello1.js"; import hello2 from "./hello2.js"; "Tanaka"[hello1](); // => "Hello Tanaka" "Tanaka"[hello2](); // => "Hello Tanaka!!!"
<script type="module" src="sample.js"></script>
常に 0 を返します。
console.log(Symbol.length); // => 0
シンボルのプロトタイプを返します。
シンボルの description を取得します。ES2019(ES10) で追加されました。
const sym1 = Symbol("foo");
console.log(sym1.description); // => "foo"
key で識別されるグローバルシンボルを生成します。key に対応するグローバルシンボルがすでに存在する場合はそれを返します。下記の例は true となります。
const sym1 = Symbol.for("foo");
const sym2 = Symbol.for("foo");
console.log(sym1 === sym2); // => true
グローバルシンボル sym を引数とし、グローバルシンボルのキーを取得します。
const sym1 = Symbol.for("foo");
console.log(Symbol.keyFor(sym1)); // => "foo"
for ... of ... で参照されるイテレータメソッドを指定するシンボルです。
class MyClass {
constructor(name) {
this.name = name;
}
*[Symbol.iterator]() {
for (var i = 0; i < this.name.length; i++) {
yield this.name.charAt(i);
}
}
}
var obj = new MyClass("Yamada");
for (o of obj) {
console.log(o); // => "Y", "a", "m", "a", "d", "a"
}
for await of で呼び出される非同期のイテレータメソッドを指定します。
string.startsWith(), string.endsWith(), string.includes() メソッドは、引数が正規表現の場合に TypeError を返しますが、Symbol.match が flase に設定されたオブジェクトは、正規表現とはみなされなくなります。
var re1 = /foo/; var re2 = /baa/; re1[Symbol.match] = false; console.log("/foo/".startsWith(re1)); // => true console.log("/baa/".startsWith(re2)); // => TypeError
string.replace() から呼び出されるリプレースメソッドを指定します。
var upper = {}; upper[Symbol.replace] = (str) => str.toUpperCase(); console.log('xyz'.replace(upper));
string.search() から呼び出されるサーチメソッドを指定します。
var mysearch = {searchStr: "Y"};
mysearch[Symbol.search] = (target) => target.search(this.searchStr);
console.log("XYZ".search(mysearch)); // => 1
string.split() から呼び出されるスプリットメソッドを指定します。
var mysplit = {delimiter: "/"};
mysplit[Symbol.split] = (target) => target.split(this.delimiter);
console.log("2019/12/01".split(mysplit)); // => ["2019", "12", "01]
instanceof から呼び出される判定メソッドを指定します。
class MyArray {
static [Symbol.hasInstance](instance) {
return Array.isArray(instance);
}
}
console.log([] instanceof MyArray); // => true
array.concat() において、オブジェクトが concat() のための平坦化が可能か否かを示します。
var arr1 = ["A", "B", "C"]; var arr2 = [3, 4, 5] console.log(arr1.concat(arr2)); // => ["A", "B", "C", 3, 4, 5] arr2[Symbol.isConcatSpreadable] = false; console.log(arr1.concat(arr2)); // => ["A", "B", "C", [3, 4, 5]]
with において、オブジェクトのプロパティがスコープ対象になるか否かを指定します。下記の場合、foo は対象、baa は非対象となります。
var obj = {foo: 1, baa: 2}; obj[Symbol.unscopables] = {foo: false, baa: true}; with (obj) { console.log(foo); // => 1 console.log(baa); // => ReferenceError }
オブジェクトのデフォルトコンストラクタを指定します。下記の例で、Symbol.species を指定しない場合は、.map() メソッドはデフォルトコンストラクタである MyClass オブジェクトを返却しますが、species を Array のコンストラクタで上書きすることにより、MyArray オブジェクトではなく、Array オブジェクトを返却するようになります。
class MyArray extends Array { static get [Symbol.species]() { return Array; } } let arr1 = new MyArray(1, 2, 3); let arr2 = arr1.map((x) => x * 2); console.log(arr1); // => MyArray(1, 2, 3) console.log(arr2); // => Array(1, 2, 3)
オブジェクトをプリミティブ値に変換するための変換メソッドを指定します。hint には、"default", "string", "number" いずれかの値が入ります。
class MyClass { constructor(str) { this.value = str; } [Symbol.toPrimitive](hint) { if (hint == "string") { return String(this.value); } else if (hint == "number") { return Number(this.value); } else { return this.value; } } } var obj = new MyClass("123"); console.log("" + obj); // => hint == "default" console.log(`${obj}`); // => hint == "string" console.log(+obj); // => hint == "number"
Object.prototype.toString() で参照されるオブジェクトの説明文字列を指定します。
class MyClass {
get [Symbol.toStringTag]() {
return "MyClass";
}
}
var obj = new MyClass();
console.log(Object.prototype.toString.call(obj)); // => "[object MyClass]"