記号

概要

ES5 オブジェクトのプロパティ名はすべて文字列であるため、プロパティ名の競合が簡単に発生する可能性があります。たとえば、他の人が提供したオブジェクトを使用しているが、このオブジェクトに新しいメソッドを追加する場合 (ミックスイン モード)、新しいメソッドの名前が既存のメソッドと競合する可能性があります。各属性の名前が一意であることを保証し、属性名の競合を根本的に防ぐメカニズムがあれば素晴らしいと思います。これが、ES6 で「Symbol」が導入された理由です。

ES6 では、一意の値を表すための新しいプリミティブ データ型 Symbol が導入されています。これは JavaScript 言語のネイティブ データ型の 1 つであり、他のデータ型には次のものがあります: unknownnull、Boolean、String、Number、BigInt、Object 。

シンボル値は Symbol() 関数を通じて生成されます。これは、オブジェクトの属性名が 2 つのタイプを持つことができることを意味します。1 つは元の文字列で、もう 1 つは新しいシンボル タイプです。シンボル タイプに属するすべての属性名は一意であり、他の属性名と競合しないことが保証されます。

let s = Symbol();

種類
// 「シンボル」

上記のコードでは、変数 s は一意の値です。 typeof 演算子の結果は、変数 s が文字列などの別の型ではなく、Symbol データ型であることを示します。

「new」コマンドは「Symbol()」関数の前に使用できないことに注意してください。使用しないとエラーが報告されます。これは、生成されたシンボルがオブジェクトではなくプリミティブ型の値であるため、「new」コマンドを使用して呼び出すことができないためです。また、Symbol値はオブジェクトではないため、プロパティを追加することはできません。基本的には文字列のようなデータ型です。

Symbol() 関数は、Symbol インスタンスの説明を表す文字列をパラメータとして受け入れることができます。これは主に、コンソールに表示するときや文字列に変換するときに区別しやすくするためです。

s1 = Symbol('foo'); とします。
s2 = Symbol('bar'); とします。

s1 // シンボル(foo)
s2 // シンボル(バー)

s1.toString() // "シンボル(foo)"
s2.toString() // "シンボル(バー)"

上記のコードでは、s1s2 は 2 つの Symbol 値です。パラメーターが追加されない場合、コンソール上の出力は Symbol() となり、差別化には役立ちません。パラメータを取得した後は、パラメータに説明を追加することと同じであり、出力時にそれがどの値であるかがわかります。

Symbol のパラメータがオブジェクトの場合、オブジェクトの toString() メソッドが呼び出されて文字列に変換され、Symbol 値が生成されます。

const obj = {
  toString() {
    「abc」を返します;
  }
};
const sym = シンボル(obj);
sym // 記号(abc)

Symbol() 関数のパラメータは現在の Symbol 値の説明のみを表すため、同じパラメータを持つ Symbol 関数の戻り値は等しくないことに注意してください。

//パラメータなし
s1 = シンボル(); とします。
s2 = シンボル(); とします。

s1 === s2 // false

// パラメータがある場合
s1 = Symbol('foo'); とします。
s2 = Symbol('foo'); とします。

s1 === s2 // false

上記のコードでは、s1s2はどちらもSymbol()関数の戻り値であり、パラメータは同じですが、等しくありません。実際、「Symbol()」を 100 回呼び出すと、100 個の不等な値が得られます。

シンボル値は他のタイプの値では操作できず、エラーが報告されます。

let sym = Symbol('私のシンボル');

「あなたのシンボルは」 + sym
// TypeError: シンボルを文字列に変換できません
`あなたのシンボルは ${sym}`
// TypeError: シンボルを文字列に変換できません

ただし、シンボル値は明示的に文字列に変換できます。

let sym = Symbol('私のシンボル');

String(sym) // 'Symbol(私のシンボル)'
sym.toString() // 'シンボル(私のシンボル)'

また、シンボル値もブール値に変換できますが、数値に変換することはできません。

sym = Symbol(); とします。
Boolean(sym) // true
!sym // false

if (sym) {
  // ...
}

