シンボルをES6に導入する動機は何ですか?


368

更新:最近、Mozillaからの素晴らしい記事が登場しました。興味があれば読んでください。

ご存じかもしれませんが、彼らはECMAScript 6 新しいSymbolプリミティブ型を含めることを計画しています(他のクレイジーなものは言うまでもありません)。:symbolRuby の概念は不必要だといつも思っていました。JavaScriptの場合のように、代わりにプレーンストリングを簡単に使用できます。そして今、彼らはJSで物事を複雑にすることにしました。

動機がわかりません。JavaScriptでシンボルが本当に必要かどうか誰かに説明してもらえますか?


6
この説明がどれほど信頼できるかはわかりませんが、それは始まりです:tc39wiki.calculist.org/es6/symbols
Felix Kling 14

8
シンボルは非常に有効であり、オブジェクトのスコープ付き一意識別子を許可します。たとえば、1か所でのみアクセス可能なオブジェクトのプロパティを持つ。
Benjamin Gruenbaum 2014

5
Object.getOwnPropertySymbols(o)を使用できるため、それについてはわかりません
Yanis

4
それはプライバシーというよりも独自性です。
カンタス94ヘビー

2
彼らは、より単純なクラスの実装のために捨てることに決めたclass属性のキーワードprivateと、より複雑なクラスの実装を持つpublicことになっていました。代わりに、プライベート変数を使用this.x = xすることになっていた。彼らはそれをはるかに最小限のクラス実装のために捨てることに決めました。シンボルは、最小限の実装でプライベートプロパティを取得するために必要な回避策になります。public x = xprivate y = y
リスコニング

回答:


224

JavaScriptにシンボルを導入する当初の動機は、プライベートプロパティを有効にすることでした。

残念ながら、彼らはひどく格下げされた。リフレクション、たとえば、Object.getOwnPropertySymbolsまたはプロキシを使用してそれらを見つけることができるため、それらはもはやプライベートではありません。

現在、これらは一意のシンボルとして知られており、プロパティ間の名前の衝突を回避することのみを目的としています。たとえば、ECMAScript自体が特定のメソッドを介して拡張フックを導入できるようになりました。これにより、ユーザー名と競合する危険を冒すことなく、オブジェクトに配置できます(反復プロトコルを定義するなど)。

それが言語に記号を追加するための十分な動機であるかどうかは議論の余地があります。


93
ほとんどの言語(すべての主流の言語はafaik)は、とにかくプライベートにアクセスするための何らかのメカニズム(通常はリフレクション)を提供します。
エサイリヤ、2014年

19
@Esailija、私はそれは本当だとは思いません-特に、多くの言語はそもそもリフレクションを提供していないためです。リフレクション(Javaなど)を介してプライベート状態をリークすることは、機能ではなくバグと見なす必要があります。これは、信頼性の高いプライベートステートがセキュリティに関連する可能性があるWebページで特に当てはまります。現在、JSでそれを実現する唯一の方法は、退屈でコストのかかるクロージャーを使用することです。
Andreas Rossberg 2014年

38
メカニズムはリフレクションである必要はありません。C++、Java、C#、Ruby、Python、PHP、Objective-Cはすべて、本当に望めば、何らかの方法でアクセスを許可します。能力ではなくコミュニケーションです。
エサイリア2014年

4
@ plalx、Webでは、カプセル化はセキュリティに関する場合もあります。
Andreas Rossberg、2015年

3
残念ながら、@ RolandPihlakas Object.getOwnPropertySymbolsだけがリークではありません。より難しいのは、「プライベート」プロパティへのアクセスを傍受するためにプロキシを使用する機能です。
Andreas Rossberg、

95

シンボルは真のプライバシーを保証するものではありませんが、オブジェクトのパブリックプロパティと内部プロパティを分離するために使用できます。Symbolプライベートプロパティを持つために使用できる例を見てみましょう。

オブジェクトのプロパティがプライベートではない例を見てみましょう。

var Pet = (function() {
  function Pet(type) {
    this.type = type;
  }
  Pet.prototype.getType = function() {
    return this.type;
  }
  return Pet;
}());

var a = new Pet('dog');
console.log(a.getType());//Output: dog
a.type = null;
//Modified outside
console.log(a.getType());//Output: null

上記のPetクラスプロパティtypeはプライベートではありません。これをプライベートにするには、クロージャーを作成する必要があります。以下の例はtype、クロージャーを使用してプライベートにする方法を示しています。

var Pet = (function() {
  function Pet(type) {
    this.getType = function(){
      return type;
    };
  }
  return Pet;
}());

var b = new Pet('dog');
console.log(b.getType());//dog
b.type = null;
//Stays private
console.log(b.getType());//dog

