Rentang kesalahan saat mengganti metode Object.prototype dengan fungsi konstruktor
Saya mencoba untuk mengganti Object.prototype.toString
dalam upaya untuk menambahkan fungsionalitas untuk deskripsi kelas tambahan.
Berikut kode awalnya:
(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);
Ketika saya menjalankan ini di konsol, saya mendapatkan output berikut:
[object TestClass]
Hal yang baik adalah bahwa itu tidak secara drastis mengubah cara Object.prototype.toString
kerjanya, jadi dengan tipe lain [yaitu bukan TestClass], semuanya bekerja seperti yang diharapkan misalnya Object.prototype.toString.call(12)
akan menampilkan [object Number]
.
Implementasi ini berfungsi tanpa masalah sejauh ini. Namun, saya memiliki implementasi lain dengan kode berikut:
(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);
Dengan ini, saya mendapatkan output yang tepat untuk TestClass, tetapi ketika saya menggunakan sesuatu yang lain, seperti 12
, saya mendapatkan 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)
Ini tampaknya menjadi masalah dengan rekursi toString.apply
. Namun, saya tidak tahu mengapa penerapan kedua ini berulang, jika yang pertama tidak?
Catatan : Alasan implementasi kedua ini adalah untuk menambahkan kode pemeriksaan tipe [yaitu if(this instanceof MyClassType){return '[object MyClassType]'}
] untuk kelas yang berbeda secara dinamis dari daftar nama kelas dalam larik. Dengan kata lain, daripada mengubah kode untuk setiap Kelas baru yang saya buat, saya menambahkan nama kelas ke array, dan pernyataan bersyarat dibuat secara otomatis.
Jawaban
Masalahnya adalah bahwa toString
parameter IIFE Anda tidak berada dalam cakupan new Function
kode Anda . Sebaliknya, ini menggunakan global toString
= window.toString
= Object.prototype.toString
.
Untuk memperbaikinya, Anda perlu mendeklarasikan toString
variabel di dalam new Function
kode agar penutupan yang dikembalikan berfungsi. Baik sebagai konstanta sederhana:
(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();
})();
atau sebagai 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);
// ^^^^^^^^^^^^^^^^^^^^^^^^^
})();