ECMAScript6にシンボルができた理由 で紹介されている Symbol の使い方を見て、fmfm するためのエントリです。
似て非なる 2つの NativeObject 拡張ライブラリを同時に読み込むとどうなるか
例
Array に JSON.stringify(Array)
相当の機能を拡張するライブラリが2つ(AwesomeArray.js, ExArray.js)あります。これらを読み込むと Array#toJSON が拡張されます。
AwesomeArray.js と ExArray.js には、カンマの後ろにスペースがある/ない という違いがあります。
ES5 式
ES5 で書かれた AwesomeArray.js と ExArray.js です。
ES5 までは、AwesomeArray.js を読み込んだ後に ExArray.js を読み込むと、AwesomeArray.js で拡張した Array#toJSON が ExArray.js のそれに上書きされてしまいます。
AwesomeArray.js と ExArray.js が混在してしまう状況では、最終的にどちらが機能するかは、読み込み順に依存することになりカオスでした。
// AwesomeArray.js
(function(global) {
Array.prototype.toJSON = function() {
return "[" + this.join(",") + "]"; // without space
};
})(this);
// ExArray.js
(function(global) {
Array.prototype.toJSON = function() {
return "[" + this.join(", ") + "]"; // with space
};
})(this);
var array = [1,2,3];
console.log(array.toJSON()); // -> [1, 2, 3] (ExArray.js の Array#toJSON が機能している)
ES6 式
ES6 からは、Symbol を使う事でこれら2つのライブラリが共存可能になり、呼び分ける事も可能になります。
// AwesomeArray.js
(function(global) {
// シンボルを作成します。文字列 "AwesomeArray Array#toJSON" はデバッグ時の識別用です
var toJSON = Symbol("AwesomeArray Array#toJSON");
// JSON.stringify(Array) 相当のメソッドを追加します。
// Symbol で追加するため for in などの enum ではメソッド名は列挙されません
Array.prototype[toJSON] = function() {
return "[" + this.join(",") + "]"; // without space
};
// Symbol を export しライブラリの外から呼び出し可能にします
global.AwesomeArray = { "toJSON": toJSON }; // export Symbol
})(this);
// ExArray.js
(function(global) {
// シンボルを作成します。文字列 "ExArray Array#toJSON" はデバッグ時の識別用です
var toJSON = Symbol("ExArray Array#toJSON");
// JSON.stringify(Array) 相当のメソッドを追加します。
// Symbol で追加するため for in などの enum ではメソッド名は列挙されません
Array.prototype[toJSON] = function() {
return "[" + this.join(", ") + "]"; // with space
};
// Symbol を export しライブラリの外から呼び出し可能にします
global.ExArray = { "toJSON": toJSON }; // export Symbol
})(this);
Test
AwesomeArray.js と ExArray.js を読み込み、
AwesomeArray.js で追加した Array#toJSON と ExArray.js で追加した Array#toJSON が共存できているかテストします。
require("AwesomeArray.js");
require("ExArray.js");
var array = [1,2,3];
var toJSON = AwesomeArray.toJSON;
console.log( array[AwesomeArray.toJSON]() ); // -> "[1,2,3]"
console.log( array[ExArray.toJSON]() ); // -> "[1, 2, 3]"
console.log( array[toJSON]() ); // -> "[1,2,3]"
Object.getOwnPropertySymbols
for ... in
や Object.getOwnPropertyNames
で Symbol が見えない事を確認します
console.log( Object.getOwnPropertyNames(Array.prototype) );
// -> [ "length", "constructor", "toString", "toLocaleString",
// "join", "pop", "push", "concat", "reverse", "shift",
// "unshift", "slice", "splice", "sort", "filter", "forEach",
// "some", "every", "map", "indexOf", "lastIndexOf", "reduce",
// "reduceRight", "entries", "keys", "copyWithin", "find",
// "findIndex", "fill", "includes" ]
Object.getOwnPropertySymbols
で、Symbol を可視化できます。
console.log( Object.getOwnPropertySymbols(Array.prototype) );
// -> [ Symbol(Symbol.unscopables),
// Symbol(Symbol.iterator),
// Symbol(AwesomeArray Array#toJSON), <- 追加されている
// Symbol(ExArray Array#toJSON) ] <- 追加されている