Когда полезен оператор запятой?

88

Я прочитал этот вопрос об «операторе запятой» в выражениях ( ,) и документах MDN об этом, но я не могу придумать сценарий, в котором это было бы полезно.

Итак, когда полезен оператор запятой?

gdoron поддерживает Монику
источник
2
var i, j, k;против var i; var j, var k?
Salman A
15
@SalmanA. Не уверен, что это как-то связано с ,оператором. Эта строка также действительна C#, но ,оператора там нет.
gdoron поддерживает Монику
2
@SalmanA. Я сделал. Не нашел, просвети меня ...
gdoron поддерживает Монику
5
@SalmanA a ,не всегда является ,оператором (и никогда не является ,оператором в C #). Таким образом, в C # может отсутствовать ,оператор, при этом он может свободно использоваться ,как часть синтаксиса.
Сет Карнеги,
8
Я думаю, что ответы здесь суммировали тот факт, что ,широко не используется (и не каждое вхождение a ,является оператором запятой) . Но вы можете позаимствовать его и массив, чтобы выполнить замену переменных на месте без создания временной переменной. Учитывая, что вы хотите поменять местами значения aи b, вы можете это сделать a = [b][b = a,0]. Это помещает ток bв массив. Второй []- это обозначение доступа к свойствам. Индекс доступа является 0, но не перед назначением aна b, который теперь безопасно , так как bсохраняются в массиве. ,позволяет нам сделать несколько выражений в [].

Ответы:

128

Следующее, вероятно, не очень полезно, поскольку вы не пишете его самостоятельно, но минификатор может сжимать код с помощью оператора запятой. Например:

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

станет:

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

? :Оператор теперь можно использовать, так как оператор запятая (в определенной степени) позволяет два заявления должны быть записаны как одно заявление.

Это является полезным в том , что она позволяет некоторым аккуратным сжатия (39 -> 24 байт здесь).


Я хотел бы подчеркнуть тот факт , что запятая в var a, bэто не оператор запятой , потому что не существует в пределах выражения . Запятая имеет особое значение в var отчетности . a, bв выражении будет ссылаться на две переменные и оценивать b, что не относится к var a, b.

pimvdb
источник
3
Как вы об этом думали? Вы где-нибудь читали? Это действительно используется?
gdoron поддерживает Монику
16
На днях я просто возился с Closure Compiler, чтобы посмотреть, что он на самом деле делает, и заметил эту замену.
pimvdb
2
Аналогичное использование, которое я считаю полезным в вашем коде, будет заключаться в назначении нескольких переменных во встроенном операторе if. Например: if (condition) var1 = val1, var2 = val2;я лично считаю, что избегание скобок там, где это возможно, делает код более читабельным.
Aidiakapi 03
2
Примерно единственное время, когда я использую оператор запятой, - это когда я добавляю операторы журнала к выражениям (foo => (console.log ('foo', foo), foo)), или если я становлюсь слишком умным с сокращением итераций . (pair.reduce ((acc, [k, v]) => (acc [k] = v, acc), {}))
Джозеф Сикорский
38

Оператор запятая позволяет помещать несколько выражений в место, где ожидается одно выражение. Результирующее значение нескольких выражений, разделенных запятой, будет значением последнего выражения, разделенного запятыми.

Я лично не использую его очень часто, потому что не так много ситуаций, когда ожидается более одного выражения, и нет менее запутанного способа написания кода, чем использование оператора запятой. Одна интересная возможность - это конец forцикла, когда вы хотите увеличить более одной переменной:

// 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
}

Здесь вы видите, что i++, j++это можно поместить в место, где разрешено одно выражение. В этом конкретном случае несколько выражений используются для побочных эффектов, поэтому не имеет значения, что составные выражения принимают значение последнего, но есть и другие случаи, когда это действительно может иметь значение.

jfriend00
источник
38

Оператор запятая часто бывает полезен при написании функционального кода на Javascript.

Рассмотрим этот код, который я написал некоторое время назад для SPA, в котором было что-то вроде следующего

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

Это был довольно сложный, но реальный сценарий. Потерпите меня, пока я объясню, что происходит, и в процессе изложу аргументы в пользу оператора запятой.


