文字の出現により、オブジェクトのすべての文字を取得できる1つのメソッドでオブジェクト
Object
が拡張されました。
var role = Symbol('role'); var score = Symbol('score'); var id = 100; var name = 'Moderator'; var user = { id: id, name: name }; user[role] = 'admin'; user[score] = 50000; Object.getOwnPropertySymbols( user ); // [Symbol(role), Symbol(score)]
このメソッドが存在するため、真にプライベートなプロパティを作成することはできません。
ここで、たとえば、ユーザーロールを取得する方法:
var userSymbols = Object.getOwnPropertySymbols( user ); var roleSymbol = userSymbols[0]; user[ roleSymbol ]; // 'admin'
クライアント側でデータを非表示にできないという事実を考慮すると、この関数は、
Object.getOwnPropertyNames
を使用して
Object.getOwnPropertyNames
のすべてのプロパティ名のリストを取得する状況と同様に、文字のリストを取得するために必要な補助と見なされる必要があります
Object.getOwnPropertyNames
。
また、ECMAScript 6仕様には、JavaScriptでのリフレクション(またはリフレクション)を許可する新しい
Reflect
オブジェクトが追加されていることにも注意してください。 また、
Reflect
オブジェクトのメソッドの1つを使用すると、文字列と文字の両方で宣言されているすべてのプロパティを取得できます。
var properties = Reflect.ownKeys( user ); console.log( user ); // ['id', 'name', Symbol(role), Symbol(score)]
シンボルは、プロパティをパブリックと内部に分離する機会を提供します。これらは、内部情報を格納し、パブリックにアクセスするプロセスでのみ使用されます。
var Project = (function () { var projectData = Symbol('project'); var projectStatus = Symbol('status'); var _getTitle = Symbol('title'); function Project( data, status ) { this[ projectData ] = data; this[ projectStatus ] = status; } Project.prototype.getProjectTitle = function () { return this[ _getTitle ](); }; Project.prototype[ _getTitle ] = function () { return this[ projectData ].name + ' (' + this[ projectData ].description + ')'; }; Project.prototype.getStatus = function () { return this[ projectStatus ]; }; Project.prototype.changeStatus = function ( status ) { this[ projectStatus ] = status; }; return Project; }()); var project = new Project({ name: 'Application', description: 'favorite project' }, 'open'); console.log( project.getStatus() ); // 'open' project.changeStatus('finished'); console.log( project.getStatus() ); // 'finished' console.log( project.getProjectTitle() ); // 'Application (favorite project)'
この例では、プロジェクト情報とそのステータスがシンボルを介して追加されているため、直接読み取り/変更することはできません。 文字によってのみ参照できる内部
_getTitle
メソッドもあります。これは、パブリックAPIから非表示にし、
getProjectTitle
呼び出されたときにのみ使用されます。 また、シンボルにより、すべてのメソッドをオブジェクトのプロトタイプに入れることができました。
それでも、文字を追加する主な理由は、プライベートプロパティを作成する必要がないことです(ちなみに、このような可能性も提案されました- プライベート名オブジェクトですが、何らかの理由で仕様の現在のバージョンには含まれていません)。 シンボルが解決する主な問題は、一意のプロパティの作成です。 シンボルを使用するライブラリ開発者は、一意であることが保証されているプロパティを追加できます。これは、他のプロパティと競合しないことを意味します(他のモジュールによって追加されるプロパティをオーバーライドせず、ユーザーコードがこれらのプロパティを誤ってオーバーライドすることはできません)。
var Request = (function () { var requestState = Symbol('state'); var states = { NOT_INITIALIZED: Symbol(), RECEIVED: Symbol(), PROCESSING: Symbol(), FINISHED: Symbol() }; function Request() { this[ requestState ] = states.NOT_INITIALIZED; } Request.prototype.getStates = function () { return states; }; Request.prototype.close = function () { this[ requestState ] = states.FINISHED; }; Request.prototype.changeState = function ( state ) { this[ requestState ] = state; }; return Request; }()) var request = new Request(); var handledState = Symbol('state'); request[ handledState ] = false; // code request[ handledState ] = true; request.close();
上記のコードは、
Request
エンティティを提供するサードパーティモジュールがある場合の状況を示しています。 このモジュールは、シンボルを使用してモジュールが機能するために必要な
requestState
状態を定義します。 また、開発者は、シンボルを使用して自分の状態(たとえば、表示される状態や要求が処理される状態)に入ります。 文字を介してプロパティを宣言するこの方法により、モジュールの動作が再定義されないことが保証されます。
この例は、文字を使用する別の方法も提供します。
states
オブジェクトは、一意の値を持つ列挙型と見なすことができます(これにより、正しい値を確認または渡すことができます)。
次に、グローバル文字レジストリに文字を作成できるメソッドを見てみましょう。
コードは、このコードが宣言されているグローバルコンテキストで実行されますが、別のコンテキストでコードを実行する機能があり、これにより既にいくつかの問題が発生する可能性があります。 たとえば、ブラウザでは、iframeで宣言されたコードは異なるグローバルコンテキストを持ち、各iframeは
RegExp
、
Array
、
Date
などの独自のコピーを持ちます。 問題は、文字も存在し、宣言されたコンテキスト内でのみ一意であるということです。 コードがiframeのコードと相互作用する場合、このiframeには文字へのリンクがなく、これらの文字を介して宣言されるプロパティにアクセスできません。
この問題を解決するために、文字のグローバルレジストリが導入されました。 すべてのコンテキストで使用できるシンボルを作成するには、
Symbol.for
メソッドを使用する必要があります。
var UUIDSymbol = Symbol.for('uuid');
このメソッドは、グローバルレジストリから文字を取得するためにも使用されます。 メソッドのロジックは次のとおりです。シンボルが見つかった場合、メソッドの結果として返されます。 レジストリにシンボルが見つからない場合、作成され、グローバルレジストリに追加され、結果として返されます。
サーバー側でグローバルレジストリを使用する必要がある同様の状況があります。 たとえば、Node.jsには、異なるコンテキストでコードを実行できる
vm
モジュールがあります。
var vm = require('vm'); var ourArray = Array; var ourSymbol = Symbol('uuid'); var theirArray = vm.runInNewContext('Array'); var theirSymbol = vm.runInNewContext('Symbol("uuid")'); ourArray === theirArray; // false ourSymbol === theirSymbol; // false
現在のコンテキストの
Array
は、別のコンテキストの
Array
と等しくないことがわかります(それぞれに独自のコピーがあります)。 iframeとシンボルを使用した場合と同様の動作は、異なるコンテキストで互いに等しくありません。
var vm = require('vm'); var ourSymbol = Symbol.for('uuid'); var theirSymbol = vm.runInNewContext('Symbol.for("uuid")'); ourSymbol === theirSymbol; // true
Symbol.for
を介してシンボルを宣言する
Symbol.for
、すべてのコンテキストでそれらを使用する機会が得られます。 プロジェクトが特定のコンテキストでコードを実行する
vm
モジュールを使用している場合、
Symbol.for
を使用することはこの状況で優れたソリューションとなります。
レジストリにシンボルを追加するためのキーを取得できるメソッド、
Symbol.keyFor
あります。
var UUIDSymbolKey = Symbol.keyFor( UUIDSymbol );
指定された文字がレジストリで見つからない場合、
undefined
が返されます。
もう一度、
Symbol('score')
を使用してシンボルを作成しても、グローバルレジストリにシンボルが作成されないことに注意してください。
次の部分では、
Symbol
オブジェクトについて引き続き検討し、よく知られているシンボルの概念に触れ、それらがどのような機会を私たちにもたらすかを見ていきます。