Peringatan: Ini adalah posting yang panjang.
Mari kita tetap sederhana. Saya ingin menghindari awalan operator baru setiap kali saya memanggil konstruktor dalam JavaScript. Ini karena saya cenderung melupakannya, dan kode saya rusak parah.
Cara sederhana sekitar ini adalah ini ...
function Make(x) {
if ( !(this instanceof arguments.callee) )
return new arguments.callee(x);
// do your stuff...
}
Tapi, saya perlu ini untuk menerima variabel no. argumen, seperti ini ...
m1 = Make();
m2 = Make(1,2,3);
m3 = Make('apple', 'banana');
Solusi langsung pertama tampaknya menjadi metode 'terapkan' seperti ini ...
function Make() {
if ( !(this instanceof arguments.callee) )
return new arguments.callee.apply(null, arguments);
// do your stuff
}
Namun ini SALAH - objek baru diteruskan ke apply
metode dan BUKAN ke konstruktor kami arguments.callee
.
Sekarang, saya telah menghasilkan tiga solusi. Pertanyaan sederhana saya adalah: mana yang tampaknya terbaik. Atau, jika Anda memiliki metode yang lebih baik, katakan itu.
Pertama - gunakan eval()
untuk secara dinamis membuat kode JavaScript yang memanggil konstruktor.
function Make(/* ... */) {
if ( !(this instanceof arguments.callee) ) {
// collect all the arguments
var arr = [];
for ( var i = 0; arguments[i]; i++ )
arr.push( 'arguments[' + i + ']' );
// create code
var code = 'new arguments.callee(' + arr.join(',') + ');';
// call it
return eval( code );
}
// do your stuff with variable arguments...
}
Kedua - Setiap objek memiliki __proto__
properti yang merupakan tautan 'rahasia' ke objek prototipe-nya. Untungnya properti ini dapat ditulisi.
function Make(/* ... */) {
var obj = {};
// do your stuff on 'obj' just like you'd do on 'this'
// use the variable arguments here
// now do the __proto__ magic
// by 'mutating' obj to make it a different object
obj.__proto__ = arguments.callee.prototype;
// must return obj
return obj;
}
Ketiga - Ini adalah sesuatu yang mirip dengan solusi kedua.
function Make(/* ... */) {
// we'll set '_construct' outside
var obj = new arguments.callee._construct();
// now do your stuff on 'obj' just like you'd do on 'this'
// use the variable arguments here
// you have to return obj
return obj;
}
// now first set the _construct property to an empty function
Make._construct = function() {};
// and then mutate the prototype of _construct
Make._construct.prototype = Make.prototype;
eval
solusi tampaknya kikuk dan dilengkapi dengan semua masalah "eval jahat".__proto__
solusi tidak standar dan "Peramban Hebat mIsERY" tidak menghormatinya.Solusi ketiga tampaknya terlalu rumit.
Tetapi dengan ketiga solusi di atas, kita dapat melakukan sesuatu seperti ini, bahwa kita tidak dapat sebaliknya ...
m1 = Make();
m2 = Make(1,2,3);
m3 = Make('apple', 'banana');
m1 instanceof Make; // true
m2 instanceof Make; // true
m3 instanceof Make; // true
Make.prototype.fire = function() {
// ...
};
m1.fire();
m2.fire();
m3.fire();
Jadi secara efektif solusi di atas memberi kita konstruktor "benar" yang menerima variabel no. argumen dan tidak perlu new
. Apa pendapat Anda tentang ini.
- PEMBARUAN -
Beberapa mengatakan "just throw a error". Tanggapan saya adalah: kami sedang melakukan aplikasi berat dengan 10+ konstruktor dan saya pikir itu akan jauh lebih rumit jika setiap konstruktor dapat "dengan cerdas" menangani kesalahan itu tanpa melemparkan pesan kesalahan pada konsol.
Make()
tanpa new
karena Make dikapitalisasi dan dengan demikian ia menganggap itu adalah konstruktor
new
? Karena jika yang terakhir, Anda mungkin bertanya di situs yang salah. Jika yang pertama, Anda mungkin tidak ingin mengabaikan saran tentang menggunakan kesalahan baru dan mendeteksi begitu cepat ... Jika aplikasi Anda benar-benar "berat", hal terakhir yang Anda inginkan adalah beberapa mekanisme konstruksi yang terlalu padat untuk memperlambatnya. new
, untuk semua kekurangan yang didapat, cukup cepat.