Number(sym) //TypeError
sym + 2 // TypeError

シンボル.プロトタイプ.説明

前述したように、Symbol() 関数が Symbol 値を作成するときに、パラメーターを使用して説明を追加できます。

const sym = Symbol('foo');

上記のコードでは、値 sym の説明は文字列 foo です。

ただし、この説明を読むには、シンボルを明示的に文字列に変換する必要があり、次のように記述されます。

const sym = Symbol('foo');

String(sym) // "シンボル(foo)"
sym.toString() // "シンボル(foo)"

上記の使用法はあまり便利ではありません。 ES2019 は、シンボル値のインスタンス属性 description を提供します。これは、シンボル値の説明を直接返します。

const sym = Symbol('foo');

sym.description // "foo"

属性名としてのシンボル

各 Symbol 値は等しくないため、Symbol 値がオブジェクトのプロパティ名の識別子として使用される限り、同じ名前のプロパティは出現しないことが保証されます。これは、オブジェクトが複数のモジュールで構成されている場合に、キーが誤って上書きされたり上書きされたりするのを防ぐのに役立ちます。

mySymbol = Symbol(); とします。

//最初の書き方
a = {} にします。
a[mySymbol] = 'こんにちは!';

// 2 番目の書き方
a = { にしておきます
  [mySymbol]: 「こんにちは!」
};

// 3 番目の書き方
a = {} にします。
Object.defineProperty(a, mySymbol, { value: 'Hello!' });

// 上記の書き方はすべて同じ結果になります。
a[mySymbol] // 「こんにちは!」

上記のコードは、角かっこ構造と Object.defineProperty() メソッドを使用して、オブジェクトのプロパティ名を Symbol 値として指定します。

オブジェクトのプロパティ名として Symbol 値を使用する場合、ドット演算子は使用できないことに注意してください。

const mySymbol = Symbol();
const a = {};

a.mySymbol = 'こんにちは!';
a[mySymbol] // 未定義
a['mySymbol'] // 「こんにちは!」

上記のコードでは、ドット演算子の後には常に文字列が続くため、mySymbol が識別名として参照する値は読み取られず、その結果、a の属性名は実際にはシンボルではなく文字列になります。 。 価値。

同様に、シンボル値を使用してオブジェクト内のプロパティを定義する場合、シンボル値を角かっこで囲む必要があります。

let s = Symbol();

obj = { にします
  [s]: 関数 (引数) { ... }
};

obj[s](123);

上記のコードで、「s」が角括弧内に配置されていない場合、属性のキー名は「s」で表されるシンボル値ではなく、文字列「s」になります。

拡張されたオブジェクト記述方法を使用すると、上記のコードの obj オブジェクトをより簡潔に記述することができます。

obj = { にします
  [s](引数) { ... }
};

シンボル タイプを使用して定数セットを定義し、この定数セットの値が等しくないことを確認することもできます。

const ログ = {};

ログレベル = {
  デバッグ: シンボル('デバッグ')、
  情報: シンボル('情報')、
  警告: シンボル('警告')
};
console.log(log.levels.DEBUG, 'デバッグメッセージ');
console.log(log.levels.INFO, '情報メッセージ');

別の例を示します。

const COLOR_RED = シンボル();
const COLOR_GREEN = シンボル();

関数 getComplement(color) {
  スイッチ (色) {
    ケース COLOR_RED:
      COLOR_GREENを返します。
    ケースCOLOR_GREEN:
      COLOR_REDを返します。
    デフォルト:
      throw new Error('未定義の色');
    }
}

定数にシンボル値を使用する最大の利点は、他の値が同じ値を持つことができないため、上記の switch ステートメントが設計どおりに動作することが保証されることです。

もう 1 つ注意すべき点は、Symbol 値が属性名として使用されている場合でも、その属性はプライベート属性ではなくパブリック属性であることです。

例: マジックストリングの削除

マジック文字列は、コード内に複数回出現し、コードとの強い結合を形成する特定の文字列または値を指します。適切なスタイルのコードでは、マジック文字列を削除し、明確な意味を持つ変数に置き換えるようにする必要があります。

