Czy "nowe" słowo kluczowe JavaScript jest uważane za szkodliwe?

W innym pytaniu użytkownik wskazał, że słowo kluczowe new jest niebezpieczne w użyciu i zaproponował rozwiązanie do tworzenia obiektów, które nie używają new. Nie wierzyłem, że to prawda, głównie dlatego, że używałem Prototype, Scriptaculous i innych doskonałych bibliotek JavaScript, a każdy z nich używał słowa kluczowego new.

Pomimo tego, wczoraj oglądałem przemówienie Douglasa Crockforda w YUI theater i powiedział dokładnie to samo. nie używał już w swoim kodzie słowa kluczowego new (Crockford on JavaScript-Act III: Function The Ultimate-50: 23 minut).

Czy używanie słowa kluczowego new jest "złe"? Jakie są zalety i wady korzystania z niego?

 575
Author: Community, 2008-12-20

12 answers

Crockford zrobił wiele, aby popularyzować dobre techniki JavaScript. Jego opinia na temat kluczowych elementów języka wywołała wiele użytecznych dyskusji. To powiedziawszy, jest zbyt wielu ludzi, którzy traktują każde głoszenie "złego" lub "szkodliwego" jako Ewangelię, odmawiając spojrzenia poza zdanie jednego człowieka. Czasami może to być trochę frustrujące.

Wykorzystanie funkcjonalności dostarczonej przez słowo kluczowe new ma kilka zalet w stosunku do budowania każdego obiektu z scratch:

  1. dziedziczenie prototypu . Chociaż często patrzył na mieszankę podejrzeń i szyderstw przez tych, którzy przyzwyczajeni są do języków oo opartych na klasach, natywna technika dziedziczenia JavaScript jest prostym i zaskakująco skutecznym sposobem ponownego użycia kodu. A nowe słowo kluczowe jest kanonicznym (i dostępnym tylko na wielu platformach) sposobem jego użycia.
  2. wydajność. Jest to efekt uboczny #1: Jeśli chcę dodać 10 metod do każdego tworzonego obiektu, mogę wystarczy napisać funkcję tworzenia, która ręcznie przypisuje każdą metodę do każdego nowego obiektu... Mogę też przypisać je do funkcji tworzenia prototype i użyć new, aby wytłoczyć nowe obiekty. Nie tylko jest to szybsze (nie wymaga kodu dla każdej metody na prototypie), ale pozwala uniknąć balonowania każdego obiektu z osobnymi właściwościami dla każdej metody. Na wolniejszych maszynach (a zwłaszcza wolniejszych interpretatorach JS), gdy powstaje wiele obiektów, może to oznaczać znaczną oszczędność czasu i pamięć.

I tak, new ma jedną zasadniczą wadę, umiejętnie opisaną przez inne odpowiedzi: jeśli zapomnisz jej użyć, twój kod pęknie bez ostrzeżenia. Na szczęście ta wada jest łatwo zniwelowana - wystarczy dodać trochę kodu do samej funkcji:

function foo()
{
   // if user accidentally omits the new keyword, this will 
   // silently correct the problem...
   if ( !(this instanceof foo) )
      return new foo();

   // constructor logic follows...
}

Teraz możesz mieć zalety new bez martwienia się o problemy spowodowane przypadkowym niewłaściwym użyciem. Możesz nawet dodać twierdzenie do sprawdzenia, czy myśl o złamanym kodzie cicho działającym przeszkadza ty. Lub, jak skomentowali niektórzy , użyj czeku, aby wprowadzić wyjątek runtime:

if ( !(this instanceof arguments.callee) ) 
   throw new Error("Constructor called as a function");

(zauważ, że ten fragment jest w stanie uniknąć kodowania nazwy funkcji konstruktora, ponieważ w przeciwieństwie do poprzedniego przykładu nie ma potrzeby tworzenia instancji obiektu-dlatego może być skopiowany do każdej funkcji docelowej bez modyfikacji.)

[[8]}John Resig szczegółowo omawia tę technikę w swoim poście prosta instancja "klasy" , a także zawiera środki budowanie tego zachowania w swoich "klasach" domyślnie. Zdecydowanie warto przeczytać... podobnie jak jego kolejna książka, Secrets of the JavaScript Ninja , która znajduje ukryte złoto w tym i wielu innych "szkodliwych" cechach języka JavaScript (rozdział {36]} on {[7] } jest szczególnie pouczający dla tych z nas, którzy początkowo odrzucili tę bardzo złośliwą funkcję jako sztuczkę).
 610
