Jaka jest konwencja JavaScript dotycząca braku operacji?

121

Jaka jest konwencja JavaScript dotycząca braku operacji? Jak passpolecenie w Pythonie .

  • Jedną z opcji jest po prostu pusta funkcja: function() {}
  • Oferty jQuery $.noop(), które po prostu wywołują pustą funkcję powyżej.
  • Czy dopuszczalne jest po prostu wpisanie wartości falselub 0?

W kontekście ... wszystkie te działają bez zgłaszania błędu w Chrome:

var a = 2;
(a === 1) ? alert(1) : function() {};
(a === 1) ? alert(1) : $.noop();
(a === 1) ? alert(1) : false;
(a === 1) ? alert(1) : 0;

EDYCJA: Wiele osób odpowiedziało „nie rób tego! Zmień strukturę kodu!” To przypomina mi post, w którym ktoś zapytał, jak powąchać przeglądarkę. Otrzymał lawinę postów z napisem „NIE ROBIĆ TEGO! TO ZŁO”, ale nikt nie powiedział mu, jak wąchać przeglądarkę . To nie jest przegląd kodu. Wyobraź sobie, że masz do czynienia ze starszym kodem, którego nie można zmienić i bez przekazania jakiejś funkcji spowoduje wyświetlenie błędu. Lub po prostu klient tego chce i płacą mi . Tak więc, z szacunkiem, odpowiedz na pytanie : Jaki jest najlepszy sposób określenia funkcji „brak operacji” w JavaScript?

EDIT2: A co z jednym z nich?

true;
false;
0;
1;
null;
kmiklas
źródło
7
Dlaczego nie proste stwierdzenie „if”?
epascarello,
11
Żadna z tych alternatyw nie 0jest w rzeczywistości lepsza (ani gorsza). Powiedziałbym, że właściwą rzeczą jest if (a === 1) doSomething();i nie używać, ? :gdy nie ma to sensu.
Pointy,
6
Nadużywasz operatora trójskładnikowego z efektem ubocznym. Jeśli musisz, zróbif (statement) action; else ;
mplungjan,
Tak falselub 0będzie działać; nullto dobry sposób na wyrażenie zgody na nie.
Matt,
3
Mam nadzieję, że trójka ma jakąś konieczność, której z jakiegoś powodu nie widzimy ... Wygląda na to, że komplikujesz swój kod, aby był fajny (wszyscy to zrobiliśmy!)
Stachu,

Odpowiedzi:

95

Odpowiadając na pierwotne pytanie, najbardziej elegancką i zgrabną implementacją funkcji noop w czystym Javascript (jak również tutaj omówiono ) jest Function.prototype . To dlatego, że:

  1. Function.prototype to funkcja:

typeof Function.prototype === "function" // returns true

  1. Można go wywołać jako funkcję i zasadniczo nie robi nic, jak pokazano tutaj:
setTimeout(function() {
      console.log('Start: ', Date.now());
      Function.prototype();
      console.log('End  : ', Date.now());
    }, 1000);

Chociaż jest to „prawdziwy noop”, ponieważ większość przeglądarek wydaje się nie robić nic, aby wykonać zdefiniowane w ten sposób noop (a tym samym oszczędzać cykle procesora), mogą być z tym związane pewne problemy z wydajnością (o czym wspominają również inni w komentarzach lub w inne odpowiedzi).

Jednak w związku z tym można łatwo zdefiniować własną funkcję noop i, w rzeczywistości, wiele bibliotek i frameworków również udostępnia funkcje noop. Poniżej kilka przykładów:

var noop = function () {};           // Define your own noop in ES3 or ES5
const noop = () => {};               // Define in ES6 as Lambda (arrow function)
setTimeout(noop, 10000);             // Using the predefined noop

setTimeout(function () {} , 10000);  // Using directly in ES3 or ES5
setTimeout(() => {} , 10000);        // Using directly in ES6 as Lambda (arrow function)

setTimeout(angular.noop, 10000);     // Using with AngularJS 1.x
setTimeout(jQuery.noop, 10000);      // Using with jQuery

Oto alfabetyczna lista różnych implementacji funkcji noop (lub powiązanych dyskusji lub wyszukiwań w Google):

AngularJS 1.x , Angular 2+ (wygląda na to, że nie ma natywnej implementacji - użyj własnej, jak pokazano powyżej), Ember , jQuery , Lodash , NodeJS , Ramda , React (nie wydaje się mieć natywnej implementacji - użyj własnej jak pokazano powyżej), RxJS , podkreślenie

BOTTOM LINE : Chociaż Function.prototype to elegancki sposób wyrażania noop w JavaScript, mogą jednak wystąpić pewne problemy z wydajnością związane z jego użyciem. Możesz więc zdefiniować i użyć własnego (jak pokazano powyżej) lub użyć takiego zdefiniowanego przez bibliotekę / framework, którego możesz używać w swoim kodzie.