function getArea(形状, オプション) {
  面積 = 0 とします。

  スイッチ (形状) {
    case 'Triangle': // マジック文字列
      エリア = .5 * オプション.幅 * オプション.高さ;
      壊す;
    /* ... さらにコード ... */
  }

  返品エリア。
}

getArea('Triangle', { width: 100, height: 100 }); // マジック文字列

上記のコードでは、文字列 Triangle がマジック文字列です。これは何度も出現し、コードとの「強い結合」を形成し、将来の変更やメンテナンスには役立ちません。

マジック文字列を削除する一般的な方法は、それを変数として書き込むことです。

const 形状タイプ = {
  三角形: 「三角形」
};

function getArea(形状, オプション) {
  面積 = 0 とします。
  スイッチ (形状) {
    ケースの形状タイプ.triangle:
      エリア = .5 * オプション.幅 * オプション.高さ;
      壊す;
  }
  返品エリア。
}

getArea(shapeType.triangle, { 幅: 100, 高さ: 100 });

上記のコードでは、「shapeType」オブジェクトの「triangle」属性として「Triangle」を記述し、強い結合を排除しています。

これを注意深く分析すると、他の shapeType 属性の値と競合しない限り、shapeType.triangle がどの値に等しいかは問題ではないことがわかります。したがって、これは代わりにシンボル値を使用するのに適しています。

const 形状タイプ = {
  三角形: 記号​​()
};

上記のコードでは、「shapeType.triangle」の値をシンボルに設定することを除いて、その他の変更は必要ありません。

属性名の走査

Symbol 値は、オブジェクトをトラバースするときに属性名として使用され、その属性は for...infor...of ループに表示されず、Object.keys( によっても使用されません)。 )Object.getOwnPropertyNames()JSON.stringify() が返されます。

ただし、これはプライベート プロパティではありません。指定されたオブジェクトのすべての Symbol プロパティ名を取得できる Object.getOwnPropertySymbols() メソッドがあります。このメソッドは、メンバーがすべて現在のオブジェクトのプロパティ名として使用される Symbol 値である配列を返します。

const obj = {};
a = Symbol('a'); とします。
b = Symbol('b'); とします。

obj[a] = 'こんにちは';
obj[b] = 'ワールド';

const objectSymbols = Object.getOwnPropertySymbols(obj);

オブジェクトシンボル
// [記号(a)、記号(b)]

上記のコードは、すべての Symbol プロパティ名を取得できる Object.getOwnPropertySymbols() メソッドの例です。

以下は、「Object.getOwnPropertySymbols()」メソッドと「for...in」ループおよび「Object.getOwnPropertyNames」メソッドを比較する別の例です。

const obj = {};
const foo = Symbol('foo');

obj[foo] = 'バー';

for (obj に入れます) {
  console.log(i); // 出力なし
}

Object.getOwnPropertyNames(obj) // []
Object.getOwnPropertySymbols(obj) // [Symbol(foo)]

上記のコードでは、「for...in」ループと「Object.getOwnPropertyNames()」メソッドを使用してシンボル キー名を取得できません。「Object.getOwnPropertySymbols()」メソッドを使用する必要があります。

もう 1 つの新しい API である Reflect.ownKeys() メソッドは、通常のキー名とシンボル キー名を含むすべてのタイプのキー名を返すことができます。

obj = { にします
  [シンボル('my_key')]: 1、
  列挙型: 2、
  非列挙型: 3
};

Reflect.ownKeys(obj)
// ["enum", "nonEnum", Symbol(my_key)]

Symbol 値はキー名として使用されるため、従来の方法では走査されません。この機能を利用して、内部でのみ使用したいオブジェクトの非プライベート メソッドを定義できます。

サイズ = シンボル('サイズ');

クラス コレクション {
  コンストラクター() {
    this[サイズ] = 0;
  }

  追加(項目) {
    this[this[size]] = アイテム;
    この[サイズ]++;
  }

  静的な sizeOf(インスタンス) {
    インスタンス[サイズ]を返します;
  }
}