上記のアプローチの短所:Pet作成された各インスタンスに追加のクロージャーを導入しているため、パフォーマンスが低下する可能性があります。

今紹介しSymbolます。これにより、余分な不要なクロージャーを使用せずにプロパティをプライベートにすることができます。以下のコード例:

var Pet = (function() {
  var typeSymbol = Symbol('type');
  function Pet(type) {
    this[typeSymbol] = type;
  }
  Pet.prototype.getType = function(){
    return this[typeSymbol];
  }
  return Pet;
}());

var a = new Pet('dog');
console.log(a.getType());//Output: dog
a.type = null;
//Stays private
console.log(a.getType());//Output: dog

15
シンボルプロパティはプライベートではないことに注意してください。シンボルには衝突がありません。受け入れられた回答を読んでください。
Bergi

3
はい、シンボルは真のプライバシーを保証するものではありませんが、オブジェクトのパブリックプロパティと内部プロパティを分離するために使用できます。申し訳ありませんが、このポイントを私の答えに追加するのを忘れていました。それに応じて私の答えを更新します。
Samar Panda

@SamarPanda、接頭辞のあるメンバーは_真のプライバシーを保証しないが、オブジェクトのパブリックプロパティと内部プロパティを分離するために使用できると言うこともできます。つまり、無意味な答えです。
パチェリエ2017年

10
シンボルはデフォルトでは列挙可能ではないため、無意味だとは言えません。また、「ミス」からアクセスすることはできませんが、他のキーはアクセスできます。
Patrick

5
オブジェクトのプライベート属性を通常の属性だけでなくシンボルとして定義する理由について、実際に意味のある例がある唯一の答えを見つけます。
Luis Lobo Borobia

42

Symbolsオブジェクトの一意のプロパティ名として使用できる新しい特別な種類のオブジェクトです。のSymbol代わりにを使用するとstring、異なるモジュールが互いに競合しないプロパティを作成できます。Symbolsをプライベートにすることもできるため、に直接アクセスしていないユーザーは、そのプロパティにアクセスできませんSymbol

Symbols新しいプリミティブです。、、プリミティブと同様にnumber、それらを作成するために使用できる関数があります。他のプリミティブとは異なり、リテラル構文はありません(たとえば、どのように持っている)-それらを作成する唯一の方法は、次のようにコンストラクターを使用することです:stringbooleanSymbolSymbolsstring''Symbol

let symbol = Symbol();

実際には、Symbolはオブジェクトにプロパティをアタッチするためのわずかに異なる方法です- から継承するすべてのものに表示されるように、よく知らSymbolsれている標準メソッドを簡単に提供できます。Object.prototype.hasOwnPropertyObject

Symbolプリミティブ型の利点のいくつかを次に示します。

Symbols デバッグ機能が組み込まれている

Symbols 説明を与えることができます。これは、コンソールにログを記録するときに、生活を少し楽にするために実際にデバッグに使用されるだけです。

SymbolsObjectキーとして使用できます

ここSymbolが本当に面白くなります。それらはオブジェクトと深く絡み合っています。Symbolオブジェクトへのキーとして割り当てることができます。つまり、オブジェクトに無制限のユニークを割り当てることができSymbol、これらがstringキーまたは他のユニークと競合しないことが保証されますSymbols

Symbols 一意の値として使用できます。

のは、次のような複数のログレベルが含まロギングライブラリ、持っていると仮定しましょうlogger.levels.DEBUGlogger.levels.INFOlogger.levels.WARNなどを。ES5コードでは、これらをstrings(so logger.levels.DEBUG === 'debug')またはnumbers(logger.levels.DEBUG === 10)にする必要があります。これらの値は一意の値ではないため、これらはどちらも理想的ではありませんが、Symbolsは一意です。したがって、logger.levels単に次のようになります。

log.levels = {
  DEBUG: Symbol('debug'),
  INFO: Symbol('info'),
  WARN: Symbol('warn'),
};
log(log.levels.DEBUG, 'debug message');
log(log.levels.INFO, 'info message');

詳細については、この素晴らしい記事をご覧ください


