Czy „nowe” słowo kluczowe JavaScript jest uważane za szkodliwe? [Zamknięte]


568

W innym pytaniu użytkownik wskazał, że newużycie słowa kluczowego jest niebezpieczne, i zaproponował rozwiązanie do tworzenia obiektów, które nie było używane new. Nie wierzyłem, że to prawda, głównie dlatego, że użyłem Prototype, Scriptaculous i innych doskonałych bibliotek JavaScript i wszyscy używali tego newsłowa kluczowego.

Mimo to wczoraj oglądałem przemówienie Douglasa Crockforda w teatrze YUI i powiedział dokładnie to samo, że nie używał newjuż tego słowa kluczowego w swoim kodzie ( Crockford w JavaScript - Akt III: Funkcja ostateczna - 50:23 minut ).

Czy użycie newsłowa kluczowego jest „złe” ? Jakie są zalety i wady korzystania z niego?


90
Używanie nowego słowa kluczowego NIE jest „złe”. Ale jeśli o tym zapomnisz, będziesz wywoływał konstruktor obiektów jako zwykłą funkcję. Jeśli twój konstruktor nie sprawdzi kontekstu wykonywania, nie zauważy, że „to” wskazuje na inny obiekt (zwykle obiekt globalny) zamiast na nową instancję. Dlatego twój konstruktor będzie dodawał właściwości i metody do obiektu globalnego (okna). Jeśli zawsze sprawdzasz, że „to” jest instancją twojego obiektu w funkcji obiektu, to nigdy nie będziesz miał tego problemu.
Kristian B

5
Nie rozumiem tego Z jednej strony doug odradza stosowanie new. Ale kiedy spojrzysz na bibliotekę YUI. Musisz używać newwszędzie. Takich jak var myDataSource = new Y.DataSource.IO({source:"./myScript.php"}); .
aditya_gaur

2
@aditya_gaur To dlatego, że jeśli potrzebujesz jakiejś inicjalizacji obiektu, musisz zhakować initmetodę, jeśli używasz tego Object.createpodejścia i wywołać to później. Znacznie łatwiejszy w użyciu, newktóry robi oba, ustawia łańcuch prototypów i wywołuje kod inicjujący.
Juan Mendes,

67
Nie sądzę, że to powinno być zamknięte. Tak, prawdopodobnie zainspiruje jad Crockforda przeciwko fanom, ale mówimy o popularnych radach, aby w zasadzie uniknąć tutaj ważnej funkcji językowej. Mechanizm, który powoduje, że każdy obiekt JQuery waży prawie nic (w przypadku pamięci) wymaga użycia słowa kluczowego „new”. Preferuj metody fabryczne, aby stale przywoływać nowe, ale nie zmniejszaj drastycznie opcji architektonicznych i potencjału wydajności, używając tylko literałów obiektowych. Mają swoje miejsce, a konstruktorzy mają swoje miejsce. Jest to opatrzona datą i kiepska rada.
Erik Reppen

2
TLDR: Używanie newnie jest niebezpieczne. Pominięcie newjest niebezpieczne, a zatem złe . Ale w ES5 możesz użyć trybu ścisłego , który chroni cię przed tym niebezpieczeństwem i wieloma innymi.
jkdev

Odpowiedzi:


603

Crockford zrobił wiele, aby spopularyzować dobre techniki JavaScript. Jego opinie na temat kluczowych elementów języka wywołały wiele przydatnych dyskusji. To powiedziawszy, jest zdecydowanie zbyt wielu ludzi, którzy traktują każde głoszenie „złego” lub „szkodliwego” jako ewangelii, odmawiając spojrzenia poza opinię jednego człowieka. Czasami może to być trochę frustrujące.