let x = 新しいコレクション();
Collection.sizeOf(x) // 0

x.add('foo');
Collection.sizeOf(x) // 1

Object.keys(x) // ['0']
Object.getOwnPropertyNames(x) // ['0']
Object.getOwnPropertySymbols(x) // [シンボル(サイズ)]

上記のコードでは、オブジェクト xsize プロパティは Symbol 値であるため、Object.keys(x)Object.getOwnPropertyNames(x) も取得できません。これにより、非プライベートな内部メソッドの効果が作成されます。

Symbol.for()、Symbol.keyFor()

場合によっては、同じ Symbol 値を再利用したい場合があり、Symbol.for() メソッドを使用してこれを行うことができます。文字列をパラメータとして受け入れ、そのパラメータを名前として持つシンボル値を検索します。存在する場合は、この Symbol 値を返します。存在しない場合は、この文字列で名前を付けた新しい Symbol 値を作成し、グローバルに登録します。

s1 = Symbol.for('foo'); とします。
s2 = Symbol.for('foo'); とします。

s1 === s2 // true

上記のコードでは、s1s2 はどちらも Symbol 値ですが、同じパラメータを使用して Symbol.for メソッドによって生成されるため、実際には同じ値になります。

Symbol.for()Symbol() の書き込みメソッドはどちらも新しいシンボルを生成します。両者の違いは、前者は検索のためにグローバル環境に登録されますが、後者は登録されないことです。 Symbol.for() は呼び出されるたびに新しい Symbol 型の値を返すのではなく、最初に指定された key がすでに存在するかどうかを確認し、存在しない場合は新しい値を作成します。たとえば、Symbol.for("cat") を 30 回呼び出すと、毎回同じ Symbol 値が返されますが、Symbol("cat") を 30 回呼び出すと、30 個の異なる Symbol 値が返されます。

Symbol.for("バー") === Symbol.for("バー")
// 真実

シンボル("バー") === シンボル("バー")
// 間違い

上記のコードでは、Symbol() の書き込みメソッドに登録機構がないため、呼び出されるたびに異なる値が返されます。

Symbol.keyFor() メソッドは、登録された Symbol タイプの値の key を返します。

s1 = Symbol.for("foo"); とします。
Symbol.keyFor(s1) // "foo"

s2 = Symbol("foo"); とします。
Symbol.keyFor(s2) // 未定義

上記のコードでは、変数 s2 が未登録の Symbol 値に属しているため、unknown が返されます。

Symbol 値に対して Symbol.for() によって登録される名前は、グローバル環境で実行されるかどうかに関係なく、グローバル環境にあることに注意してください。

関数 foo() {
  return Symbol.for('bar');
}

const x = foo();
const y = Symbol.for('bar');
console.log(x === y); // true

上記のコードでは、関数内で Symbol.for('bar') が実行されていますが、生成された Symbol 値はグローバル環境に登録されます。したがって、Symbol.for('bar') を 2 回目に実行すると、この Symbol 値を取得できます。

Symbol.for() のグローバル登録機能を使用すると、異なる iframe または Service Worker で同じ値を取得できます。

iframe = document.createElement('iframe');
iframe.src = 文字列(window.location);
document.body.appendChild(iframe);

iframe.contentWindow.Symbol.for('foo') === Symbol.for('foo')
// 真実

上記のコードでは、iframe ウィンドウによって生成された Symbol 値をメイン ページで取得できます。

例: モジュールのシングルトン モード

シングルトン パターンは、いつでもクラスを呼び出して同じインスタンスを返すことを指します。

Nodeの場合、モジュールファイルをクラスとみなすことができます。このモジュール ファイルが実行されるたびに同じインスタンスが返されるようにするにはどうすればよいですか?

インスタンスは最上位オブジェクト「global」に配置できると考えるのは簡単です。

// mod.js
関数 A() {
  this.foo = 'こんにちは';
}

if (!global._foo) {
  global._foo = 新しい A();
}

module.exports = global._foo;

次に、上記の mod.js を読み込みます。

const a = require('./mod.js');
console.log(a.foo);

