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.
sumber
Make()
tanpanew
karena Make dikapitalisasi dan dengan demikian ia menganggap itu adalah konstruktornew
? 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.Jawaban:
Pertama
arguments.callee
tidak lagi digunakan dalam ES5, jadi kami tidak menggunakannya. Solusi sebenarnya agak sederhana.Kamu tidak menggunakan
new
sama sekali.Itu rasa sakit yang tepat di pantat kan?
Mencoba
enhance
Sekarang tentu saja ini membutuhkan ES5, tetapi semua orang menggunakan ES5-shim bukan?
Anda mungkin juga tertarik pola js OO alternatif
Sebagai tambahan Anda dapat mengganti opsi dua dengan
Jika Anda ingin ES5
Object.create
shim Anda sendiri maka itu sangat mudahsumber
Object.create
. Bagaimana dengan pre ES5? ES5-Shim terdaftarObject.create
sebagai DUBIOUS.__proto__
sesuatu di sana, maka kita masih pada titik yang sama. Karena sebelum ES5 tidak ada cara yang lebih mudah untuk bermutasi prototipe. Tapi bagaimanapun, solusi Anda tampaknya paling elegan dan berwawasan ke depan. +1 untuk itu. (batas pemungutan suara saya tercapai)Object.create
shim Anda cukup banyak solusi ketiga saya tetapi tidak terlalu rumit dan lebih tampan daripada milik saya tentu saja.Jawaban yang jelas adalah jangan lupa
new
kata kunci.Anda mengubah struktur dan makna bahasa.
Yang, menurut pendapat saya, dan demi pengelola kode Anda di masa mendatang, adalah ide yang mengerikan.
sumber
new
atau tidak, saya akan menemukan itu lebih dapat dikelola.;
pernyataan akhir. (Penyisipan Titik Koma Otomatis)new
atau tidak identik secara semantik . Ada yang tidak ada kasus halus di mana invarian ini rusak. Itu sebabnya bagus dan mengapa Anda ingin menggunakannya.Solusi termudah adalah dengan hanya mengingat
new
dan melempar kesalahan agar jelas Anda lupa.Apa pun yang Anda lakukan, jangan gunakan
eval
. Saya akan menghindar dari menggunakan properti non-standar seperti__proto__
khusus karena mereka non-standar dan fungsinya dapat berubah.sumber
.__proto__
adalah iblisSaya sebenarnya menulis posting tentang ini. http://js-bits.blogspot.com/2010/08/constructors-without-using-new.html
Dan Anda bahkan dapat menggeneralisasikannya sehingga Anda tidak perlu menambahkannya ke atas setiap konstruktor. Anda dapat melihatnya dengan mengunjungi posting saya
Penafian Saya tidak menggunakan ini dalam kode saya, saya hanya mempostingnya untuk nilai didaktik. Saya menemukan bahwa melupakan
new
adalah bug yang mudah dikenali. Seperti yang lain, saya tidak berpikir kita benar-benar membutuhkan ini untuk sebagian besar kode. Kecuali jika Anda menulis perpustakaan untuk membuat warisan JS, dalam hal ini Anda bisa menggunakan dari satu tempat dan Anda sudah akan menggunakan pendekatan yang berbeda dari warisan lurus ke depan.sumber
var x = new Ctor();
dan kemudian memiliki x sebagaithis
dan lakukanvar y = Ctor();
, ini tidak akan berperilaku seperti yang diharapkan.this
", bisakah Anda mengirim jsfiddle untuk menunjukkan masalah potensial?Ctor.call(ctorInstance, 'value')
. Saya tidak melihat skenario yang valid untuk apa yang Anda lakukan. Untuk membangun objek, Anda dapat menggunakanvar x = new Ctor(val)
atauvar y=Ctor(val)
. Bahkan jika ada skenario yang valid, klaim saya adalah bahwa Anda dapat memiliki konstruktor tanpa menggunakannew Ctor
, bukan bahwa Anda dapat memiliki konstruktor yang berfungsi menggunakanCtor.call
See jsfiddle.net/JHNcR/2Bagaimana dengan ini?
EDIT: Saya lupa menambahkan:
"Jika aplikasi Anda benar-benar 'berat', hal terakhir yang Anda inginkan adalah mekanisme konstruksi yang terlalu padat untuk memperlambatnya"
Saya benar-benar setuju - ketika membuat 'barang' di atas tanpa kata kunci 'baru', itu lebih lambat / lebih berat daripada dengan itu. Kesalahan adalah teman Anda, karena mereka memberi tahu Anda apa yang salah. Selain itu, mereka memberi tahu sesama pengembang Anda apa yang mereka lakukan salah.
sumber