Korzystanie z funkcji newsłowa kluczowego ma kilka zalet w porównaniu z budowaniem każdego obiektu od zera:

  1. Dziedziczenie prototypów . Choć często przywoływana jest przez podejrzliwość i szyderstwa osób przyzwyczajonych do klasowych języków OO, natywna technika dziedziczenia JavaScript jest prostym i zaskakująco skutecznym sposobem ponownego wykorzystania kodu. Nowe słowo kluczowe to kanoniczny (i dostępny tylko na różnych platformach) sposób jego użycia.
  2. Wydajność. Jest to efekt uboczny nr 1: jeśli chcę dodać 10 metod do każdego obiektu, który tworzę, mógłbym po prostu napisać funkcję tworzenia, która ręcznie przypisuje każdą metodę do każdego nowego obiektu ... Lub mógłbym przypisać je do właściwości funkcje tworzenia prototypei używać newdo wybijania nowych obiektów. Jest to nie tylko szybsze (nie wymaga kodu dla każdej metody w prototypie), ale również pozwala uniknąć balonowania każdego obiektu z osobnymi właściwościami dla każdej metody. W przypadku wolniejszych maszyn (a zwłaszcza wolniejszych interpreterów JS), gdy tworzonych jest wiele obiektów, może to oznaczać znaczne oszczędności czasu i pamięci.

I tak, newma jedną istotną wadę, dobrze opisaną innymi odpowiedziami: jeśli zapomnisz go użyć, Twój kod złamie się bez ostrzeżenia. Na szczęście tę wadę można łatwo złagodzić - 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 korzystać z zalet, newnie martwiąc się o problemy spowodowane przypadkowym niewłaściwym użyciem. Możesz nawet dodać twierdzenie do czeku, jeśli przeszkadza ci myśl o uszkodzonym kodzie cicho działającym. Lub, jak niektórzy komentowali, użyj zaznaczenia, aby wprowadzić wyjątek czasu wykonywania:

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

(Pamiętaj, że ten fragment kodu pozwala uniknąć zakodowania na stałe nazwy funkcji konstruktora, ponieważ w przeciwieństwie do poprzedniego przykładu nie ma potrzeby rzeczywistego tworzenia instancji obiektu - dlatego można go skopiować do każdej funkcji docelowej bez modyfikacji.)

John Resig szczegółowo omawia tę technikę w swoim prostym poście „Tworzenie klasy” , a także domyślnie włącza sposoby budowania tego zachowania w „klasach”. Na pewno warto przeczytać ... jak jego zbliżający się książka Tajemnice JavaScript Ninja , który odnajduje ukryte złoto w tej i wielu innych „szkodliwe” cech języka JavaScript (the rozdział na withszczególnie oświecania dla tych z nas, którzy początkowo odrzucona ta bardzo złośliwa funkcja jako chwyt).


96
if (! (this instanceof arguments.callee)) zgłasza błąd („Konstruktor wywoływany jako funkcja”); // Bardziej ogólny, nie wymaga znajomości nazwy konstruktorów, niech użytkownik naprawi kod.
jakieś

5
Jeśli martwisz się wydajnością - i rzeczywiście masz powód - nie rób tego wcale. Pamiętaj, aby użyć newlub użyj funkcji otoki, aby zapamiętać ją dla Ciebie. I podejrzewam , że jeśli jesteś w miejscu, gdzie ma to znaczenie, już wyczerpane inne optymalizacje, takie jak memoization, dzięki czemu tworzenie połączeń będą zlokalizowane w każdym razie ...
Shog9

65
Używanie arguments.calleedo sprawdzania, czy zostałeś wezwany, newmoże nie być tak dobre, ponieważ arguments.calleenie jest dostępne w trybie ścisłym. Lepiej użyć nazwy funkcji.
Sean McMillan,

69
Jeśli źle wpisuję identyfikator, mój kod się psuje. Czy nie powinienem używać identyfikatorów? Nieużywanie, newponieważ możesz zapomnieć o napisaniu go w kodzie, jest tak samo niedorzeczne jak mój przykład.
Thomas Eding,

16
Jeśli włączysz tryb ścisły, jeśli zapomnisz użyć nowego, otrzymasz wyjątek, gdy spróbujesz użyć tego: yuiblog.com/blog/2010/12/14/strict-mode-is-coming-to-town To jest łatwiejsze niż dodanie wystąpienia sprawdzania do każdego konstruktora.
stephenbez