上記のコードでは、変数 a には常に A の同じインスタンスがロードされます。

ただし、ここで問題が発生します。グローバル変数 global._foo は書き込み可能であり、どのファイルからも変更できます。

global._foo = { foo: 'ワールド' };

const a = require('./mod.js');
console.log(a.foo);

上記のコードは、mod.js をロードするスクリプトを歪めます。

これを防ぐには、Symbol を使用します。

// mod.js
const FOO_KEY = Symbol.for('foo');

関数 A() {
  this.foo = 'こんにちは';
}

if (!global[FOO_KEY]) {
  グローバル[FOO_KEY] = 新しい A();
}

module.exports = グローバル[FOO_KEY];

上記のコードでは、global[FOO_KEY] が誤って上書きされないことが保証されていますが、それでも上書きされる可能性があります。

global[Symbol.for('foo')] = { foo: 'world' };

const a = require('./mod.js');

Symbolメソッドでキー名を生成した場合、その値を外部から参照することはできず、当然書き換えることもできません。

// mod.js
const FOO_KEY = シンボル('foo');

//以下のコードは同じです...

上記のコードにより、他のスクリプトは FOO_KEY を参照できなくなります。しかし、これには問題があります。つまり、このスクリプトを複数回実行すると、毎回取得する FOO_KEY が異なります。 Nodeはスクリプトの実行結果をキャッシュしますが、一般的に同じスクリプトが複数回実行されることはありませんが、ユーザーが手動でキャッシュをクリアできるため、絶対に信頼できるわけではありません。

組み込みシンボル値

使用する Symbol 値を定義することに加えて、ES6 は言語の内部で使用されるメソッドを指す 11 個の組み込み Symbol 値も提供します。

Symbol.hasInstance

オブジェクトの Symbol.hasInstance プロパティは内部メソッドを指します。このメソッドは、他のオブジェクトが instanceof 演算子を使用して、それらがオブジェクトのインスタンスであるかどうかを判断するときに呼び出されます。たとえば、foo instanceof Foo は実際には言語内で Foo[Symbol.hasInstance](foo) によって呼び出されます。

クラス MyClass {
  [Symbol.hasInstance](foo) {
    foo 配列のインスタンスを返します。
  }
}

[1, 2, 3]instanceof new MyClass() // true

上記のコードでは、MyClass はクラスであり、new MyClass() はインスタンスを返します。このインスタンスの Symbol.hasInstance メソッドは、instanceof 操作を実行するときに自動的に呼び出され、左側の演算子が Array のインスタンスであるかどうかを判断します。

別の例を示します。

偶数クラス {
  static [Symbol.hasInstance](obj) {
    戻り値 Number(obj) % 2 === 0;
  }
}

// と同等
const 偶数 = {
  [Symbol.hasInstance](obj) {
    戻り値 Number(obj) % 2 === 0;
  }
};

1 インスタンスオブ偶数 // false
2instanceof Even // true
12345 インスタンスオブ偶数 // false

Symbol.isConcatSpreadable

オブジェクトの Symbol.isConcatSpreadable プロパティはブール値に等しく、Array.prototype.concat() で使用されるときにオブジェクトを展開できるかどうかを示します。

arr1 = ['c', 'd']; とします。
['a', 'b'].concat(arr1, 'e') // ['a', 'b', 'c', 'd', 'e']
arr1[Symbol.isConcatSpreadable] // 未定義

arr2 = ['c', 'd']; とします。
arr2[Symbol.isConcatSpreadable] = false;
['a', 'b'].concat(arr2, 'e') // ['a', 'b', ['c','d'], 'e']

上記のコードは、配列のデフォルトの動作が展開可能であること、および Symbol.isConcatSpreadable がデフォルトで unknown に等しいことを示しています。この属性が「true」に等しい場合、拡張の効果もあります。

配列のようなオブジェクトはその逆で、デフォルトでは展開されません。展開する前に、その Symbol.isConcatSpreadable プロパティが true に設定されます。

let obj = {長さ: 2, 0: 'c', 1: 'd'};
['a', 'b'].concat(obj, 'e') // ['a', 'b', obj, 'e']