Author: Shog9,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:25

Właśnie przeczytałem kilka części jego książki Crockfords "Javascript: the Good Parts". Mam wrażenie, że uważa wszystko, co kiedykolwiek go ugryzło za szkodliwe: {]}

O switch fall through:

Nigdy nie pozwalam, aby sprawy switch upadły do następnej sprawy. Kiedyś znalazłem błąd w moim kodzie spowodowany przez / align = "left" / po energicznym przemówieniu o tym, dlaczego Upadek był czasem przydatne. (strona 97, ISBN 978-0-596-51774-8)

O ++ I --

The ++ (increment) and -- (decrement) operatorzy byli znani z przyczynić się do złego kodu poprzez zachęcanie ekscesywne oszustwo. Są drugie tylko do wadliwej architektury w włączanie wirusów i innych zabezpieczeń menaces. (strona 122)

O Nowym:

Jeśli zapomnisz dołączyć Nowy przedrostek przy wywołaniu konstruktora funkcji, wtedy to nie będzie bound to nowy obiekt. Niestety, to będzie związany z obiektem globalnym, więc zamiast powiększać swój nowy obiekt, będziesz trzepać globalnie zmienne. To jest naprawdę złe. Tam nie ma ostrzeżenia kompilacji i nie ma ostrzeżenie o uruchomieniu. (strona 49)

Jest ich więcej, ale mam nadzieję, że zrozumiesz. Moja odpowiedź na twoje pytanie: nie, to nie jest szkodliwe. ale jeśli zapomnisz go użyć, kiedy powinieneś, możesz mieć pewne problemy. Jeśli rozwijasz w dobrym otoczeniu można to zauważyć.

Update

Około rok po napisaniu tej odpowiedzi ukazała się piąta edycja ECMAScript, z obsługą strict mode. W trybie strict, {[0] } nie jest już związane z obiektem globalnym, ale z undefined.

 183
Author: some,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-25 16:59:40

Javascript jako dynamiczny język istnieje milion sposobów na bałagan tam, gdzie inny język by cię zatrzymał.

Unikanie podstawowych funkcji językowych, takich jak new na podstawie tego, że możesz zepsuć, jest trochę jak zdejmowanie nowych, błyszczących butów przed pójściem przez pole minowe, na wypadek, gdyby buty były zabłocone.

Używam konwencji, w której nazwy funkcji zaczynają się od małej litery, A 'funkcje', które są w rzeczywistości definicjami klas, zaczynają się od wielkiej litery list. Wynik jest naprawdę dość przekonującą wizualną wskazówką, że 'składnia' jest zła: - {]}

var o = MyClass();  // this is clearly wrong.

Poza tym dobre nawyki nazewnicze pomagają. W końcu wszystkie funkcje robią rzeczy i dlatego w jego nazwie powinien być czasownik, podczas gdy klasy reprezentują przedmioty i są rzeczownikami i przymiotnikami bez czasownika.

var o = chair() // Executing chair is daft.
var o = createChair() // makes sense.

Ciekawe jak kolorowanie składni so zinterpretowało powyższy kod.

 92
Author: AnthonyWJones,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-30 21:03:52

Jestem nowicjuszem w Javascript, więc może nie jestem zbyt doświadczony w dostarczaniu dobrego punktu widzenia. Chcę jednak podzielić się moim poglądem na ten" nowy " temat.

Pochodzę ze świata C#, gdzie użycie słowa kluczowego " new " jest tak naturalne, że to fabryczny wzorzec projektowy wygląda dla mnie dziwnie.

Kiedy po raz pierwszy koduję w Javascript, nie zdaję sobie sprawy, że istnieje" nowe " słowo kluczowe i Kod jak ten we wzorze YUI i nie zajmuje mi dużo czasu, aby wpaść w katastrofę. I lose śledzenie tego, co dana linia ma robić, patrząc wstecz na kod, który napisałem. Bardziej chaotyczne jest to, że mój umysł nie może tak naprawdę przejść między granicami instancji obiektów, kiedy jestem "suchy" kod.