10
私はあなたの例を理解しているlog.levels = {DEBUG: Symbol('debug')とは思えませんlog.levels = {DEBUG:'debug'}。最後は同じです。オブジェクトのキーを反復処理するとき、シンボルは見えないことを言及する価値があると思います。それが彼らの「もの」
vsync 2018

1つの利点は、誰かが誤ってリテラルを使用できず、それが永久に機能すると信じていることです。(これは実際には強力な引数ではないことに注意してください。{}同じ結果を(一意の値として)使用して達成できるためです。あるいは、そのプロジェクトではリテラルが優先されるか、最初にドキュメントを読む必要があると言うことができます。)個人的には、コード内のユニークな意味が読みやすくなると思います
apple apple

一意の値として使用すると、オブジェクトリテラルにもデバッグ機能が組み込まれます。つまり、にSymbol("some message")なり{message:'some message'}ます。複数のフィールドを追加できるため、オブジェクトは間違いなくここで優れています。
アップルアップル

38

この投稿はについてでありSymbol()、私が見つけたり作成したりできる実際の例と、見つけた事実と定義が提供されています。

TLDR;

これSymbol()は、ECMAScript 6(ES6)のリリースで導入されたデータ型です。

シンボルには2つの興味深い事実があります。

  • 最初のデータ型であり、リテラルを持たないJavaScriptの唯一のデータ型

  • で定義された変数Symbol()は一意のコンテンツを取得しますが、実際には非公開ではありません。

  • データには独自のシンボルがあり、同じデータの場合、シンボルは同じになります。次の段落で詳細を説明します。それ以外の場合はTLRDではありません。:)

シンボルを初期化するにはどうすればよいですか?

1.デバッグ可能な値を持つ一意の識別子を取得するには

次のいずれかの方法で行うことができます。

var mySymbol1 = Symbol();

またはこのように:

var mySymbol2 = Symbol("some text here");

"some text here"文字列は、シンボルから抽出することができない、それはデバッグ目的のためだけの説明です。シンボルの動作を変更することはありません。ただし、それは可能ですconsole.log(値はデバッグ用であり、他のログエントリと間違えないようにするため、これは公平です)。

console.log(mySymbol2);
// Symbol(some text here)

2.一部の文字列データのシンボルを取得するには

この場合、symbolの値が実際に考慮され、このようにして2つのシンボルが一意でなくなる可能性があります。

var a1 = Symbol.for("test");
var a2 = Symbol.for("test");
console.log(a1 == a2); //true!

それらのシンボルを「第2タイプのシンボル」と呼びましょう。これらは、「最初のタイプ」のシンボル(つまり、で定義されたシンボル)とは決して交差しませんSymbol(data)

次の2つの段落は、最初のタイプのシンボルのみに関係します。

古いデータ型の代わりにシンボルを使用するメリットは何ですか?

まず、標準のデータ型であるオブジェクトについて考えてみましょう。そこでいくつかのキーと値のペアを定義し、キーを指定することで値にアクセスできます。

var persons = {"peter":"pan","jon":"doe"};
console.log(persons.peter);
// pan

ピーターという名前の人が2人いるとしたらどうでしょう。

これを行う:

var persons = {"peter":"first", "peter":"pan"};

あまり意味がありません。

したがって、同じ名前を持つ2人のまったく異なる人の問題であるように見えます。次にnewを参照してみましょうSymbol()。実生活の人のようなものです。どの人もユニークですが、名前は同じでもかまいません。2人の「人」を定義してみましょう。

 var a = Symbol("peter");
 var b = Symbol("peter");

これで、同じ名前の2人の異なる人物ができました。私たちの人は本当に違うのですか?彼らです; あなたはこれをチェックすることができます:

 console.log(a == b);
 // false

そこでどのようなメリットがありますか?

私たちはあなたのオブジェクトに異なる人のために2つのエントリーを作ることができ、それらは決して間違えられません。

 var firstPerson = Symbol("peter");
 var secondPerson = Symbol("peter");
 var persons = {[firstPerson]:"first", [secondPerson]:"pan"};

注:
オブジェクトを文字列JSON.stringify化すると、Symbolをキーとして初期化されたすべてのペアが削除されることに注意してください
実行してObject.keysも、そのようなSymbol()->valueペアは返されません。

この初期化を使用すると、最初の人と2番目の人のエントリを間違えることは絶対に不可能です。console.logそれらを呼び出すと、2番目の名前が正しく出力されます。

 console.log(persons[a]);
 // first
 console.log(persons[b]);
 // pan

オブジェクトで使用すると、列挙不可能なプロパティを定義する場合とどのように異なりますか?

実際、非表示Object.keysおよび列挙するプロパティを定義する方法はすでに存在していました。ここにあります:

var anObject = {};
var fruit = "apple";    

Object.defineProperty( anObject, fruit, {
    enumerable: false,
    value: "green"
});

Symbol()そこにはどんな違いがありますか?違いはObject.defineProperty、通常の方法で定義されたプロパティを引き続き取得できることです。

console.log(anObject[fruit]); //green
console.log(anObject["apple"]); //green
console.log(anObject.apple); //green

そして、前の段落のようにシンボルで定義されている場合:

fruit = Symbol("apple");

変数を知っている場合にのみ、その値を受け取ることができます。