obj[Symbol.isConcatSpreadable] = true;
['a', 'b'].concat(obj, 'e') // ['a', 'b', 'c', 'd', 'e']

Symbol.isConcatSpreadable プロパティはクラス内で定義することもできます。

クラス A1 extends Array {
  コンストラクター(引数) {
    スーパー(引数);
    this[Symbol.isConcatSpreadable] = true;
  }
}
class A2 extends Array {
  コンストラクター(引数) {
    スーパー(引数);
  }
  get [Symbol.isConcatSpreadable] () {
    false を返します。
  }
}
a1 = 新しい A1(); とします。
a1[0] = 3;
a1[1] = 4;
a2 = 新しい A2(); とします。
a2[0] = 5;
a2[1] = 6;
[1, 2].concat(a1).concat(a2)
// [1、2、3、4、[5、6]]

上記のコードでは、クラス A1 は展開可能ですが、クラス A2 は展開可能ではないため、concat を使用すると結果が異なります。

Symbol.isConcatSpreadable の場所の違いは、A1 がインスタンス上で定義され、A2 がクラス自体上で定義されていることです。これは同じ効果をもたらします。

シンボル.種

オブジェクトの Symbol.species プロパティはコンストラクターを指します。このプロパティは、派生オブジェクトを作成するときに使用されます。

class MyArray extends Array {
}

const a = 新しい MyArray(1, 2, 3);
const b = a.map(x => x);
const c = a.filter(x => x > 1);

binstanceof MyArray // true
cinstanceof MyArray // true

上記のコードでは、サブクラス MyArray は親クラス Array を継承し、aMyArray のインスタンスであり、bca の派生オブジェクトです。 bc はどちらも array メソッドを呼び出して生成されるため、配列 (Array のインスタンス) であるはずだと思うかもしれませんが、実際には MyArray のインスタンスでもあります。

この問題を解決するために、Symbol.species プロパティが提供されています。これで、MyArraySymbol.species プロパティを設定できるようになりました。

class MyArray extends Array {
  static get [Symbol.species]() { 配列を返す }
}

上記のコードでは、Symbol.species 属性が定義されているため、この属性によって返される関数は、派生オブジェクトを作成するときにコンストラクターとして使用されます。この例は、「Symbol.species」プロパティを定義するために「get」評価子を使用する必要があることも示しています。デフォルトの Symbol.species プロパティは、次の記述と同等です。

static get [Symbol.species]() {
  これを返します。
}

次に、前の例を見てください。

class MyArray extends Array {
  static get [Symbol.species]() { 配列を返す }
}

const a = new MyArray();
const b = a.map(x => x);

binstanceof MyArray // false
b 配列のインスタンス // true

上記のコードでは、「a.map(x => x)」によって生成された派生オブジェクトは「MyArray」のインスタンスではなく、直接「Array」のインスタンスです。

別の例を見てみましょう。

クラス T1Promise { を拡張します
}

クラス T2Promise { を拡張します
  static get [Symbol.species]() {
    約束を返します。
  }
}

new T1(r => r()).then(v => v)instanceof T1 // true
new T2(r => r()).then(v => v)instanceof T2 // false

上記のコードでは、「T2」は「Symbol.species」属性を定義していますが、「T1」は定義していません。その結果、派生オブジェクト (then メソッド) を作成するとき、T1 は独自のコンストラクターを呼び出しますが、T2Promise コンストラクターを呼び出します。

つまり、Symbol.species の機能は、インスタンス オブジェクトが動作中に再度独自のコンストラクターを呼び出す必要がある場合、この属性で指定されたコンストラクターを呼び出すことです。その主な目的は、一部のクラス ライブラリが基本クラスに基づいて変更されることです。サブクラスが継承されたメソッドを使用する場合、作成者はサブクラスのインスタンスではなく、基本クラスのインスタンスを返したい場合があります。

シンボル.マッチ

オブジェクトの Symbol.match プロパティは関数を指します。 str.match(myObject) を実行すると、プロパティが存在する場合はそのプロパティが呼び出され、メソッドの戻り値が返されます。