Это использует цепочку Underscore для

  1. Разберите все параметры, переданные в эту функцию, используя pairs которые превратятся { a: 1, b: 2}в[['a', 1], ['b', 2]]

  2. Этот массив пар свойств фильтруется по тому, какие из них считаются «действиями» в системе.

  3. Затем второй индекс в массиве заменяется функцией, которая возвращает обещание, представляющее это действие (используя map)

  4. Наконец, вызов reduceобъединит каждый «массив свойств» ( ['a', 1]) обратно в конечный объект.

Конечным результатом является преобразованная версия optionsаргумента, которая содержит только соответствующие ключи и значения которых потребляются вызывающей функцией.


Глядя только на

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

Вы можете видеть, что функция сокращения начинается с пустого объекта состояния state, и для каждой пары, представляющей ключ и значение, функция возвращает тот же stateобъект после добавления свойства к объекту, соответствующему паре ключ / значение. Из-за синтаксиса стрелочной функции ECMAScript 2015 тело функции является выражением, и в результате оператор запятая позволяет использовать краткую и полезную функцию «итерация» .

Лично я сталкивался с многочисленными случаями, когда писал Javascript в более функциональном стиле с помощью ECMAScript 2015 + Arrow Functions. Сказав это, до того, как столкнуться с функциями стрелок (например, во время написания вопроса), я никогда не использовал оператор запятой намеренно.

Syynth
источник
2
Это единственный действительно полезный ответ относительно того, как и когда программист может использовать оператор запятой. Очень полезно, особенно вreduce
hgoebl 08
1
С ES6 + это должен быть принятый ответ.
Арди Арам,
Хороший ответ, но если я могу предложить что - то чуть - чуть более читаемым: .reduce((state, [key, value]) => (state[key] = value, state), {}). И я понимаю, что это противоречит цели ответа, но .reduce((state, [key, value]) => Object.assign(state, { [key]: value }), {})полностью устраняет необходимость в операторе запятой.
Патрик Робертс
Хотя в наши дни Object.assign, вероятно, более идиоматичен, или даже просто оператор распространения, я не уверен, что они широко использовались в то время. Я также хотел бы указать, что, хотя оператор запятой немного более неясен, он может генерировать намного меньше мусора в ситуациях, когда набор данных очень велик. Деструктуризация, безусловно, улучшит читаемость!
Syynth
18

Другой вариант использования оператора запятой - скрытие результатов, которые вам не нужны, в реплике или консоли исключительно для удобства.

Например, если вы выполняете оценку myVariable = aWholeLotOfTextв repl или console, он распечатает все данные, которые вы только что назначили. Это могут быть страницы и страницы, и если вы не хотите их видеть, вы можете вместо этого оценить myVariable = aWholeLotOfText, 'done', и repl / console просто напечатает «готово».

Ориэл правильно отмечает †, что индивидуальные настройки toString()или get()функции могут даже сделать это полезным.

Джулиан де Бхал
источник
1
Ха, очень хорошая идея! (Наконец, ответ, который фактически отвечает на вопрос, в отличие от почти всех ответов {и 3 удаленных ответа, которые вам нужно 20K репутации, чтобы увидеть ...})
gdoron поддерживает Монику
1
Если присвоенное значение является объектом, консоль может попытаться отобразить его красиво. Для этого он может, например, вызвать геттеры, которые могут изменить состояние объекта. Это можно предотвратить с помощью запятой.
Oriol
@Oriol - Отлично! Вы совершенно правы. Каким-то образом эта потенциально полезная вещь немного разочаровывает :)
Джулиан де Бхал
13

Оператор запятая не является специфическим для JavaScript, он доступен на других языках, таких как C и C ++ . Как бинарный оператор это полезно, когда первый операнд, который обычно является выражением, имеет желаемый побочный эффект, требуемый вторым операндом. Один пример из Википедии:

i = a += 2, a + b;

Очевидно, вы можете написать две разные строки кода, но использование запятой - еще один вариант, который иногда более удобочитаем.

Taskinoor
источник
1
Считайте это альтернативой, хотя определение добра может варьироваться от человека к человеку. Однако я не могу найти ни одного примера, где вы ДОЛЖНЫ использовать запятую. Еще одна похожая вещь - тернарный оператор? :. Это всегда можно заменить на if-else, но иногда?: Делает код более читаемым, чем if-else. То же самое относится и к запятой.
taskinoor 06
Кстати, я не рассматриваю использование запятой в объявлении переменной или инициализацию нескольких переменных в цикле. В таких случаях лучше использовать запятую.
taskinoor 06
2
это выглядит сбивающим с толку черт возьми.
Timmerz
6

