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

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

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

15 ответов

Решение

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

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,

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

Лично я не использую его очень часто, потому что не так много ситуаций, когда ожидается более одного выражения, и нет менее запутанного способа написания кода, чем использование оператора запятой. Одна интересная возможность в конце 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++ можно поместить в место, где разрешено одно выражение. В этом конкретном случае множественные выражения используются для побочных эффектов, поэтому не имеет значения, что составные выражения принимают значение последнего, но существуют другие случаи, когда это может иметь значение.

Оператор запятой часто полезен при написании функционального кода на 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), {})

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

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

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

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

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

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

i = a += 2, a + b;

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

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

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

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

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

Найти первый родительский элемент, аналог jQuery .parent() функция:

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">

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

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

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

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

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

Есть что-то "странное", что можно сделать в 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 работает по-разному, когда вызывается напрямую против косвенно.

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

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

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

arr = [];

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

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

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

(arr.push('bar'), arr.push('baz'), arr).sort(); // [ 'bar', 'baz', 'foo' ]

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

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

  // ...
}

Я только что наткнулся на это сегодня, глядя на предложения по предложению оператора трубопровода и частичному применению ...

Кроме того, конвейеры в стиле Hack уже возможны без введения нового синтаксиса сегодня:

      let $; // Hack-style topic variable
let result = (
  $= books,
  $= filter($, _ => _.title = "..."),
  $= map($, _ => _.author),
  $);

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

Устранение пространства между $= имитирует ощущение правильного жетона трубки, |>. Обратите внимание, что переменная "тема", $, здесь может быть что угодно и что это просто сокращение для многократной перезаписи переменной. Так что что-то более похожее на ...

      // blocking inside an IIFE
let result = (() => {
  let $;
  $ = books;
  $ = filter($, _ => _.title = "..."),
  $ = map($, _ => _.author),
  return $;
})()

Версия с «запятой» успешно устраняет некоторые шумы, приближая вас к тому, что было бы предложением:

      let result = books
  |> filter($, _ => _.title = "..."
  |> map($, _ => _.author)

Вот еще один пример его использования для составления функций:

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

пусть х = 1;

х = (х++, х);

консоль.лог(х); // ожидаемый результат: 2

х = (2, 3);

консоль.лог(х); // ожидаемый результат: 3

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator

Это избавляет вас от использования returnво вложенных условных выражениях, и это очень удобно, особенно с тернарным оператором. Такой как;

      function insert(v){
  return this.node > v ? this.left.size < this.right.size ? ( this.left.insert(v)
                                                            , this
                                                            )
                                                          : ( this.left.insert(this.node)
                                                            , this.node = this.right.popmin()
                                                            , this.insert(v)
                                                            , this
                                                            )
                       : this.left.size < this.right.size ? ( this.right.insert(this.node)
                                                            , this.node = this.left.popmax()
                                                            , this.insert(v)
                                                            , this
                                                            )
                                                          : ( this.right.insert(v)
                                                            , this
                                                            )
}

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

Скажем, разработчик пишет такой код:

var foo = 'bar';

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

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

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

Другие вопросы по тегам