String.prototype.match(正規表現)
// と同等
正規表現[Symbol.match](this)

クラス MyMatcher {
  [シンボル.一致](文字列) {
    'hello world' を返します。indexOf(string);
  }
}

'e'.match(new MyMatcher()) // 1

シンボル.置換

オブジェクトの Symbol.replace プロパティはメソッドを指し、オブジェクトが String.prototype.replace メソッドによって呼び出される場合、そのメソッドの戻り値が返されます。

String.prototype.replace(searchValue, replaceValue)
// と同等
searchValue[Symbol.replace](this, replaceValue)

以下に例を示します。

const x = {};
x[Symbol.replace] = (...s) => console.log(s);

'Hello'.replace(x, 'World') // ["Hello", "World"]

Symbol.replace メソッドは 2 つのパラメータを受け取ります。最初のパラメータは replace メソッドが動作するオブジェクトです。上の例は Hello です。上の例は World' です。

シンボル検索

オブジェクトの Symbol.search プロパティはメソッドを指します。オブジェクトが String.prototype.search メソッドによって呼び出される場合、そのメソッドの戻り値が返されます。

文字列.prototype.search(正規表現)
// と同等
正規表現[シンボル.検索](this)

クラス MySearch {
  コンストラクター(値) {
    this.value = 値;
  }
  [シンボル検索](文字列) {
    戻り値 string.indexOf(this.value);
  }
}
'foobar'.search(new MySearch('foo')) // 0

シンボル.split

オブジェクトの Symbol.split プロパティはメソッドを指します。オブジェクトが String.prototype.split メソッドによって呼び出される場合、メソッドの戻り値が返されます。

String.prototype.split(区切り文字、制限)
// と同等
separator[Symbol.split](this,limit)

以下に例を示します。

クラス MySplitter {
  コンストラクター(値) {
    this.value = 値;
  }
  [シンボル.split](文字列) {
    インデックス = string.indexOf(this.value);
    if (インデックス === -1) {
      文字列を返します。
    }
    戻る [
      string.substr(0, インデックス),
      string.substr(index + this.value.length)
    ];
  }
}

'foobar'.split(new MySplitter('foo'))
// [''、 'バー']

'foobar'.split(new MySplitter('bar'))
// ['foo', '']

'foobar'.split(new MySplitter('baz'))
// 'フーバー'

上記のメソッドは、Symbol.split メソッドを使用して、文字列オブジェクトの split メソッドの動作を再定義します。

シンボル.イテレータ

オブジェクトの Symbol.iterator プロパティは、オブジェクトのデフォルトの反復子メソッドを指します。

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

[...myIterable] // [1, 2, 3]

オブジェクトが for...of ループを実行すると、Symbol.iterator メソッドが呼び出され、オブジェクトのデフォルトのトラバーサが返されます。詳細については、「イテレータと for...of ループ」の章を参照してください。

クラス コレクション {
  *[Symbol.iterator]() {
    i = 0 とします。
    while(this[i] !== 未定義) {
      これを生成します[i];
      ++i;
    }
  }
}

let myCollection = new Collection();
myCollection[0] = 1;
myCollection[1] = 2;

for(myCollection の値を許可) {
  console.log(値);
}
// 1
// 2

シンボル.toプリミティブ

オブジェクトの Symbol.toPrimitive プロパティはメソッドを指します。オブジェクトがプリミティブ型の値に変換されると、このメソッドが呼び出され、オブジェクトに対応するプリミティブ型の値が返されます。

Symbol.toPrimitive が呼び出されるとき、現在の動作モードを示す文字列パラメータを受け取ります。 合計 3 つのモードがあります。

  • 数値: この場合、数値に変換する必要があります。
  • 文字列: この場合、文字列に変換する必要があります。
  • デフォルト: この場合、数値または文字列に変換できます。
obj = { にします
  [Symbol.toPrimitive](ヒント) {
    スイッチ (ヒント) {
      ケース「番号」:
        123 を返します。
      ケース「文字列」:
        'str'を返します;
      'デフォルト'の場合:
        「デフォルト」を返します。
      デフォルト:
        新しい Error() をスローします。
     }
   }
};