Я бы не согласился с Фланаганом и сказал, что запятая действительно полезна и позволяет писать более читаемый и элегантный код, особенно когда вы знаете, что делаете:

Вот очень подробная статья об использовании запятых:

Несколько примеров оттуда для доказательства демонстрации:

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

Генератор Фибоначчи:

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

Найдите первый родительский элемент, аналог .parent()функции 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">
шаман. сэр
источник
7
Я не уверен, что что-то из этого более читабельно, но это определенно довольно элегантно, так что +1
Крис Марисич
1
Вам не нужна запятая в цикле while в последнем примере, while ((el = el.parentNode) && (el.tagName != tagName.toUpperCase()))в этом контексте было бы хорошо.
PitaJ
5

Я не нашел практического применения, кроме этого, но вот один сценарий, в котором Джеймс Падолси прекрасно использует эту технику для обнаружения IE в цикле 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;

}());

Эти две строки должны выполняться:

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

И внутри оператора запятой оба оцениваются, хотя можно было как-то сделать их отдельными операторами.

Сарфраз
источник
3
Это могло быть do- whileпетля.
Кейси Чу
5

Есть кое-что «странное», что можно сделать в JavaScript, вызвав функцию косвенно с помощью оператора запятой.

Здесь есть подробное описание: Косвенный вызов функции в JavaScript

Используя этот синтаксис:

(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);
  
  }());

Вы можете получить доступ к глобальной переменной, потому что evalпри прямом вызове работает иначе, а при косвенном вызове.

Джереми Дж. Старчер
источник
4

Я считаю, что оператор запятой наиболее полезен при написании таких помощников.

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

Вы можете заменить запятую на || или &&, но тогда вам нужно знать, что возвращает функция.

Более важно то, что разделитель запятой сообщает о намерении - код не заботится о том, что оценивает левый операнд, тогда как альтернативы могут иметь другую причину для присутствия. Это, в свою очередь, упрощает понимание и рефакторинг. Если тип возвращаемого значения функции когда-либо изменится, это не повлияет на приведенный выше код.

Естественно, вы можете добиться того же другими способами, но не так кратко. Если || и && нашли место в общем использовании, как и оператор запятой.

Ламброс Симеонакис
источник
Подобно тому, что делает Ramda \ Lodash с tap( ramdajs.com/docs/#tap ). По сути, вы выполняете побочный эффект, а затем возвращаете начальное значение; очень полезен в функциональном программировании :)
avalanche1
1

Типичный случай, когда я в конечном итоге использую его, - это анализ необязательных аргументов. Я думаю, что это делает его более читабельным и лаконичным, так что синтаксический анализ аргументов не доминирует над телом функции.

/**
 * @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();

  // ...
}
Рич Ремер
источник
Хотя я сам не одобряю его, это единственный приведенный кем-либо пример, который, как мне кажется, человек мог бы сказать, что он лучше для читабельности, чем эквивалентный список отдельных выражений, не считая меня полным безумием.
Точка с запятой
1

Допустим, у вас есть массив:

arr = [];

Когда вы pushзаходите в этот массив, вас редко интересует pushвозвращаемое значение, а именно новая длина массива, а скорее сам массив:

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

Используя оператор запятой, мы можем нажать на массив, указать массив в качестве последнего операнда запятой, а затем использовать результат - сам массив - для последующего вызова метода массива, своего рода цепочки:

(arr.push('bar'), arr.push('baz'), arr).sort(); // [ 'bar', 'baz', 'foo' ]
Dexygen
источник
-2

Еще одна область, в которой можно использовать оператор запятой, - это обфускация кода. .

Допустим, разработчик пишет такой код:

var foo = 'bar';

Теперь она решает скрыть код. Используемый инструмент может изменить код следующим образом:

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

Демо: http://jsfiddle.net/uvDuE/

Стефан
источник
1
@gdoron Пожалуйста, ознакомьтесь с этим ответом stackoverflow.com/a/17903036/363573 об операторе запятой в C ++. Вы заметите комментарий Джеймса Канце об обфускации.
Стефан