182

Właśnie przeczytałem niektóre 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 przełączniku spadają:

Nigdy nie pozwalam, aby skrzynki przełączników przechodziły do ​​następnej skrzynki. Kiedyś znalazłem błąd w moim kodzie spowodowany niezamierzonym upadkiem natychmiast po tym, jak wygłosiłem energiczną mowę o tym, dlaczego upadek był czasem przydatny. (strona 97, ISBN 978-0-596-51774-8)

Informacje o ++ i -

Wiadomo, że operatory ++ (inkrementacja) i - (dekrementacja) przyczyniają się do złego kodu, zachęcając do nadmiernej podstępności. Są drugim po wadliwej architekturze włączaniem wirusów i innych zagrożeń bezpieczeństwa. (strona 122)

O nowym:

Pominięcie zastosowania obejmują nowy prefiks podczas wywoływania funkcji konstruktora, wtedy to nie będzie zobowiązany do nowego obiektu. Niestety, ten będzie związany z globalnym obiektu, więc zamiast zwiększając swój nowy obiekt, będzie clobbering zmiennych globalnych. To naprawdę źle. Nie ma ostrzeżenia kompilacji ani ostrzeżenia środowiska wykonawczego. (strona 49)

Jest ich więcej, ale mam nadzieję, że dostaniesz zdjęcie.

Moja odpowiedź na twoje pytanie: nie, to nie jest szkodliwe. ale jeśli zapomnisz go użyć, możesz mieć problemy. Jeśli rozwijasz się w dobrym środowisku, zauważysz to.

Aktualizacja

Około rok po napisaniu tej odpowiedzi wydano 5. edycję ECMAScript ze wsparciem dla trybu ścisłego . W trybie ścisłym thisnie jest już związany z obiektem globalnym, ale z undefined.


3
Całkowicie się zgadzam. Rozwiązanie: Zawsze dokumentuj, w jaki sposób użytkownicy muszą tworzyć instancje twoich obiektów. Użyj przykładu, a użytkownicy prawdopodobnie będą wycinać / wklejać. Istnieją konstrukcje / funkcje w każdym języku, które mogą być niewłaściwie używane, co powoduje nietypowe / nieoczekiwane zachowanie. To nie czyni ich szkodliwymi.
nicerobot,

45
Istnieje konwencja, aby zawsze uruchamiać konstruktory dużymi literami, a wszystkie inne funkcje małymi literami.
jakieś

61
Właśnie zdałem sobie sprawę, że Crockford nie uważa, GDY jest szkodliwy ... Nie wiem, ile razy utworzyłem pętlę bezokolicznika, ponieważ zapomniałem inkrementować zmienną ...
jakieś

5
To samo dotyczy ++, -. Wyrażają dokładnie to, co zamierzam, w najczystszym możliwym języku. Kocham je! przełączanie upadków może być dla niektórych jasne, ale jestem tym zmęczony. Używam ich, kiedy są wyraźnie wyraźniejsze („bo kalambur nie mógł się oprzeć”).
PEZ,

30
Moja odpowiedź dla Crockforda: programowanie jest trudne, chodźmy na zakupy. Do licha!
Jason Jackson,

91

JavaScript jest dynamicznym językiem, istnieje mnóstwo sposobów na zepsucie się w miejscu, w którym zatrzyma Cię inny język.

Unikanie podstawowej funkcji językowej, na przykład newpowodowania bałaganu, przypomina trochę zdejmowanie nowych błyszczących butów przed przejściem przez pole minowe na wypadek, gdyby buty się zabrudziły.

Używam konwencji, w której nazwy funkcji zaczynają się od małej litery, a „funkcje”, które są definicjami klas, zaczynają się od dużej litery. Rezultatem jest naprawdę bardzo przekonująca wizualna wskazówka, że ​​„składnia” jest nieprawidłowa: -

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