Alan CS
źródło
5
To powinna być odpowiedź. Można go nawet użyć, Function.prototype()jeśli nie potrzebujesz limitu czasu i chcesz, aby został wykonany od razu.
Springloaded
1
setTimeout(Function(), 10000);
iegik
@iegik: +1. Tak, masz rację. Wszystkie poniższe są równoważne: setTimeout (function () {}, 10000); LUB setTimeout (new Function (), 10000); LUB setTimeout (Function (), 10000); LUB setTimeout (funkcja, 10000); ponieważ gramatyka Javascript i implementacja konstruktora Function pozwalają na takie konstrukcje.
Alan CS
5
W ES6 lub przy użyciu babel lub innego transpilera (( )=>{};)technicznie Czysty JS i znacznie krótszy niż, function () {}ale dokładnie równoważny.
Ray Foss
2
Byłem zainteresowany znalezieniem konstrukcji typu no-op, aby skutecznie wyłączyć kod debugowania w moim programie. W C / C ++ użyłbym makr. Przetestowałem przypisanie mojego fn do Function.prototype i ustawiłem czas, porównując wyniki z po prostu posiadaniem instrukcji if wewnątrz funkcji, aby natychmiast powrócić. Porównałem to również z wynikami zwykłego całkowitego usunięcia funkcji z pętli. Niestety, Function.prototype w ogóle nie działało dobrze. wywołanie pustej funkcji było znacznie lepsze pod względem wydajności, nawet szybsze niż wywołanie funkcji z prostym testem „if” w celu zwrócenia.
bearvarine
73

Najbardziej zwięzły i wydajnych noop jest funkcja pusta strzałka : ()=>{}.

Funkcje strzałek działają natywnie we wszystkich przeglądarkach poza IE ( jeśli musisz, istnieje transformacja Babel ): MDN


()=>{} vs. Function.Prototype

  • ()=>{}jest 87% szybszy niż Function.prototypew Chrome 67.
  • ()=>{}jest o 25% szybszy niż Function.prototypew przeglądarce Firefox 60.
  • ()=>{}jest 85% szybszy niż Function.prototypew Edge (15.06.2018) .
  • ()=>{}to 65% mniej kodu niż Function.prototype.

Poniższy test rozgrzewa się, używając funkcji strzałek, aby uzyskać odchylenie Function.prototype, ale funkcja strzałka jest wyraźnym zwycięzcą:

const noop = ()=>{};
const noopProto = Function.prototype;

function test (_noop, iterations) {
    const before = performance.now();
    for(let i = 0; i < iterations; i++) _noop();
    const after = performance.now();
    const elapsed = after - before;
    console.info(`${elapsed.toFixed(4)}MS\t${_noop.toString().replace('\n', '')}\tISNOOP? ${_noop() === undefined}`);
    return elapsed;
}

const iterations = 10000000
console.info(`noop time for ${iterations.toLocaleString()} iterations`)
const timings = {
    noop: test(noop, iterations),
    noopProto: test(noopProto, iterations)
}

const percentFaster = ((timings.noopProto - timings.noop)/timings.noopProto).toLocaleString("en-us", { style: "percent" });
console.info(`()=>{} is ${percentFaster} faster than Function.prototype in the current browser!`)

cchamberlain
źródło
1
Stworzyłem test jsPerf, aby porównać niektóre opcje: funkcja noop . Wygląda na to, że w Firefoksie 54 wszystkie alternatywy wydają się być równoważne; w Chromium 58 Function.prototypejest drastycznie wolniejszy, ale żadna z pozostałych opcji nie ma zdecydowanej przewagi.
Lucas Werkmeister
_=>{}jest o jeden znak mniej i robi to samo.
Ross Attrill
@RossAttrill * coś podobnego - _=>{}ma jeden parametr. Jeśli ktoś używa TypeScript z dość ścisłą konfiguracją, to się nie skompiluje. Jeśli to nazwiesz, twoje IDE prawdopodobnie powie, że akceptuje pojedynczy parametr, który może być dowolnego typu (zarówno JavaScript, jak i TypeScript w vscode).
cchamberlain
15

cokolwiek tutaj osiągasz, jest złe. Wyrażeń trójskładnikowych nie należy używać jako pełnego oświadczenia, tylko w wyrażeniu, więc odpowiedź na twoje pytanie brzmi:

żadna z twoich sugestii, zamiast tego zrób:

var a = 2;
if (a === 1)
    alert(1)
// else do nothing!

wtedy kod jest łatwo zrozumiały, czytelny i tak efektywny, jak to tylko możliwe.

