Symbol

[up] [prev] [next]

目次

シンボル

シンボル(Symbol)は、ES2015(ES6) で追加された新たなプリミティブ型です。

Symbol([description])

シンボルを作成します。description にはデバッグの際にシンボルを見分けるための文字列を指定します。new は使用しません。

JavaScript
const sym1 = Symbol();
const sym2 = Symbol("foo");

description に同じ名前を指定しても、Symbol() は毎回新しいオブジェクトを生成します。下記の実行結果は false となります。

JavaScript
const sym1 = Symbol("foo");
const sym2 = Symbol("foo");
console.log(sym1 === sym2);     // => false

シンボルは辞書のキーとして使用することができます。

JavaScript
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() メソッドを追加する場合など、メソッドをシンボルで識別することにより、メソッド名の重複を気にすることなく、拡張が可能となります。

JavaScript (hello1.js)
const hello = Symbol("hello");
String.prototype[hello] = function() { console.log("Hello " + this); }
export { hello as default };
JavaScript (hello2.js)
const hello = Symbol("hello");
String.prototype[hello] = function() { console.log("Hello " + this + "!!!"); }
export { hello as default };
JavaScript (sample.js)
import hello1 from "./hello1.js";
import hello2 from "./hello2.js";
"Tanaka"[hello1]();    // => "Hello Tanaka"
"Tanaka"[hello2]();    // => "Hello Tanaka!!!"
HTML
<script type="module" src="sample.js"></script>

プロパティ

Symbol.length

常に 0 を返します。

JavaScript
console.log(Symbol.length);     // => 0
Symbol.prototype

シンボルのプロトタイプを返します。

symbol.description

シンボルの description を取得します。ES2019(ES10) で追加されました。

JavaScript
const sym1 = Symbol("foo");
console.log(sym1.description);     // => "foo"

メソッド

Symbol.for(key)

key で識別されるグローバルシンボルを生成します。key に対応するグローバルシンボルがすでに存在する場合はそれを返します。下記の例は true となります。

JavaScript
const sym1 = Symbol.for("foo");
const sym2 = Symbol.for("foo");
console.log(sym1 === sym2);     // => true
Symbol.keyFor(sym)

グローバルシンボル sym を引数とし、グローバルシンボルのキーを取得します。

JavaScript
const sym1 = Symbol.for("foo");
console.log(Symbol.keyFor(sym1));     // => "foo"

Well-knownシンボル

Symbol.iterator

for ... of ... で参照されるイテレータメソッドを指定するシンボルです。

JavaScript
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"
}
Symbol.asyncIterator

for await of で呼び出される非同期のイテレータメソッドを指定します。

Symbol.match

string.startsWith(), string.endsWith(), string.includes() メソッドは、引数が正規表現の場合に TypeError を返しますが、Symbol.match が flase に設定されたオブジェクトは、正規表現とはみなされなくなります。

JavaScript
var re1 = /foo/;
var re2 = /baa/;
re1[Symbol.match] = false;
console.log("/foo/".startsWith(re1));   // => true
console.log("/baa/".startsWith(re2));   // => TypeError
Symbol.replace

string.replace() から呼び出されるリプレースメソッドを指定します。

JavaScript
var upper = {};
upper[Symbol.replace] = (str) => str.toUpperCase();
console.log('xyz'.replace(upper));

string.search() から呼び出されるサーチメソッドを指定します。

JavaScript
var mysearch = {searchStr: "Y"};
mysearch[Symbol.search] = (target) => target.search(this.searchStr);
console.log("XYZ".search(mysearch));   // => 1
Symbol.split

string.split() から呼び出されるスプリットメソッドを指定します。

JavaScript
var mysplit = {delimiter: "/"};
mysplit[Symbol.split] = (target) => target.split(this.delimiter);
console.log("2019/12/01".split(mysplit));   // => ["2019", "12", "01]
Symbol.hasInstance

instanceof から呼び出される判定メソッドを指定します。

JavaScript
class MyArray {
    static [Symbol.hasInstance](instance) {
        return Array.isArray(instance);
    }
}
console.log([] instanceof MyArray);     // => true
Symbol.isConcatSpreadable

array.concat() において、オブジェクトが concat() のための平坦化が可能か否かを示します。

JavaScript
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]]
Symbol.unscopables

with において、オブジェクトのプロパティがスコープ対象になるか否かを指定します。下記の場合、foo は対象、baa は非対象となります。

JavaScript
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

オブジェクトのデフォルトコンストラクタを指定します。下記の例で、Symbol.species を指定しない場合は、.map() メソッドはデフォルトコンストラクタである MyClass オブジェクトを返却しますが、species を Array のコンストラクタで上書きすることにより、MyArray オブジェクトではなく、Array オブジェクトを返却するようになります。

JavaScript
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)
Symbol.toPrimitive

オブジェクトをプリミティブ値に変換するための変換メソッドを指定します。hint には、"default", "string", "number" いずれかの値が入ります。

JavaScript
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"
Symbol.toStringTag

Object.prototype.toString() で参照されるオブジェクトの説明文字列を指定します。

JavaScript
class MyClass {
    get [Symbol.toStringTag]() {
        return "MyClass";
    }
}
var obj = new MyClass();
console.log(Object.prototype.toString.call(obj));    // => "[object MyClass]"

[up] [prev] [next]
Copyright (C) 2019 杜甫々
改訂版初版:2019年12月1日、最終更新:2019年12月1日
http://www.tohoho-web.com/js/symbol.htm