Kiedy operator przecinka jest przydatny?

88

Czytałem to pytanie o "operator przecinka" w wyrażeniach ( ,) i dokumentację MDN na ten temat, ale nie przychodzi mi do głowy scenariusz, w którym by się przydał.

Kiedy więc operator przecinka jest przydatny?

gdoron wspiera Monikę
źródło
2
var i, j, k;vs var i; var j, var k?
Salman A
15
@SalmanA. Nie jestem pewien, czy ma to coś wspólnego z ,operatorem. Ta linia jest również ważna w C#, ale ,operator tam nie istnieje.
gdoron wspiera Monikę
2
@SalmanA. Zrobiłem. Nie znalazłem tego, oświeć mnie ...
gdoron wspiera Monikę
5
@SalmanA a ,nie zawsze jest ,operatorem (i nigdy nie jest ,operatorem w C #). Więc C # może brakować ,operatora, ale nadal może swobodnie używać ,jako części składni.
Seth Carnegie
8
Myślę, że odpowiedzi tutaj podsumowały fakt, że ,nie jest powszechnie używany (i nie każde wystąpienie a ,jest operatorem przecinka) . Ale możesz pożyczyć ją i Array, aby dokonać bezpośredniej zamiany zmiennych bez tworzenia zmiennej tymczasowej. Biorąc pod uwagę, że chcesz zamienić wartości ai b, możesz to zrobić a = [b][b = a,0]. Spowoduje to umieszczenie prądu bw Array. Drugi []to notacja dostępu do właściwości. Indeks, do którego uzyskano dostęp, jest 0, ale nie przed przypisaniem ado b, który jest teraz bezpieczny, ponieważ bjest przechowywany w tablicy. ,pozwala nam wykonać wiele wyrażeń w [].

Odpowiedzi:

128

Poniższe prawdopodobnie nie są zbyt przydatne, ponieważ nie piszesz tego samodzielnie, ale minifier może zmniejszyć kod za pomocą operatora przecinka. Na przykład:

if(x){foo();return bar()}else{return 1}

stanie się:

return x?(foo(),bar()):1

? :Operator może być używany teraz, ponieważ operator przecinek (do pewnego stopnia) pozwala na dwa oświadczenia mają być zapisane jako jeden oświadczeniu.

Jest to przydatne, ponieważ pozwala na pewną zgrabną kompresję (tutaj 39 -> 24 bajty).


Chciałbym podkreślić, że przecinek w nievar a, b jest operatorem przecinka, ponieważ nie istnieje w wyrażeniu . Przecinek ma specjalne znaczenie w wypowiedziach . w wyrażeniu odnosiłoby się do dwóch zmiennych i oceniał do , co nie ma miejsca .var a, bbvar a, b

pimvdb
źródło
3
Jak o tym myślisz? Czytałeś to gdzieś? Czy to naprawdę jest używane?
gdoron wspiera Monikę
16
Niedawno bawiłem się kompilatorem Closure Compiler, aby zobaczyć, co właściwie robi, i zauważyłem tę zamianę.
pimvdb
2
Podobnym zastosowaniem, które moim zdaniem jest przydatne w kodzie, byłoby przypisanie wielu zmiennych w instrukcji wbudowanej if. Na przykład: if (condition) var1 = val1, var2 = val2;Osobiście uważam, że unikanie nawiasów, jeśli to możliwe, sprawia, że ​​kod jest bardziej czytelny.
Aidiakapi
2
Używam operatora przecinka tylko wtedy, gdy dodaję instrukcje dziennika do wyrażeń (foo => (console.log ('foo', foo), foo)) lub gdy jestem zbyt sprytny, używając funkcji redukuj iteracje . (pairs.reduce ((acc, [k, v]) => (acc [k] = v, acc), {}))
Joseph Sikorski
38

Operator przecinka umożliwia umieszczenie wielu wyrażeń w miejscu, w którym oczekiwane jest jedno wyrażenie. Wynikowa wartość wielu wyrażeń oddzielonych przecinkami będzie wartością ostatniego wyrażenia oddzielonego przecinkami.

Osobiście nie używam go zbyt często, ponieważ nie ma tak wielu sytuacji, w których oczekuje się więcej niż jednego wyrażenia i nie ma mniej zagmatwanego sposobu pisania kodu niż użycie operatora przecinka. Jedna interesująca możliwość znajduje się na końcu forpętli, kiedy chcesz zwiększyć więcej niż jedną zmienną:

// j is initialized to some other value
// as the for loop executes both i and j are incremented
// because the comma operator allows two statements to be put in place of one
for (var i = 0; i < items.len; i++, j++) {
    // loop code here that operates on items[i] 
    // and sometimes uses j to access a different array
}

Tutaj widzisz, że i++, j++można to umieścić w miejscu, w którym dozwolone jest jedno wyrażenie. W tym konkretnym przypadku wiele wyrażeń jest używanych do efektów bocznych, więc nie ma znaczenia, że ​​wyrażenia złożone przyjmują wartość ostatniego, ale są inne przypadki, w których może to mieć znaczenie.

jfriend00
źródło
38

Operator przecinka jest często przydatny podczas pisania kodu funkcjonalnego w JavaScript.

Rozważ ten kod, który napisałem jakiś czas temu dla SPA, który miał coś podobnego do następującego

const actions = _.chain(options)
                 .pairs() // 1
                 .filter(selectActions) // 2
                 .map(createActionPromise) // 3
                 .reduce((state, pair) => (state[pair[0]] = pair[1], state), {}) // 4
                 .value();

To był dość złożony, ale prawdziwy scenariusz. Trzymaj się ze mną, gdy wyjaśniam, co się dzieje, i podczas tego procesu przedstaw argumenty dla operatora przecinka.


To używa łańcucha Underscore do

  1. Rozłóż wszystkie opcje przekazane do tej funkcji za pomocą, pairs która zamieni się { a: 1, b: 2}w[['a', 1], ['b', 2]]

  2. Ta tablica par właściwości jest filtrowana według tego, które z nich są uważane za „akcje” w systemie.

  3. Następnie drugi indeks w tablicy jest zastępowany funkcją, która zwraca obietnicę reprezentującą tę akcję (za pomocą map)

  4. Na koniec wywołanie funkcji reducepołączy każdą „tablicę właściwości” ( ['a', 1]) z powrotem w ostateczny obiekt.

Efektem końcowym jest przekształcona wersja optionsargumentu, która zawiera tylko odpowiednie klucze i których wartości są używane przez funkcję wywołującą.


Patrząc tylko

.reduce((state, pair) => (state[pair[0]] = pair[1], state), {})

Możesz zobaczyć, że funkcja redukcji zaczyna się od pustego obiektu stanu state, a dla każdej pary reprezentującej klucz i wartość funkcja zwraca ten sam stateobiekt po dodaniu właściwości do obiektu odpowiadającego parze klucz / wartość. Ze względu na składnię funkcji strzałek ECMAScript 2015, treść funkcji jest wyrażeniem, w wyniku czego operator przecinka umożliwia zwięzłą i użyteczną funkcję „iteruj” .

Osobiście spotkałem się z wieloma przypadkami podczas pisania Javascript w bardziej funkcjonalnym stylu z ECMAScript 2015 + Arrow Functions. Powiedziawszy to, zanim napotkałem funkcje strzałkowe (takie jak w czasie pisania pytania), nigdy nie użyłem operatora przecinka w żaden celowy sposób.

Syynth
źródło
2
To jedyna naprawdę przydatna odpowiedź, jeśli chodzi o to, jak / kiedy programista może używać operatora przecinka. Bardzo przydatne zwłaszcza wreduce
hgoebl
1
W przypadku ES6 + powinna to być akceptowana odpowiedź.
Ardee Aram
Nicea odpowiedź, ale jeśli mogę coś zasugerować nieco bardziej czytelne: .reduce((state, [key, value]) => (state[key] = value, state), {}). I zdaję sobie sprawę, że to podważa cel odpowiedzi, ale .reduce((state, [key, value]) => Object.assign(state, { [key]: value }), {})całkowicie wyeliminowałoby potrzebę stosowania operatora przecinka.
Patrick Roberts
Chociaż Object. assign jest obecnie prawdopodobnie bardziej idiomatyczny, a nawet jest tylko operatorem spreadu, nie jestem pewien, czy był on wówczas powszechnie używany. Chciałbym również zwrócić uwagę, że chociaż operator przecinka jest trochę bardziej niejasny, może to generować dużo mniej śmieci w sytuacjach, gdy zbiór danych jest bardzo duży. Destrukturyzacja z pewnością jednak poprawiłaby czytelność!
Syynth
18

Innym zastosowaniem operatora przecinka jest ukrycie wyników, na których nie zależy nam w repl lub konsoli, wyłącznie dla wygody.

Na przykład, jeśli oceniasz myVariable = aWholeLotOfTextw repl lub konsoli, wydrukuje wszystkie dane, które właśnie przypisałeś. Mogą to być strony i strony, a jeśli wolisz ich nie widzieć, możesz zamiast tego ocenić myVariable = aWholeLotOfText, 'done', a repl / console po prostu wydrukuje „gotowe”.

Oriel słusznie wskazuje †, że dostosowywanie toString()lub get()funkcje mogą nawet sprawić, że będzie to przydatne.

Julian de Bhal
źródło
1
Ha, bardzo fajny pomysł! (Wreszcie odpowiedź, która faktycznie odpowiada na pytanie, w przeciwieństwie do prawie wszystkich odpowiedzi {i 3 usunięte odpowiedzi, których potrzebujesz reputacji 20K, aby zobaczyć ...})
gdoron wspiera Monikę
1
Jeśli przypisana wartość jest obiektem, konsola może próbować ładnie go wyświetlić. W tym celu może np. Wywołać metody pobierające, które mogłyby zmienić stan obiektu. Użycie przecinka może temu zapobiec.
Oriol
@Oriol - Nice! Masz całkowitą rację. W jakiś sposób to potencjalnie użyteczne jest trochę rozczarowujące :)
Julian de Bhal
13