Oprócz tego pomagają dobre nawyki nazywania. Po tym, jak wszystkie funkcje działają, dlatego w nazwie powinien znajdować się czasownik, podczas gdy klasy reprezentują obiekty i są rzeczownikami i przymiotnikami bez czasownika.

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

Interesujące jest to, w jaki sposób kolorystyka składniowa SO zinterpretowała powyższy kod.


8
Tak, myślałem właśnie o kolorowaniu składni.
BobbyShaftoe,

4
Kiedyś zapomniałem wpisać słowo „funkcja”. Tego słowa należy unikać za wszelką cenę. Innym razem nie użyłem nawiasów klamrowych w wielowierszowej instrukcji if / then. Więc teraz nie używam nawiasów klamrowych i po prostu piszę jednowierszowe warunkowe.
Hal50000,

„To jest wyraźnie złe” . Dlaczego? W przypadku moich klas (które po prostu robią if (! this instanceof MyClass) return new MyClass()) wolę newskładnię bez użycia. Dlaczego? Ponieważ funkcje są bardziej ogólne niż funkcje konstruktora . Opuszczenie newułatwia zmianę funkcji konstruktora na funkcję zwykłą ... Lub na odwrót. Dodanie newpowoduje, że kod jest bardziej szczegółowy niż powinien. A zatem mniej elastyczny. Porównaj z Javą, gdzie zaleca się zaakceptować Listzamiast, ArrayListaby osoba dzwoniąca mogła wybrać implementację.
Stijn de Witt

41

Jestem początkującym w Javascript, więc może nie mam zbyt dużego doświadczenia w zapewnianiu dobrego punktu widzenia na ten temat. Jednak chcę podzielić się moim poglądem na temat tej „nowej” rzeczy.

Pochodzę ze świata C #, w którym użycie słowa kluczowego „nowy” jest tak naturalne, że dziwnie wygląda dla mnie wzór fabryczny.

Kiedy po raz pierwszy koduję w Javascript, nie zdaję sobie sprawy z tego, że istnieje „nowe” słowo kluczowe i kod podobny do tego we wzorze YUI i nie zajmuje mi to dużo czasu. Nie pamiętam, co powinna robić konkretna linia, patrząc wstecz na napisany przeze mnie kod. Bardziej chaotyczne jest to, że mój umysł nie może tak naprawdę przechodzić między granicami instancji obiektów, kiedy „uruchamiam” na sucho kod.

Następnie znalazłem „nowe” słowo kluczowe, które według 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ę, daje mi silne wskazówki na ten temat.

Na przykład, gdy var bar=foo();nie mam żadnych wskazówek co do tego, jaki pasek mógłby być… Czy to jest wartość zwracana, czy jest to nowo utworzony obiekt? Ale z var bar = new foo();pewnością wiem, że pasek jest przedmiotem.


3
Zgadzam się i uważam, że wzór fabryczny powinien być zgodny z konwencją nazewnictwa, taką jak makeFoo ()
pluckyglen

3
+1 - obecność „nowego” daje wyraźniejsze oświadczenie woli.
belugabob

4
Ta reakcja jest dość dziwna, gdy prawie wszystko w JS jest obiektem. Dlaczego musisz wiedzieć na pewno, że funkcja jest obiektem, skoro wszystkie funkcje są obiektami?
Joshua Ramirez

@JoshuaRamirez Nie chodzi o to, że typeof new foo() == "object". To newzwraca instancję fooi wiesz, że możesz zadzwonić foo.bar()i foo.bang(). Można to jednak łatwo złagodzić, używając @return JsDoc Nie dlatego, że jestem zwolennikiem kodu proceduralnego (unikając słowa new)
Juan Mendes

1
@JuanMendes Hmm ... Twój post sprawił, że wydało mi się, że lubisz nowe, ponieważ zawierało wyraźne słowo kluczowe w bazie kodu. Mogę to wykopać. Dlatego używam wzorca modułu. Będę miał funkcję o nazwie createFoo, NewFoo lub MakeFoo, nie ma znaczenia, o ile jest jawna. Wewnątrz deklaruję zmienne, które są używane jako zamknięcia wewnątrz literału obiektu, który jest zwracany z funkcji. Ten dosłowny obiekt ostatecznie jest twoim obiektem, a funkcja była tylko funkcją konstrukcyjną.
Joshua Ramirez