Potem znalazłem" nowe " słowo kluczowe, które dla mnie "oddziela" rzeczy. Dzięki nowemu słowu kluczowemu tworzy rzeczy. Bez nowego słowa kluczowego wiem, że nie pomylę go z tworzeniem rzeczy, chyba że funkcja, którą wywołuję, da mi silne wskazówki.

Na przykład, z var bar=foo(); nie mam żadnych wskazówek, jaki może być bar.... Jest to wartość zwracana czy nowo utworzony obiekt? Ale z var bar = new foo(); wiem na pewno, że bar jest obiektem.

 41
Author: Conrad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-16 13:09:55

Kolejny przypadek dla nowy jest to, co nazywam Puchatek . Kubuś Puchatek podąża za brzuchem. Mówię: idź z językiem, którego używasz, a nie przeciwko temu.

Są szanse, że opiekunowie języka zoptymalizują język pod kątem idiomów, do których starają się zachęcać. Jeśli dodają nowe słowo kluczowe do języka, prawdopodobnie uważają, że ma to sens, aby być jasnym podczas tworzenia nowej instancji.

Kod napisany zgodnie z intencjami języka zwiększy wydajność z każdym wydaniem. A kod unikający kluczowych konstrukcji języka będzie cierpieć z czasem.

EDIT: a to znacznie wykracza poza wydajność. Nie mogę zliczyć razy, które słyszałem (lub powiedziałem) "dlaczego oni to zrobili ?"podczas znajdowania dziwnie wyglądającego kodu. Często okazuje się, że w momencie pisania kodu istniał jakiś "dobry" powód. Podążanie za Tao języka jest najlepszym ubezpieczeniem za to, że Twój kod nie wyśmiewa niektórych za wiele lat.

 39
Author: PEZ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-17 07:11:37

Napisałem post o tym, jak złagodzić problem wywołania konstruktora bez nowego słowa kluczowego.
Jest w większości dydaktyczny, ale pokazuje, jak można tworzyć konstruktory, które działają z new lub bez new i nie wymaga dodawania kodu kotła do testowania this w każdym konstruktorze.

Http://js-bits.blogspot.com/2010/08/constructors-without-using-new.html

Oto istota techniki:

/**
 * Wraps the passed in constructor so it works with
 * or without the new keyword
 * @param {Function} realCtor The constructor function.
 *    Note that this is going to be wrapped
 *    and should not be used directly 
 */
function ctor(realCtor){
  // This is going to be the actual constructor
  return function wrapperCtor(){
    var obj; // object that will be created
    if (this instanceof wrapperCtor) {
      // Called with new
      obj = this;
    } else {
      // Called without new. Create an empty object of the
      // correct type without running that constructor
      surrogateCtor.prototype = wrapperCtor.prototype;
      obj = new surrogateCtor();
    }
    // Call the real constructor function
    realCtor.apply(obj, arguments);
    return obj;
  }

  function surrogateCtor() {}
}

Oto jak go używać:

// Create our point constructor
Point = ctor(function(x,y){
  this.x = x;
  this.y = y;
});

// This is good
var pt = new Point(20,30);
// This is OK also
var pt2 = Point(20,30);
 24
Author: Juan Mendes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-24 18:28:17

Uzasadnienie braku użycia nowego słowa kluczowego jest proste:

Nie używając go w ogóle, unikasz pułapki, która przychodzi z przypadkowym pominięciem go. Wzór konstrukcyjny, którego używa YUI, jest przykładem tego, jak można całkowicie uniknąć nowego słowa kluczowego "
var foo = function () {
    var pub= { };
    return pub;
}
var bar = foo();

Alternatywnie możesz TAK:

function foo() { }
var bar = new foo();

Ale w ten sposób ryzykujesz, że ktoś zapomni użyć new, a Ten operator będzie cały fubar. AFAIK nie ma korzyści z robienia tego (poza tym, że jesteś do tego przyzwyczajony).

/ Align = "left" / możesz użyć nowego Oświadczenia? Tak. Czy to czyni Twój kod bardziej niebezpiecznym? Tak.

Jeśli kiedykolwiek pisałeś C++, jest to podobne do ustawiania wskaźników NA NULL po ich usunięciu.

 22
Author: Greg Dean,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-16 13:11:09