console.log(anObject[fruit]); //green
console.log(anObject["apple"]); //undefined
console.log(anObject.apple); //undefined

さらに、キーの下に別のプロパティを定義"apple"すると、オブジェクトは古いプロパティを削除します(ハードコードされている場合、エラーがスローされる可能性があります)。だから、これ以上のリンゴはありません!それは残念だ。前の段落を参照すると、記号は一意であり、Symbol()一意になるようにキーを定義しています。

型変換とチェック

  • 他のデータ型とは異なり、他のデータ型に変換することは不可能Symbol()です。

  • を呼び出すことにより、プリミティブデータタイプに基づいてシンボルを「作成」することができSymbol(data)ます。

  • タイプのチェックに関しては、何も変更されません。

    function isSymbol ( variable ) {
        return typeof someSymbol === "symbol";
    }
    
    var a_Symbol = Symbol("hey!");
    var totally_Not_A_Symbol = "hey";
    
    console.log(isSymbol(a_Symbol)); //true
    console.log(isSymbol(totally_Not_A_Symbol)); //false


これはSOドキュメントから移行されましたか?
Knu

1
@KNUではありませんでした。私は情報を収集し、この答えを自分で書きました
nicael

本当に美しい答え!
Mihai Alexandru-Ionut

1
Symbolに対する素晴らしい答えですが、なぜ配列の代わりにシンボルキーを持つオブジェクトを使用するのかはまだわかりません。{"peter": "pan"} {"john": "doe"}のように複数の人物がいる場合、それらを1つのオブジェクトに入れるのは気分が悪いです。personFirstName1、personFirstName2のような重複したプロパティを持つクラスを作成しないのと同じ理由で。これを文字列化できないことと組み合わせると、メリットだけではデメリットはわかりません。
eldo

18

これが私の見方です。シンボルは、オブジェクトのキー/プロパティがObject.keys()やJSON.stringify()などの一般的なメソッドを通じて公開されないようにすることにより、「追加レベルのプライバシー」を提供します。

var age = Symbol();  // declared in another module perhaps?
class Person {
   constructor(n,a){
      this.name = n;
      this[age] = a;  
   }
   introduce(){
       console.log(`My name is ${this.name}. I am ${this[age]-10}.`);
   }
}
var j = new Person('Jane',45);
j.introduce();  // My name is Jane. I am 35.
console.log(JSON.stringify(j)); // {"name":"Jane"}
console.log(Object.keys(j)); // ["name"]
console.log(j[age]); // 45   (well…only if you know the age in the first place…)

オブジェクト自体が指定されていても、そのようなプロパティはリフレクション、プロキシ、Object.getOwnPropertySymbols()などを介して公開できますが、OOPの観点からは十分な場合があるいくつかの直接メソッドを介してそれらにアクセスする自然な方法はありません。


2

JSシンボルは新しいプリミティブデータ型です。これらは、一意のIDとして機能するトークンです。シンボルはSymbolコンストラクタを使用して作成できます。たとえば、MDNの次のスニペットを見てみましょう。

// The symbol constructor takes one optional argument, 
// the descriptions which is used for debugging only.
// Here are two symbols with the same description
let Sym1 = Symbol("Sym");
let Sym2 = Symbol("Sym");
  
console.log(Sym1 == Sym2); // returns "false"
// Symbols are guaranteed to be unique.
// Even if we create many symbols with the same description,
// they are different values.

シンボルを一意のオブジェクトプロパティキーとして使用すると便利です。次に例を示します。

let obj = {};
let prop = Symbol();

obj[prop] = 123;  // the symbol prop is assigned 123
obj.prop  = 456;  // the string prop is assigned 456

console.log(obj.prop, obj[prop]); // logs 456, 123


0

シンボルには2つの主な使用例があります。

  1. 「非表示」オブジェクトのプロパティ。別のスクリプトまたはライブラリに「属する」オブジェクトにプロパティを追加する場合は、シンボルを作成してプロパティキーとして使用できます。シンボリックプロパティはに表示されないfor..inため、他のプロパティと一緒に誤って処理されることはありません。また、別のスクリプトにはシンボルがないため、直接アクセスすることはできません。そのため、プロパティは誤って使用または上書きされないように保護されます。

    したがって、シンボリックプロパティを使用して、必要なオブジェクトに何かを「密かに」隠すことができます。

  2. JavaScriptで使用される多くのシステムシンボルがあり、としてアクセスできますSymbol.*。これらを使用して、いくつかの組み込み動作を変更できます。たとえばSymbol.iterator、反復可能Symbol.toPrimitiveオブジェクトの場合、 オブジェクトからプリミティブへの変換をセットアップする場合などです。

ソース

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.