39

Innym przypadkiem za nowy jest to, co ja nazywam Puchatek kodowania . Kubuś Puchatek podąża za jego brzuchem. Mówię idź z językiem, którego używasz, a nie przeciwko .

Są szanse, że opiekunowie języka zoptymalizują język pod kątem idiomów, które próbują zachęcić. Jeśli dodadzą nowe słowo kluczowe do języka, prawdopodobnie uważają, że sensowne jest, aby było jasne podczas tworzenia nowej instancji.

Kod napisany zgodnie z intencjami języka będzie zwiększał wydajność z każdym wydaniem. Z czasem kod unikający kluczowych konstrukcji języka będzie cierpiał.

EDYCJA: I to znacznie wykracza poza wydajność. Nie mogę policzyć razy słyszałem (lub mówi) „dlaczego do cholery zrobili , że ?” przy znajdowaniu 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 za kilka lat nie wyśmiejesz kodu.


3
+1 dla linka Puchatek Kodowanie - teraz muszę znaleźć wymówki, aby wykorzystać to w moich rozmowach ...
Shog9

LOL! Mam wieloletnie doświadczenie w tej konkretnej dziedzinie i zapewniam cię, że nie znajdziesz żadnych trudności. Często nazywają mnie Puchatkiem na robowiki.net. =)
PEZ,

1
Nie wiem, czy kiedykolwiek zobaczysz ten komentarz, ale ten link kodowania Puchatek jest martwy.
Calvin

Dzięki za heads-upy. W zeszłym tygodniu przeprowadziliśmy migrację robowiki.net na nową wiki, a stara zawartość jest obecnie niedostępna. Spieszę się, żeby te stare linki działały.
PEZ

24

Napisałem post, jak złagodzić problem wywołania konstruktora bez nowego słowa kluczowego.
Jest to głównie dydaktyczne, ale pokazuje, w jaki sposób można tworzyć konstruktory, które działają z lub bez newi nie wymagają dodawania kodu testera thisw każdym konstruktorze.

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

Oto sedno 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żyć:

// 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);

6
unikanie arguments.calleejest niesamowite!
Gregg Lind

2
surrogateConstructorpowinno być surrogateCtor(lub odwrotnie).
David Conrad

Zachowałem kolekcję podobnych narzędzi zainspirowanych Crockfordem i odkryłem, że zawsze znajdowałem implementacje, kiedy zaczynałem nowy projekt. I to obejmuje tak podstawową część programowania: tworzenie instancji obiektów. Cała ta praca, tylko po to, by włączyć przypadkowy kod instancji? Doceniam pomysłowość tego opakowania, szczerze mówiąc, ale wydaje się, że wywołuje ono nieostrożne kodowanie.
Joe Coder,

1
@joecoder Wspomniałem, że to tylko w celach dydaktycznych, nie subskrybuję tego paranoicznego stylu kodowania. Gdybym jednak pisał bibliotekę klas, to tak, dodałbym tę funkcję w sposób przejrzysty dla dzwoniących
Juan Mendes,

22

Uzasadnienie nieużywania nowego słowa kluczowego jest proste:

W ogóle go nie wykorzystując unikniesz pułapki, która przypadkowo go ominie. Wzorzec 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 to zrobić:

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

Ale robiąc to, ryzykujesz, że ktoś zapomni użyć nowego słowa kluczowego, a ten operator będzie fubarem. AFAIK nie ma z tego żadnej korzyści (poza tym, że jesteś do tego przyzwyczajony).

Na koniec dnia: chodzi o obronę. Czy możesz użyć nowego wyciągu? Tak. Czy to czyni twój kod bardziej niebezpiecznym? Tak.