Po co to utrudniać, skoro może to być proste?

edytować:

Czy zatem polecenie „braku operacji” zasadniczo wskazuje na gorszą strukturę kodu?

Nie rozumiesz, o co mi chodzi. Wszystko powyżej dotyczy wyrażenia trójskładnikowego x ? y : z.

Jednak polecenie no operation nie ma sensu w językach wyższego poziomu, takich jak Javascript.

Jest zwykle używany w językach niższego poziomu, takich jak asembler lub C, jako sposób, aby procesor nie robił nic dla jednej instrukcji dla celów synchronizacji.

W JS, czy to zrobić 0;, null;, function () {};lub pustym stwierdzeniem, istnieją duże szanse, że zostanie ona zignorowana przez tłumacza, gdy czyta je, ale zanim to dostaje interpretowane, więc w końcu, będziesz po prostu uczynić Twój program będzie ładowany wolniej przez naprawdę niewielki czas. Nota Bene : Zakładam to, ponieważ nie jestem zaangażowany w żaden powszechnie używany interpreter JS i są szanse, że każdy interpreter ma swoją własną strategię.

W przypadku, gdy używasz czegoś bardziej skomplikowanego, na przykład $.noop()lub var foo = function () {}; foo(), interpreter może wykonać nieużyteczne wywołanie funkcji, które w końcu zepsuje kilka bajtów twojego stosu funkcji i kilka cykli.

Jedynym powodem, dla którego widzę taką funkcję, jak $.noop()istniałaby, byłoby to, że mógłbym nadal udostępniać funkcję zwrotną jakiejś funkcji zdarzenia, która zgłosiłaby wyjątek, gdyby nie mogła wywołać tego wywołania zwrotnego. Ale z konieczności jest to funkcja, którą musisz nadać, a nadanie jej noopnazwy jest dobrym pomysłem, więc mówisz swoim czytelnikom (a może to być ty za 6 miesięcy), że celowo dajesz pustą funkcję.

Ostatecznie nie ma czegoś takiego jak „gorsza” lub „wyższa” struktura kodu. Masz rację lub mylisz się w sposobie używania narzędzi. Używanie trójskładnika jako przykładu jest jak używanie młotka, gdy chcesz wkręcić. To zadziała, ale nie jesteś pewien, czy możesz coś zawiesić na tej śrubie.

To, co można uznać za „gorsze” lub „lepsze”, to algorytm i pomysły, które umieścisz w swoim kodzie. Ale to inna sprawa.

zmo
źródło
1
Czy zatem polecenie „braku operacji” zasadniczo wskazuje na gorszą strukturę kodu?
kmiklas
3
@kmiklas: Nie mogę powiedzieć, że kiedykolwiek znalazłem uzasadnioną potrzebę NOOP, poza asemblerem.
Matt Burland,
@zmo: Rozumiem twój punkt widzenia dotyczący tego, jak twoje komentarze odnoszą się do wyrażenia trójskładnikowego; wydaje się, że wszyscy przegapili mój punkt widzenia, że ​​był to tylko przykład ilustrujący wywołanie funkcji „Brak działania”.
kmiklas
1
Oczywiście są powody, by chcieć / potrzebować funkcji no-op. Nie twórz szerokich uogólnień w oparciu o 12 sekund głębokiego namysłu nad czymś. JavaScript to język, w którym funkcje można w pełni kontrolować jako zmienne. Załóżmy, że musisz wyłączyć funkcję bez jawnego testu? To byłoby idealne do tego. Jeśli masz funkcję, która pojawia się często, na przykład instrukcja debugowania, byłoby miło móc ją wyłączyć przez ponowne przypisanie funkcji do opcji no-op zamiast konieczności dodawania dodatkowego kodu do testowania zmiennej za każdym razem, gdy pojawi się funkcja .
bearvarine
2
@bearvarine dziękuję za tak dobrą opinię o mnie :-D. Twój komentarz ma całkowitą rację, ale tak naprawdę nie jest to projekt kodu, tylko projektowanie makiety przy użyciu hackowania. To, co
opisujesz,
7

Myślę, że jQuery noop()ma głównie na celu zapobieganie awariom kodu, zapewniając domyślną funkcję, gdy żądana nie jest dostępna. Na przykład, biorąc pod uwagę następujący przykład kodu, $.noopjest wybierany, jeśli fakeFunctionnie jest zdefiniowany, co zapobiega fnawarii następnego wywołania funkcji :

var fn = fakeFunction || $.noop;
fn() // no crash

Następnie noop()pozwala zaoszczędzić pamięć, unikając wielokrotnego pisania tej samej pustej funkcji w całym kodzie. Nawiasem mówiąc, $.noopjest nieco krótszy niż function(){}(6 bajtów zapisanych na token). Tak więc nie ma związku między kodem a pustym wzorcem funkcji. Stosowanie null, falselub 0jeśli chcesz, w Twoim przypadku nie będzie efektem ubocznym. Ponadto warto zauważyć, że ten kod ...

