関数コンストラクターでObject.prototypeメソッドをオーバーライドするときの範囲エラー
Object.prototype.toString
クラスの説明を追加する機能を追加するために、オーバーライドしようとしています。
初期コードは次のとおりです。
(function(toString){
Object.prototype.toString = function(){
if(this instanceof TestClass)
{
return '[object TestClass]';
}
return toString.apply(this, arguments);
}
})(Object.prototype.toString);
function TestClass(){}
var instance_obj = new TestClass();
Object.prototype.toString.call(instance_obj);
これをコンソールで実行すると、次の出力が得られます。
[object TestClass]
良い点は、Object.prototype.toString
動作方法が大幅に変更されないことです。そのため、別のタイプ(つまり、TestClassではない)では、期待どおりObject.prototype.toString.call(12)
に動作します(出力など)[object Number]
。
この実装は、これまでのところ問題なく機能します。ただし、次のコードを使用した別の実装があります。
(function(toString){
var fn_code_str = `return function(){
if(this instanceof TestClass)
{
return '[object TestClass]';
}
return toString.apply(this, arguments);
}`;
var pre_fn = new Function(fn_code_str);
Object.prototype.toString = pre_fn();
})(Object.prototype.toString);
function TestClass(){}
var instance_obj = new TestClass();
Object.prototype.toString.call(instance_obj);
これにより、TestClassの適切な出力が得られますが、のような他のものを使用すると、12
RangeErrorが発生します。
VM527:5 Uncaught RangeError: Maximum call stack size exceeded
at Function.[Symbol.hasInstance] (<anonymous>)
at Number.eval (eval at <anonymous> (getElements.html:19), <anonymous>:5:21)
at Number.eval (eval at <anonymous> (getElements.html:19), <anonymous>:10:29)
at Number.eval (eval at <anonymous> (getElements.html:19), <anonymous>:10:29)
at Number.eval (eval at <anonymous> (getElements.html:19), <anonymous>:10:29)
at Number.eval (eval at <anonymous> (getElements.html:19), <anonymous>:10:29)
at Number.eval (eval at <anonymous> (getElements.html:19), <anonymous>:10:29)
at Number.eval (eval at <anonymous> (getElements.html:19), <anonymous>:10:29)
at Number.eval (eval at <anonymous> (getElements.html:19), <anonymous>:10:29)
at Number.eval (eval at <anonymous> (getElements.html:19), <anonymous>:10:29)
これは、の再帰に関する問題のようですtoString.apply
。ただし、最初の実装が繰り返されないのに、なぜこの2番目の実装が繰り返されるのか理解できません。
注:この2番目の実装の理由if(this instanceof MyClassType){return '[object MyClassType]'}
は、配列内のクラス名のリストから、さまざまなクラスの型チェックコード[つまり]を動的に追加するためです。つまり、新しいクラスごとにコードを変更するのではなく、代わりにクラス名を配列に追加すると、条件ステートメントが自動的に生成されます。
回答
問題はtoString
、IIFEのパラメーターがnew Function
コードのスコープ内にないことです。代わりに、グローバル使用していますtoString
=をwindow.toString
= Object.prototype.toString
。
これを修正するには、返されたクロージャを機能させるために、コードtoString
内で変数を宣言する必要がありますnew Function
。単純な定数として:
(function() {
var pre_fn = new Function(`
const toString = Object.prototype.toString;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
return function(){
if(this instanceof TestClass) {
return '[object TestClass]';
}
return toString.apply(this, arguments);
}`);
Object.prototype.toString = pre_fn();
})();
またはパラメータとして:
(function() {
var pre_fn = new Function('toString', `
// ^^^^^^^^^^^
return function(){
if(this instanceof TestClass) {
return '[object TestClass]';
}
return toString.apply(this, arguments);
}`);
Object.prototype.toString = pre_fn(Object.prototype.toString);
// ^^^^^^^^^^^^^^^^^^^^^^^^^
})();