UPDATE:最近Mozillaからの 素晴らしい記事 が登場しました。興味があるならそれを読んでください。
ご存知かもしれませんが、それらは ECMAScript 6に new Symbolプリミティブ型を含めることを計画しています(他のクレイジーなものは言うまでもありません)。私はいつもRubyの:symbol
の概念は無用だと思いました。 JavaScriptのように、代わりにプレーンな文字列を簡単に使用できます。そして今、彼らはそれを使ってJSのことを複雑にすることにしました。
やる気がわからない。 JavaScriptで本当にシンボルが必要かどうかを私に説明してもらえますか。
Javascriptにシンボルを導入する最初の動機は、privateプロパティを有効にすることでした。
残念ながら、彼らはひどく格下げされてしまいました。たとえばObject.getOwnPropertySymbols
やプロキシを使ってリフレクションを使って見つけることができるので、これらはもうプライベートではありません。
それらは現在ユニークシンボルとして知られており、それらの唯一の意図された用途はプロパティ間の名前衝突を避けることです。たとえば、ECMAScript自体は、ユーザー名と衝突する危険を冒さずに、オブジェクトに配置できる特定の方法で拡張フックを導入できるようになりました(たとえば、反復プロトコルの定義など)。
それが言語にシンボルを追加する動機付けが十分に強いかどうかは議論の余地があります。
シンボルは真のプライバシーを保証するものではありませんが、オブジェクトのパブリックプロパティと内部プロパティを区別するために使用できます。プライベートプロパティを持つために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
Symbols
name__は、オブジェクト内で一意のプロパティ名として使用できる新しい特別な種類のオブジェクトです。 Symbol
name__の代わりにstring
name__を使用すると、さまざまなモジュールが互いに競合しないプロパティを作成できます。 Symbols
name__を非公開にすることもできます。そのため、Symbol
name__に直接アクセスできない人がそのプロパティにアクセスすることはできません。
Symbols
name__は新しいプリミティブです。 number
name__、string
name__、およびboolean
name__プリミティブのように、Symbol
name__にはそれらを作成するために使用できる関数があります。他のプリミティブとは異なり、Symbols
name__にはリテラル構文はありません(例:string
name__の''
の作成方法) - それらを作成する唯一の方法は、以下のようにSymbol
name__コンストラクターを使用することです。
let symbol = Symbol();
実際には、Symbol
name__は、プロパティをオブジェクトにアタッチするためのわずかに異なる方法です - Symbols
name__を継承するすべてのものに現れるObject.prototype.hasOwnProperty
のように、標準メソッドとしてよく知られているObject
name__を簡単に提供できます。
Symbol
name__プリミティブ型の利点をいくつか紹介します。
Symbols
name__にはデバッグ機能が組み込まれていますSymbols
name__には説明を与えることができます。これは、コンソールにログインするときの動作を少し楽にするために、デバッグのためだけに使用されます。
Symbols
name__はObject
name__キーとして使用できますこれがSymbol
name__が本当におもしろくなるところです。それらは物と深く絡み合っています。 Symbol
name__はオブジェクトへのキーとして割り当てることができます。つまり、オブジェクトには無制限の数の一意のSymbol
name__を割り当てることができ、これらがstring
name__キーや他の一意のSymbols
name__と競合しないことが保証されます。
Symbols
name__は一意の値として使用できます。logger.levels.DEBUG
、logger.levels.INFO
、logger.levels.WARN
などの複数のログレベルを含むロギングライブラリがあるとしましょう。 ES5コードでは、これらのstring
name__s(so logger.levels.DEBUG === 'debug'
)、またはnumber
name__s(logger.levels.DEBUG === 10
)を作成します。これらの値は、一意の値ではないため理想的ではありませんが、Symbol
name__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');
この続きを読む すばらしい記事
この記事はSymbol()
についてのもので、私が見つけることができる実際の例と、見つけることができる事実と定義が付属しています。
Symbol()
は、ECMAScript 6(ES6)のリリースで導入されたデータ型です。
シンボルについて2つの興味深い事実があります。
リテラルを持たないJavaScriptの最初のデータ型と唯一のデータ型
Symbol()
で定義された変数はすべて一意のコンテンツを取得しますが、実際はそうではありません private 。
どのデータにも独自の記号があり、同じデータの場合、記号は同じになります。次の段落で詳細な情報があります。そうでなければそれはTLRDではありません。 :)
これはどちらの方法でも可能です。
var mySymbol1 = Symbol();
またはこのように:
var mySymbol2 = Symbol("some text here");
"some text here"
文字列はシンボルから抽出することはできません。デバッグ目的のための単なる説明です。 symbolの動作を変えることはありません。しかし、あなたはそれをconsole.log
することができます(それは他のログエントリとそのログを間違えないように、値はデバッグのためのものなので公平です):
console.log(mySymbol2);
// Symbol(some text here)
この場合、symbolの値は実際に考慮され、このように2つのシンボルは一意ではない可能性があります。
var a1 = Symbol.for("test");
var a2 = Symbol.for("test");
console.log(a1 == a2); //true!
それらのシンボルを「第2タイプ」のシンボルと呼びましょう。それらは、「第1タイプ」のシンボル(つまり、Symbol(data)
で定義されたもの)と交差しません。
次の2つの段落はfirst-type記号だけに関係します。
最初にオブジェクト、標準データ型を考えましょう。そこでキーと値のペアをいくつか定義し、キーを指定することで値にアクセスできます。
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
これが私の見方です。シンボルは、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パースペクティブ。
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