Jeśli kiedykolwiek pisałeś C ++, przypomina to ustawianie wskaźników na NULL po ich usunięciu.


6
Nie. Za pomocą „new foo ()” niektóre właściwości są ustawiane na zwróconym obiekcie, np. Konstruktor.
jakieś

34
Tak więc, żeby to wyjaśnić: nie powinieneś używać „nowego”, ponieważ możesz o tym zapomnieć? Żartujesz, prawda?
Bombe,

16
@Bombe: w innych językach pominięcie słowa „nowy” spowoduje błąd. W Javascripcie po prostu kontynuuje transport. Możesz zapomnieć i nigdy nie zdawać sobie sprawy. A samo patrzenie na błędny kod wcale nie będzie oczywiste.
Kent Fredric,

3
@Greg: Nie rozumiem, w jaki sposób pierwsza technika pozwala na użycie łańcuchów prototypowych - dosłowne obiekty są świetne, ale odrzucenie wydajności i innych zalet prototypów ze strachu wydaje się trochę głupie.
Shog9,

5
@Bombe - Powinieneś używać „nowego”, ponieważ ty (i każdy, kto używa twojego kodu) nigdy nie popełnisz błędu? Żartujesz, prawda?
Greg Dean,

20

Myślę, że „nowy” dodaje przejrzystości kodu. A przejrzystość jest warta wszystkiego. Dobrze wiedzieć, że są pułapki, ale unikanie ich poprzez unikanie klarowności nie wydaje mi się właściwą drogą.


16

Przypadek 1: newnie 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

Przypadek 2: newjest wymagany, w przeciwnym razie pojawi się błąd

new Date().getFullYear();     // correct, returns the current year, i.e. 2010
Date().getFullYear();         // invalid, returns an error

5
Należy zauważyć, że w przypadku 1 wywołanie konstruktora jako funkcji ma sens tylko wtedy, gdy zamierzasz dokonać konwersji typu (i nie wiesz, czy obiekt kadłuba ma metodę konwersji, taką jak toString()). We wszystkich innych przypadkach używaj literałów. Nie String('asd') , ale po prostu 'asd'i nie Number(12) , ale po prostu 12.
PointedEars,

1
@PointedEars Jednym wyjątkiem od tej reguły byłoby Array: Array(5).fill(0) jest oczywiście bardziej czytelne niż [undefined, undefined, undefined, undefined, undefined].fill(0)i(var arr = []).length = 5; arr.fill(0);
yyny

@YoYoYonnY Moje oświadczenie zostało wydane w kontekście przypadku 1 odpowiedzi, który odnosi się do użycia newz konstruktorami dla typów obiektów odpowiadających typom pierwotnym . Dosłowne że sugeruje to nie równoważne Arraywezwanie (TRY 0 in …), a reszta jest błąd składni :) W przeciwnym razie są poprawne, chociaż należy zauważyć, jak dobrze, że Array(5)mogą być niejednoznaczne jeśli wziąć pod uwagę niezgodnych implementacje (i dlatego emulowany Array.prototype.fill(…) ).
PointedEars,

12

Oto najkrótsze podsumowanie dwóch najsilniejszych argumentów za i przeciw użyciu newoperatora:

Argumenty przeciw new

  1. Funkcje zaprojektowane jako instancje jako obiekty korzystające z newoperatora mogą mieć katastrofalne skutki, jeśli zostaną niepoprawnie wywołane jako normalne funkcje. W takim przypadku kod funkcji zostanie wykonany w zakresie, w którym funkcja jest wywoływana, zamiast w zakresie obiektu lokalnego, zgodnie z przeznaczeniem. Może to spowodować zastąpienie globalnych zmiennych i właściwości katastrofalnymi konsekwencjami.
  2. Wreszcie pisanie function Func(), a następnie wywoływanie Func.prototype i dodawanie do niego elementów, aby można było wywoływać new Func()konstrukcję obiektu, wydaje się brzydkie dla niektórych programistów, którzy wolą używać innego stylu dziedziczenia obiektów ze względów architektonicznych i stylistycznych.