2 * オブジェクト // 246
3 + obj // '3default'
obj == 'デフォルト' // true
String(obj) // 'str'

Symbol.toStringTag

オブジェクトの Symbol.toStringTag プロパティは文字列を設定するために使用されます (他のタイプの値に設定することは無効ですが、エラーは報告されません)。ターゲット オブジェクトで Object.prototype.toString() メソッドを呼び出すときに、Symbol.toStringTag プロパティが存在する場合、このプロパティによって設定された文字列が toString() メソッドによって返される文字列に表示されます。オブジェクトのタイプ。つまり、この属性は、[object Object] または [object Array]object の後の大文字文字列をカスタマイズするために使用できます。

//例1
({[Symbol.toStringTag]: 'Foo'}.toString())
// "[オブジェクト Foo]"

//例2
クラス コレクション {
  get [Symbol.toStringTag]() {
    「xxx」を返します;
  }
}
let x = 新しいコレクション();
Object.prototype.toString.call(x) // "[オブジェクト xxx]"

ES6 の新しい組み込みオブジェクトの Symbol.toStringTag プロパティの値は次のとおりです。

  • JSON[Symbol.toStringTag]: 'JSON'
  • Math[Symbol.toStringTag]: '数学'
  • モジュールオブジェクト M[Symbol.toStringTag]: 'モジュール'
  • ArrayBuffer.prototype[Symbol.toStringTag]: 'ArrayBuffer'
  • DataView.prototype[Symbol.toStringTag]: 'DataView'
  • Map.prototype[Symbol.toStringTag]: 'マップ'
  • Promise.prototype[Symbol.toStringTag]: '約束'
  • Set.prototype[Symbol.toStringTag]: '設定'
  • %TypedArray%.prototype[Symbol.toStringTag]: 'Uint8Array' など。
  • WeakMap.prototype[Symbol.toStringTag]: 'WeakMap'
  • WeakSet.prototype[Symbol.toStringTag]: 'WeakSet'
  • %MapIteratorPrototype%[Symbol.toStringTag]: 'マップ反復子'
  • %SetIteratorPrototype%[Symbol.toStringTag]: '反復子の設定'
  • %StringIteratorPrototype%[Symbol.toStringTag]: '文字列反復子'
  • Symbol.prototype[Symbol.toStringTag]: 'シンボル'
  • Generator.prototype[Symbol.toStringTag]: 'ジェネレーター'
  • GeneratorFunction.prototype[Symbol.toStringTag]: 'GeneratorFunction'

Symbol.unscopables

オブジェクトの Symbol.unscopables プロパティはオブジェクトを指します。このオブジェクトは、「with」キーワードを使用するときに「with」環境から除外するプロパティを指定します。

Array.prototype[Symbol.unscopables]
// {
// copyWithin: true,
// エントリ: true,
// 塗りつぶし: true,
// 検索: true、
// findIndex: true,
// 含まれるもの: true、
// キー: true
// }

Object.keys(Array.prototype[Symbol.unscopables])
// ['copyWithin', 'entries', 'fill', 'find', 'findIndex', 'includes', 'keys']

上記のコードは、配列に 7 つの属性があり、これらは with コマンドによって除外されることを示しています。

// アンスコープがない場合
クラス MyClass {
  foo() { 1 を返す }
}

var foo = function () { return 2 };

with (MyClass.prototype) {
  foo(); // 1
}

// スコープ不可能なものがある場合
クラス MyClass {
  foo() { 1 を返す }
  get [Symbol.unscopables]() {
    戻り値 { foo: true };
  }
}

var foo = function () { return 2 };

with (MyClass.prototype) {
  foo(); // 2
}

上記のコードは Symbol.unscopables 属性を指定しているため、with 構文ブロックは現在のスコープで foo 属性を検索しません。つまり、foo は外側のスコープの変数を指します。


作者: wangdoc

アドレス: https://wangdoc.com/

ライセンス: クリエイティブ・コモンズ 3.0