Operator przecinka nie jest specyficzny dla JavaScript, jest dostępny w innych językach, takich jak C i C ++ . Jako operator binarny jest to przydatne, gdy pierwszy operand, który jest generalnie wyrażeniem, ma pożądany efekt uboczny wymagany przez drugi operand. Jeden przykład z Wikipedii:

i = a += 2, a + b;

Oczywiście możesz napisać dwa różne wiersze kodu, ale przecinek jest inną opcją i czasami jest bardziej czytelny.

taskinoor
źródło
1
Potraktuj to jako alternatywę, chociaż definicja dobra może się różnić w zależności od ludzi. Jednak nie mogę znaleźć żadnego przykładu, w którym MUSISZ użyć przecinka. Inną podobną rzeczą jest trójskładnikowy?: Operator. Zawsze można to zastąpić if-else, ale czasami?: Tworzy bardziej czytelny kod niż if-else. Ta sama koncepcja dotyczy również przecinka.
taskinoor
BTW, nie rozważam użycia przecinka w deklaracji zmiennej lub inicjalizacji wielu zmiennych w pętli. W takich przypadkach przecinek jest przeważnie lepszy.
taskinoor
2
wygląda to zagmatwane af ... wtf.
Timmerz,
6

Nie zgodziłbym się z Flanaganem i powiedziałbym, że przecinek jest naprawdę przydatny i pozwala pisać bardziej czytelny i elegancki kod, zwłaszcza gdy wiesz, co robisz:

Oto bardzo szczegółowy artykuł na temat używania przecinków:

Kilka przykładów z tego miejsca na dowód demonstracji:

function renderCurve() {
  for(var a = 1, b = 10; a*b; a++, b--) {
    console.log(new Array(a*b).join('*'));
  }
}

Generator Fibonacciego:

for (
    var i=2, r=[0,1];
    i<15;
    r.push(r[i-1] + r[i-2]), i++
); 
// 0,1,1,2,3,5,8,13,21,34,55,89,144,233,377

Znajdź pierwszy element nadrzędny, odpowiednik .parent()funkcji jQuery :

function firstAncestor(el, tagName) {
    while(el = el.parentNode, el && (el.tagName != tagName.toUpperCase()));
    return el;
}

//element in http://ecma262-5.com/ELS5_HTML.htm
var a = $('Section_15.1.1.2'); 

firstAncestor(a, 'div'); //<div class="page">
szaman.sir
źródło
7
Nie jestem pewien, czy powiedziałbym, że cokolwiek z tego jest bardziej czytelne, ale z pewnością jest dość sprytne, więc +1
Chris Marisic
1
Nie potrzebujesz przecinka w pętli while w ostatnim przykładzie, while ((el = el.parentNode) && (el.tagName != tagName.toUpperCase()))w tym kontekście wystarczy.
PitaJ
5

Nie znalazłem praktycznego zastosowania tego innego niż to, ale oto jeden scenariusz, w którym James Padolsey ładnie używa tej techniki do wykrywania IE w pętli while:

var ie = (function(){

    var undef,
        v = 3,
        div = document.createElement('div'),
        all = div.getElementsByTagName('i');

    while ( // <-- notice no while body here
        div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->',
        all[0]
    );

    return v > 4 ? v : undef;

}());

Te dwie linie muszą zostać wykonane:

div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->',
all[0]