Więcej informacji na temat tego argumentu można znaleźć w świetnej i zwięzłej książce Douglasa Crockforda Javascript: The Good Parts. W rzeczywistości i tak to sprawdź.

Argument za new

  1. Korzystanie z newoperatora wraz z przypisaniem prototypu jest szybkie.
  2. Tym problemom związanym z przypadkowym uruchomieniem kodu funkcji konstruktora w globalnej przestrzeni nazw można łatwo zapobiec, jeśli zawsze dołączasz trochę kodu do funkcji konstruktora, aby sprawdzić, czy są one wywoływane poprawnie, aw przypadkach, gdy nie są , obsługując połączenie odpowiednio do potrzeb.

Zobacz post Johna Resiga, aby uzyskać proste wyjaśnienie tej techniki i ogólnie głębsze wyjaśnienie modelu dziedziczenia, który on popiera.


Aktualizacja argumentów przeciw: # 1 można złagodzić za pomocą 'use strict';trybu (lub metody w łączu) # 2 Ma cukier synatyczny w ES6 , jednak zachowanie jest takie samo jak poprzednio.
ninMonkey

9

Zgadzam się z Pez i niektórymi tutaj.

Wydaje mi się oczywiste, że „nowy” to samoopisujące tworzenie obiektów, w którym wzorzec YUI, który opisuje Greg Dean, jest całkowicie zaciemniony .

Możliwość, że ktoś mógłby napisać var bar = foo;lub gdy var bar = baz();baza nie jest metodą tworzenia obiektów, wydaje się znacznie bardziej niebezpieczna.


8

Myślę, że nowe jest złe, nie dlatego, że jeśli zapomnisz go użyć przez pomyłkę, może to powodować problemy, ale dlatego, że psuje łańcuch dziedziczenia, utrudniając zrozumienie języka.

JavaScript jest zorientowany obiektowo na prototypy. Dlatego każdy obiekt MUSI być utworzony z innego obiektu takiego var newObj=Object.create(oldObj). Tutaj oldObj nazywa się prototypem newObj (stąd „oparty na prototypie”). Oznacza to, że jeśli właściwość nie zostanie znaleziona w newObj , zostanie przeszukana w oldObj . newObj domyślnie będzie zatem pustym obiektem, ale ze względu na swój łańcuch prototypów wydaje się, że ma wszystkie wartości oldObj .

Z drugiej strony, jeśli to zrobisz var newObj=new oldObj(), prototypem newObj jest oldObj.prototype , co jest niepotrzebnie trudne do zrozumienia.

Sztuką jest użycie

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

Jest w tej funkcji i tylko tutaj należy użyć nowej. Następnie skorzystaj z Object.create () . Metoda rozwiązuje problem prototypu.


7
Szczerze mówiąc, nie przepadam za tą techniką - nie dodaje ona nic nowego, a jak pokazuje twoja odpowiedź, może nawet stać się kulą. IMHO, zrozumienie łańcuchów prototypów i tworzenie instancji obiektów jest kluczowe dla zrozumienia JavaScript ... Ale jeśli sprawia ci to dyskomfort w bezpośrednim korzystaniu z nich, to użycie funkcji pomocnika do zadbania o niektóre szczegóły jest w porządku, o ile pamiętasz, czym jesteś robić. FWIW: (nieco bardziej przydatna) odmiana na ten temat jest częścią ECMAScript 5. edycja. standardowe i już dostępne w niektórych przeglądarkach - dlatego należy uważać, aby nie przedłużyć go na nowo!
Shog9

BTW: Nie jestem pewien, dlaczego zrobiłeś to CW, ale jeśli chcesz ponownie opublikować go z poprawkami formatowania, które wprowadziłem, uważaj, aby nie zaznaczyć tego pola wyboru ...
Shog9

2
Niepotrzebnie trudne do zrozumienia? var c = new Car()to to samo co robienievar c = Object.create(Car.prototype); Car.call(c)
Juan Mendes,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.