问题
I'm trying to override Object.prototype.toString
in a bid to add functionality for additional class descriptions.
Here's the initial code:
(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);
When I run this in the console, I get the following output:
[object TestClass]
The good thing is that it doesn't drastically modify the way Object.prototype.toString
works, so with another type [i.e. not TestClass], things work just as expected e.g. Object.prototype.toString.call(12)
will output [object Number]
.
This implementation works with no issues so far. However, I have another implementation with the following code:
(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);
With this, I get the proper output for TestClass, but when I use something else, like 12
, I get a 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)
This appears to be an issue with recursion of toString.apply
. However, I can't figure out why this second implementation is recursing, if the first one does not?
Note: The reason for this second implementation is to add the type-checking code [i.e. if(this instanceof MyClassType){return '[object MyClassType]'}
] for different classes dynamically from a list of class names in an array. In other words, rather than modifying code for each new Class I come up with, I append the class name to the array instead, and the conditional statement is generated automatically.
回答1:
The problem is that the toString
parameter of your IIFE is not in scope in your new Function
code. Instead, it uses the global toString
= window.toString
= Object.prototype.toString
.
To fix this, you need to declare the toString
variable inside the new Function
's code to make the returned closure work. Either as a simple constant:
(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();
})();
or as a parameter:
(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);
// ^^^^^^^^^^^^^^^^^^^^^^^^^
})();
来源:https://stackoverflow.com/questions/65076114/range-error-when-overriding-object-prototype-method-with-function-constructor