Wewnątrz operatora przecinka oba są oceniane, chociaż w jakiś sposób można by je rozdzielić.

Sarfraz
źródło
3
To mogła być pętla do- while.
Casey Chu,
5

Jest coś „dziwnego”, co można zrobić w JavaScript wywołując funkcję pośrednio za pomocą operatora przecinka.

Tutaj jest długi opis: Pośrednie wywołanie funkcji w JavaScript

Używając tej składni:

(function() {
    "use strict";
  
    var global = (function () { return this || (1,eval)("this"); })();
    console.log('Global === window should be true: ', global === window);
  
    var not_global = (function () { return this })();
    console.log('not_global === window should be false: ', not_global === window);
  
  }());

Możesz uzyskać dostęp do zmiennej globalnej, ponieważ evaldziała inaczej, gdy jest wywoływana bezpośrednio, a inaczej wywoływana pośrednio.

Jeremy J Starcher
źródło
4

Zauważyłem, że operator przecinka jest najbardziej przydatny podczas pisania takich pomocników.

const stopPropagation = event => (event.stopPropagation(), event);
const preventDefault = event => (event.preventDefault(), event);
const both = compose(stopPropagation, preventDefault);

Możesz zamienić przecinek na || lub &&, ale wtedy musisz wiedzieć, co zwraca funkcja.

Co ważniejsze, separator przecinka komunikuje zamiar - kod nie dba o to, do czego oblicza lewy operand, podczas gdy alternatywy mogą mieć inny powód, dla którego istnieją. To z kolei ułatwia zrozumienie i refaktoryzację. Jeśli typ zwracanej funkcji kiedykolwiek się zmieni, powyższy kod nie ulegnie zmianie.

Oczywiście możesz osiągnąć to samo na inne sposoby, ale nie tak zwięźle. Jeśli || i && znalazły miejsce w powszechnym użyciu, podobnie jak operator przecinka.

Lambros Symeonakis
źródło
Podobny do tego, co Ramda \ Lodash robi z tap( ramdajs.com/docs/#tap ). Zasadniczo wykonujesz efekt uboczny, a następnie zwracasz wartość początkową; bardzo przydatne w programowaniu funkcjonalnym :)
lawina1
1

Jednym z typowych przypadków, w których go używam, jest opcjonalna analiza argumentów. Myślę, że dzięki temu jest bardziej czytelny i zwięzły, dzięki czemu analiza argumentów nie dominuje w treści funkcji.

/**
 * @param {string} [str]
 * @param {object} [obj]
 * @param {Date} [date]
 */
function f(str, obj, date) {
  // handle optional arguments
  if (typeof str !== "string") date = obj, obj = str, str = "default";
  if (obj instanceof Date) date = obj, obj = {};
  if (!(date instanceof Date)) date = new Date();

  // ...
}
Rich Remer
źródło
Chociaż sam tego nie faworyzuję, jest to jedyny podany przez kogoś przykład, który moim zdaniem jest lepszy pod względem czytelności niż równoważna lista indywidualnych wyrażeń, nie myśląc, że są całkowicie szalone.
Średnik
1

Powiedzmy, że masz tablicę:

arr = [];

Kiedy wchodzisz pushdo tej tablicy, rzadko jesteś zainteresowany pushzwracaną wartością, a mianowicie nową długością tablicy, ale raczej samą tablicą:

arr.push('foo')  // ['foo'] seems more interesting than 1

Używając operatora przecinka, możemy wypchnąć tablicę, określić tablicę jako ostatni operand przecinka, a następnie użyć wyniku - samej tablicy - do kolejnego wywołania metody tablicowej, rodzaj łączenia:

(arr.push('bar'), arr.push('baz'), arr).sort(); // [ 'bar', 'baz', 'foo' ]
Dexygen
źródło
-2

Innym obszarem, w którym można użyć operatora przecinka, jest zaciemnianie kodu .

Powiedzmy, że programista pisze taki kod:

var foo = 'bar';

Teraz postanawia zaciemnić kod. Użyte narzędzie może zmienić kod w następujący sposób:

var Z0b=(45,87)>(195,3)?'bar':(54,65)>(1,0)?'':'baz';// Z0b == 'bar'

Demo: http://jsfiddle.net/uvDuE/

Stephan
źródło
1
@gdoron Zapoznaj się z odpowiedzią stackoverflow.com/a/17903036/363573 dotyczącą operatora przecinka w języku C ++. Zauważysz komentarz Jamesa Kanze na temat zaciemniania.
Stephan