true/false ? alert('boo') : function(){};

... jest całkowicie bezużyteczna, ponieważ nigdy nie wywołasz funkcji, a ta ...

true/false ? alert('boo') : $.noop();

... jest jeszcze bardziej bezużyteczny, ponieważ wywołujesz pustą funkcję, która jest dokładnie tym samym, co ...

true/false ? alert('boo') : undefined;

Zastąpmy wyrażenie trójskładnikowe ifinstrukcją, aby zobaczyć, jak bardzo jest bezużyteczne:

if (true/false) {
    alert('boo');
} else {
    $.noop(); // returns undefined which goes nowhere
}

Możesz po prostu napisać:

if (true/false) alert('boo');

Albo jeszcze krócej:

true/false && alert('boo');

Aby wreszcie odpowiedzieć na twoje pytanie, myślę, że „konwencjonalna operacja nie” to taka, która nigdy nie została napisana.

liść
źródło
1
Czasami jednak musisz podać funkcję. Na przykład w przypadku obietnic (fn, fn) czasami chcesz podać drugą funkcję, ale nie pierwszą. dlatego potrzebujesz czegoś dla posiadacza miejsca ...mypromise.then($.noop, myErrorhandler);
John Henckel
3

Używam:

 (0); // nop

Aby przetestować czas wykonania tego uruchomienia jako:

console.time("mark");
(0); // nop
console.timeEnd("mark");

wynik: ocena: 0.000ms

Użycie Boolean( 10 > 9)można sprowadzić do prostego ( 10 > 9)określenia, które zwraca true. Wymyślenie pomysłu użycia pojedynczego operandu, którego w pełni oczekiwałem (0);, zwróci false, ale po prostu zwraca argument z powrotem, co można przejrzeć, wykonując ten test w konsoli.

> var a = (0);
< undefined
> a
< 0
Jajka
źródło
1
wielu, którzy spróbują tego dokonać, otrzymaargument 0 is not a function
Zaklinacz kodów
4
Po prostu przypisujesz 0. IMO nooppowinno być funkcją oceniającą z undefinedwynikiem.
cchamberlain
1
Jak to stwierdzenie (0); przypisując 0 ?, gdzie jest zmienna i znak równości? Nie ma nazwy funkcji przed (0), więc nie rozumiem, jak to może być argument? jeśli spróbujesz typeof ((0)), wróci jako liczba. Kiedy odpowiadałem na pytanie, podawałem oświadczenie, którego używam do naśladowania NOP, ale jestem gotów zmienić swoją odpowiedź na po prostu użyć; do przedstawienia pustego stwierdzenia
Eggs
1
Myślę (0); jest akceptowalnym rozwiązaniem pytania OP. Myślę, że ludzie są zdezorientowani, ponieważ znaleźli tę stronę, szukając funkcji, która nic nie robi, a nie stwierdzenia, które nic nie robi. OP nie pomaga, sugerując początkowo function(){}jako rozwiązanie. Wezwanie (0)()spowodujeTypeError: 0 is not a function
Benjamin
Miałem na myśli var a = (0);. No-op nie powinien zmieniać pamięci, a przykład przydziału właśnie to robi. Samo (0)w sobie można uznać za bezużyteczny no-op (w JavaScript jest ich nieskończona liczba, więc nic nie czyni twojego przykładu wyjątkowym).
cchamberlain
2

Nie ma absolutnie żadnego problemu ani spadku wydajności w przypadku używania Function.prototypeponad () => {}.

Główną zaletą Function.prototypejest posiadanie funkcji singleton zamiast ponownego definiowania nowej funkcji anonimowej za każdym razem. Szczególnie ważne jest, aby Function.prototypepodczas definiowania wartości domyślnych i zapamiętywania używać opcji no-op, ponieważ zapewnia ona spójny wskaźnik obiektu, który nigdy się nie zmienia.

Powodem, dla którego Function.prototyperaczej polecam, a nie Functiondlatego, że nie są takie same:

Function() === Function()
// false

Function.prototype() === Function.prototype()
// true

Również testy porównawcze z innych odpowiedzi są mylące . W rzeczywistości Function.prototypedziała szybciej niż w () => {}zależności od tego, jak napiszesz i uruchomisz test porównawczy:

Nie można ufać benchmarkom JS .

Nie stylizuj swojego kodu na podstawie testów; zrób wszystko, co możliwe, i pozwól tłumaczowi dowiedzieć się, jak zoptymalizować w dłuższej perspektywie.

Sawtaytoes
źródło