Myślę, że "nowy" dodaje jasności do kodu. A jasność jest warta wszystkiego. Dobrze wiedzieć, że są pułapki, ale unikanie ich przez unikanie jasności nie wydaje mi się sposobem.

 20
Author: PEZ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-20 16:12:11

Przypadek 1: new nie jest wymagany i należy go unikać

var str = new String('asd');  // type: object
var str = String('asd');      // type: string

var num = new Number(12);     // type: object
var num = Number(12);         // type: number

Case 2: new jest wymagane, w przeciwnym razie otrzymasz błąd

new Date().getFullYear();     // correct, returns the current year, i.e. 2010
Date().getFullYear();         // invalid, returns an error
 16
Author: nyuszika7h,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-26 17:27:58

Oto krótkie podsumowanie dwóch najsilniejszych argumentów za i przeciw używaniu operatora new:

Argument przeciw new

  1. funkcje przeznaczone do utworzone jako obiekty przy użyciu new operator może mieć katastrofalne efekty, jeśli są niepoprawne wywoływane jako normalne funkcje. A kod funkcji w takim przypadku będzie być wykonywane w zakresie, w którym funkcja jest wywoływana, zamiast w zakres obiektu lokalnego jako zamierzony. Może to spowodować global zmienne i właściwości do uzyskania nadpisane katastrofą konsekwencje.
  2. wreszcie pisanie function Func(), a następnie wywołanie Func.prototype i dodawanie do niego rzeczy, aby can call new Func() to construct Twój obiekt wydaje się brzydki dla niektórych programistów, którzy woleliby używać inny styl dziedziczenia obiektów dla architektury i stylistyki powody.

Aby dowiedzieć się więcej na ten temat, sprawdź świetną i zwięzłą książkę Douglasa Crockforda Javascript: dobre części. Zobacz też w każdym razie.

Argument na rzecz new

  1. używając operatora new wraz z zadanie prototypowe jest szybkie.
  2. to coś o przypadkowym uruchamianie funkcji konstruktora kod w globalnej przestrzeni nazw może łatwo zapobiegać, jeśli zawsze Dołącz trochę kodu do swojego funkcje konstruktora do sprawdzenia zobacz, czy są powoływani prawidłowo, a w przypadkach, gdy nie są, Obsługa połączenia odpowiednio, zgodnie z życzeniem.

Zobacz Jan Resig ' s post dla prostego wyjaśnienia tej techniki i ogólnie głębszego wyjaśnienia modelu dziedziczenia, który opowiada.

 12
Author: alegscogs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-30 19:06:01

Zgadzam się z pez i niektórymi tutaj.

Wydaje mi się oczywiste, że "nowe" jest samoopisowe tworzenie obiektów, gdzie wzór YUI opisany przez Grega Deana jest całkowicie przysłonięty .

Możliwość napisania var bar = foo; LUB var bar = baz();Gdzie baz nie jest metodą tworzenia obiektów wydaje się daleko bardziej niebezpieczna.

 9
Author: annakata,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-20 17:10:39

Myślę, że nowy jest zły, nie dlatego, że jeśli zapomnisz go użyć przez pomyłkę, może to spowodować problemy, ale dlatego, że psuje łańcuch dziedziczenia, co sprawia, że język jest trudniejszy do zrozumienia.

JavaScript jest zorientowany obiektowo w oparciu o prototyp. Stąd każdy obiekt musi być utworzony z innego obiektu, takiego jak var newObj=Object.create(oldObj). Tutaj oldObj nazywa się prototypem newObj (stąd "prototype-based"). Oznacza to, że jeśli właściwość nie zostanie znaleziona w newObj, to będzie wyszukano w oldObj . newObj domyślnie będzie pustym obiektem, ale ze względu na łańcuch prototypów wydaje się, że posiada wszystkie wartościoldObj .

Z drugiej strony, jeśli zrobisz var newObj=new oldObj(), prototyp newObj jest oldObj.prototyp , który jest niepotrzebnie trudny do zrozumienia.

The trick is to use

Object.create=function(proto){
  var F = function(){};
  F.prototype = proto;
  var instance = new F();
  return instance;
};

Jest wewnątrz tej funkcji i tylko tutaj Nowa powinna być używana. Po tym wystarczy użyć obiekt.metoda create () . Metoda rozwiązuje problem prototypu.

 8
Author: Mihir Gogate,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-30 23:38:11