web-dev-qa-db-ja.com

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

UPDATE:最近Mozillaからの 素晴らしい記事 が登場しました。興味があるならそれを読んでください。

ご存知かもしれませんが、それらは ECMAScript 6に new Symbolプリミティブ型を含めることを計画しています(他のクレイジーなものは言うまでもありません)。私はいつもRubyの:symbolの概念は無用だと思いました。 JavaScriptのように、代わりにプレーンな文字列を簡単に使用できます。そして今、彼らはそれを使ってJSのことを複雑にすることにしました。

やる気がわからない。 JavaScriptで本当にシンボルが必要かどうかを私に説明してもらえますか。

321
Yanis

Javascriptにシンボルを導入する最初の動機は、privateプロパティを有効にすることでした。

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

それらは現在ユニークシンボルとして知られており、それらの唯一の意図された用途はプロパティ間の名前衝突を避けることです。たとえば、ECMAScript自体は、ユーザー名と衝突する危険を冒さずに、オブジェクトに配置できる特定の方法で拡張フックを導入できるようになりました(たとえば、反復プロトコルの定義など)。

それが言語にシンボルを追加する動機付けが十分に強いかどうかは議論の余地があります。

196

シンボルは真のプライバシーを保証するものではありませんが、オブジェクトのパブリックプロパティと内部プロパティを区別するために使用できます。プライベートプロパティを持つために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
82
Samar Panda

Symbolsname__は、オブジェクト内で一意のプロパティ名として使用できる新しい特別な種類のオブジェクトです。 Symbolname__の代わりにstringname__を使用すると、さまざまなモジュールが互いに競合しないプロパティを作成できます。 Symbolsname__を非公開にすることもできます。そのため、Symbolname__に直接アクセスできない人がそのプロパティにアクセスすることはできません。

Symbolsname__は新しいプリミティブです。 numbername__、stringname__、およびbooleanname__プリミティブのように、Symbolname__にはそれらを作成するために使用できる関数があります。他のプリミティブとは異なり、Symbolsname__にはリテラル構文はありません(例:stringname__の''の作成方法) - それらを作成する唯一の方法は、以下のようにSymbolname__コンストラクターを使用することです。

let symbol = Symbol();

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

Symbolname__プリミティブ型の利点をいくつか紹介します。

Symbolsname__にはデバッグ機能が組み込まれています

Symbolsname__には説明を与えることができます。これは、コンソールにログインするときの動作を少し楽にするために、デバッグのためだけに使用されます。

Symbolsname__はObjectname__キーとして使用できます

これがSymbolname__が本当におもしろくなるところです。それらは物と深く絡み合っています。 Symbolname__はオブジェクトへのキーとして割り当てることができます。つまり、オブジェクトには無制限の数の一意のSymbolname__を割り当てることができ、これらがstringname__キーや他の一意のSymbolsname__と競合しないことが保証されます。

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

logger.levels.DEBUGlogger.levels.INFOlogger.levels.WARNなどの複数のログレベルを含むロギングライブラリがあるとしましょう。 ES5コードでは、これらのstringname__s(so logger.levels.DEBUG === 'debug')、またはnumbername__s(logger.levels.DEBUG === 10)を作成します。これらの値は、一意の値ではないため理想的ではありませんが、Symbolname__sはそうです!だから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');

この続きを読む すばらしい記事

34

この記事はSymbol()についてのもので、私が見つけることができる実際の例と、見つけることができる事実と定義が付属しています。

TLDR;

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

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

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

  • Symbol()で定義された変数はすべて一意のコンテンツを取得しますが、実際はそうではありません private

  • どのデータにも独自の記号があり、同じデータの場合、記号は同じになります。次の段落で詳細な情報があります。そうでなければそれはTLRDではありません。 :)

シンボルを初期化する方法

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

これはどちらの方法でも可能です。

var mySymbol1 = Symbol();

またはこのように:

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

"some text here"文字列はシンボルから抽出することはできません。デバッグ目的のための単なる説明です。 symbolの動作を変えることはありません。しかし、あなたはそれを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タイプ」のシンボルと呼びましょう。それらは、「第1タイプ」のシンボル(つまり、Symbol(data)で定義されたもの)と交差しません。

次の2つの段落はfirst-type記号だけに関係します。

古いデータ型の代わりにSymbolを使用するとどのように恩恵がありますか?

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

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

Peterという名前の人が2人いるとしたらどうでしょうか。

これを行う:

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

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

それで、2人の絶対に異なる人が同じ名前を持つことの問題であるように思われます。それでは、新しい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ペアは返されません。

この初期化を使用して、1人目と2人目のエントリを間違えることは絶対に不可能です。彼らのためにconsole.logを呼び出すことは彼らのセカンドネームを正しく出力するでしょう。

 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

また、前の段落のようにSymbolで定義した場合:

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
    

28
nicael

これが私の見方です。シンボルは、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パースペクティブ。

17
Chong Lip Phang

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