Ostrzeżenie: To jest długi post.
Uprośćmy to. Chcę uniknąć konieczności dodawania nowego operatora za każdym razem, gdy wywołuję konstruktor w JavaScript. Jest tak, ponieważ zwykle zapominam o tym, a mój kod źle się psuje.
Prostym sposobem na obejście tego jest ...
function Make(x) {
if ( !(this instanceof arguments.callee) )
return new arguments.callee(x);
// do your stuff...
}
Ale potrzebuję tego, aby zaakceptować zmienną nr. takich argumentów ...
m1 = Make();
m2 = Make(1,2,3);
m3 = Make('apple', 'banana');
Pierwszym natychmiastowym rozwiązaniem wydaje się być metoda „zastosuj” w ten sposób ...
function Make() {
if ( !(this instanceof arguments.callee) )
return new arguments.callee.apply(null, arguments);
// do your stuff
}
Jest to NIEPRAWIDŁOWE - nowy obiekt jest przekazywany do apply
metody, a NIE do naszego konstruktora arguments.callee
.
Teraz wymyśliłem trzy rozwiązania. Moje proste pytanie brzmi: które wydaje się najlepsze. Lub, jeśli masz lepszą metodę, powiedz to.
Po pierwsze - użyj eval()
do dynamicznego tworzenia kodu JavaScript, który wywołuje 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...
}
Po drugie - każdy obiekt ma __proto__
właściwość, która jest „tajnym” łączem do jego obiektu prototypowego. Na szczęście ta właściwość jest zapisywalna.
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;
}
Po trzecie - jest to coś podobnego do drugiego rozwiązania.
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
rozwiązanie wydaje się niezdarne i wiąże się ze wszystkimi problemami „ewolucji zła”.__proto__
rozwiązanie jest niestandardowe, a „Great Browser of mIsERY” go nie honoruje.Trzecie rozwiązanie wydaje się zbyt skomplikowane.
Ale dzięki wszystkim powyższym trzem rozwiązaniom możemy zrobić coś takiego, czego nie możemy inaczej ...
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();
Tak skutecznie powyższe rozwiązania dają nam „prawdziwe” konstruktory, które akceptują zmienną nr. argumentów i nie wymagają new
. Jakie jest twoje zdanie na ten temat?
-- AKTUALIZACJA --
Niektórzy powiedzieli „po prostu wyrzuć błąd”. Moja odpowiedź brzmi: robimy ciężką aplikację z ponad 10 konstruktorami i myślę, że byłoby znacznie bardziej przydatne, gdyby każdy konstruktor mógł „mądrze” poradzić sobie z tym błędem bez wyświetlania komunikatów o błędach na konsoli.
źródło
Make()
beznew
ponieważ Marka jest aktywowane i dlatego zakłada, że jest to konstruktornew
? Ponieważ jeśli to jest to drugie, prawdopodobnie pytasz na niewłaściwej stronie. Jeśli jest to pierwsze, możesz nie odrzucić sugestii dotyczących korzystania z nowych i wykrywania błędów tak szybko ... Jeśli Twoja aplikacja jest naprawdę „ciężka”, ostatnią rzeczą, jakiej chcesz, jest jakiś nadmierny mechanizm konstrukcyjny, który ją spowolni.new
, dla całego tego, co dostaje, jest dość szybkie.Odpowiedzi:
Po pierwsze
arguments.callee
jest przestarzałe w ES5 strict, więc go nie używamy. Prawdziwe rozwiązanie jest raczej proste.W ogóle nie używasz
new
.To właściwy ból w dupie, prawda?
Próbować
enhance
Teraz oczywiście wymaga to ES5, ale wszyscy używają podkładki ES5, prawda?
Możesz być także zainteresowany alternatywnymi wzorami OO js
Na marginesie możesz zastąpić opcję drugą
Jeśli chcesz mieć własną
Object.create
podkładkę ES5, jest to naprawdę łatweźródło
Object.create
. Co z wersją wcześniejszą niż ES5? ES5-Shim podaje sięObject.create
jako WĄTPLIWE.__proto__
tam czegoś, to wciąż jesteśmy w tym samym punkcie. Ponieważ przed ES5 nie ma łatwiejszego sposobu mutowania prototypu. W każdym razie Twoje rozwiązanie wydaje się najbardziej eleganckie i wybiegające w przyszłość. +1 za to. (mój limit głosowania został osiągnięty)Object.create
podkładka to właściwie moje trzecie rozwiązanie, ale mniej skomplikowane i lepiej wyglądające niż moje.Oczywistą odpowiedzią byłoby, nie zapomnij
new
słowa kluczowego.Zmieniasz strukturę i znaczenie języka.
Co, moim zdaniem, i ze względu na przyszłych opiekunów twojego kodu, to okropny pomysł.
źródło
new
czy nie, uważałbym to za łatwiejsze do utrzymania.;
się instrukcje zakończenia. (Automatyczne wstawianienew
lub nie jest semantycznie identyczne . Nie są żadne subtelne przypadki, w których to niezmienna jest zepsuty. Dlatego jest dobry i dlaczego chcesz go używać.Najłatwiejszym rozwiązaniem jest zapamiętanie
new
i zgłoszenie błędu, aby było oczywiste, że zapomniałeś.Cokolwiek robisz, nie używaj
eval
. Odwróciłbym się od korzystania z niestandardowych właściwości, na przykład__proto__
dlatego, że są one niestandardowe, a ich funkcjonalność może ulec zmianie.źródło
.__proto__
to diabełNapisałem o tym post. http://js-bits.blogspot.com/2010/08/constructors-without-using-new.html
Możesz nawet go uogólnić, abyś nie musiał dodawać tego na szczycie każdego konstruktora. Możesz to zobaczyć odwiedzając mój post
Oświadczenie Nie używam tego w moim kodzie, opublikowałem go tylko dla wartości dydaktycznej. Przekonałem się,
new
że łatwo zauważyć błąd. Podobnie jak inni, nie sądzę, że tak naprawdę potrzebujemy tego w przypadku większości kodów. Chyba że piszesz bibliotekę do tworzenia dziedziczenia JS, w takim przypadku możesz użyć jednego miejsca i już będziesz używać innego podejścia niż dziedziczenie bezpośrednie.źródło
var x = new Ctor();
a potem mam X tak jakthis
i robięvar y = Ctor();
, to nie działałoby zgodnie z oczekiwaniami.this
”, czy możesz opublikować jsfiddle, aby pokazać potencjalny problem?Ctor.call(ctorInstance, 'value')
. Nie widzę prawidłowego scenariusza dla tego, co robisz. Aby zbudować obiekt, użyjvar x = new Ctor(val)
albovar y=Ctor(val)
. Nawet jeśli istniał prawidłowy scenariusz, moim twierdzeniem jest, że możesz mieć konstruktory bez użycianew Ctor
, nie że możesz mieć konstruktory, które działają przy użyciuCtor.call
Zobacz jsfiddle.net/JHNcR/2Co powiesz na to?
EDYCJA: Zapomniałem dodać:
„Jeśli twoja aplikacja jest naprawdę„ ciężka ”, ostatnią rzeczą, jakiej chcesz, jest jakiś przekręcony mechanizm konstrukcyjny, który ją spowolni”
Absolutnie się zgadzam - podczas tworzenia „rzeczy” powyżej bez słowa kluczowego „nowy” jest wolniejszy / cięższy niż przy nim. Błędy są twoim przyjacielem, ponieważ mówią ci, co jest nie tak. Co więcej, mówią swoich kolegów programistów co oni robi źle.
źródło