逗号运算符什么时候有用?


88

我看了这个关于表达式(以下简称“逗号操作”的问题,)和MDN文档有关,但我想不出一个场景的地方是有用的。

那么,逗号运算符什么时候有用?


2
var i, j, k;var i; var j, var k
Salman A'3

15
@SalmanA。我不确定这与,操作员有什么关系。该行在其中也有效C#,但是,运算符在那里不存在。
gdoron支持Monica 2012年

2
@SalmanA。是的 没找到,给我
启发

5
@SalmanA a,并不总是,运算符(并且,在C#中也不是运算符)。因此,C#可能缺少,运算符,而仍然可以自由使用,作为语法的一部分。
赛斯·卡内基

8
我认为这里的答案总结了一个事实,,即a未被广泛使用(并非a的每次出现,都是逗号运算符)。但是您可以借用它和Array进行内联变量交换,而无需创建临时变量。假设您要交换aand的值b,则可以执行a = [b][b = a,0]。这会将电流b放置在数组中。第二个[]是属性访问符号。访问的索引是0,但在分配a给之前不存在b,由于b保留在数组中,因此现在是安全的。在,让我们做的多表达式[]

Answers:


128

以下内容可能不是很有用,因为您不是自己编写的,但是minifier可以使用逗号运算符收缩代码。例如:

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


3
您是怎么想的?您在哪里读过书吗?它真的被使用吗?
gdoron支持Monica 2012年

16
前几天,我只是在与Closure Compiler混在一起,看看它的实际作用,我确实注意到了这种替换。
pimvdb 2012年

2
我认为在您的代码中有用的类似用法是在内联if语句中分配多个变量。例如:if (condition) var1 = val1, var2 = val2;我个人认为尽可能避免使用方括号会使代码更具可读性。
Aidiakapi 2014年

2
大约只有在将日志语句添加到表达式中时才使用逗号运算符(foo =>(console.log('foo',foo),foo)),或者如果我对缩减迭代器变得太聪明。(pairs.reduce((acc,[k,v])=>(acc [k] = v,acc),{}))
Joseph Sikorski

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++可以将其放置在允许一个表达式的地方。在这种特殊情况下,多个表达式可用于副作用,因此复合表达式采用最后一个表达式的值并不重要,但是在其他情况下,这实际上可能很重要。


38

用Java编写功能代码时,逗号运算符通常非常有用。

考虑一下我为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. 然后,将数组中的第二个索引替换为一个函数,该函数返回表示该操作的Promise(使用map

  4. 最终,对的调用reduce会将每个“属性数组”(['a', 1])合并回最终对象。

最终结果是options参数的转换版本,该参数仅包含适当的键,并且其值可由调用函数使用。


看着只是

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

您可以看到reduce函数以一个空状态对象开头state,并且对于每个表示键和值的对,该函数state在将属性添加到与键/值对相对应的对象后,将返回同一对象。由于ECMAScript 2015的箭头函数语法,函数主体是一个表达式,因此,逗号运算符允许使用简洁而有用的“ iteratee”函数。

我个人遇到过很多案例,同时使用ECMAScript 2015 + Arrow Functions以更实用的样式编写Javascript。话虽如此,在遇到箭头功能之前(例如在编写问题时),我从未以任何故意的方式使用逗号运算符。


2
对于程序员如何/何时使用逗号运算符,这是唯一真正有用的答案。非常有用,尤其是在reduce
hgoebl

1
对于ES6 +,这应该是公认的答案。
Ardee Aram

尼斯的答案,但如果我可以建议一些稍微更具可读性:.reduce((state, [key, value]) => (state[key] = value, state), {})。而且我意识到这违反了答案的目的,但.reduce((state, [key, value]) => Object.assign(state, { [key]: value }), {})将完全不需要逗号运算符。
Patrick Roberts

尽管Object.assign如今可能更惯用,甚至只是散布运算符,但我不确定它们当时是否被广泛使用。我还要指出,尽管逗号运算符有点晦涩难懂,但是在数据集非常大的情况下,这可以减少很多垃圾。销毁当然会提高可读性!
Syynth

18

逗号运算符的另一种用途是纯粹为了方便而将您不关心的结果隐藏在repl或控制台中。

例如,如果您myVariable = aWholeLotOfText在repl或控制台中进行评估,它将打印您刚刚分配的所有数据。这可能是页面和页面,如果不想看到它,可以评估myVariable = aWholeLotOfText, 'done',并且repl / console只会打印“ done”。

Oriel正确指出定制toString()get()功能甚至可以使此功能有用。


1
哈,好主意!(最后一个实际上回答问题的答案与几乎所有答案都不相同(和3个删除的答案,您需要20K信誉才能看到...})
gdoron支持Monica,

1
如果分配的值是一个对象,则控制台可能会尝试很好地显示它。为此,它可能(例如)调用吸气剂,这可能会更改对象的状态。使用逗号可以防止这种情况。
Oriol

@Oriol-太好了!你是完全正确的。不知何故,这种潜在有用的感觉有点令人失望:)
朱利安·德·巴尔

13

逗号运算符不特定于JavaScript,它在其他语言(例如C和C ++)中可用。作为二进制运算符,当第一操作数(通常是表达式)具有第二操作数所需的所需副作用时,这很有用。维基百科的一个例子:

i = a += 2, a + b;

显然,您可以编写两行不同的代码,但是使用逗号是另一种选择,有时更易读。


1
尽管对商品的定义可能因人而异,但可以将其视为替代品。但是,我找不到您必须使用逗号的任何示例。另一个类似的东西是三元?:运算符。总是可以用if-else代替,但是有时?:比if-else更具可读性。同样的概念也适用于逗号。
taskinoor 2012年

顺便说一句,我不考虑在变量声明中使用逗号或在循环中初始化多个变量。在这些情况下,逗号通常更好。
taskinoor 2012年

2
这看起来令人困惑... wtf。
Timmerz

6

我不同意Flanagan,并且说逗号非常有用,并且允许编写更易读和优雅的代码,尤其是当您知道自己在做什么的时候:

这是有关逗号用法的非常详细的文章

那里有几个例子可以证明这一点:

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

7
我不确定我是否会说其中的任何一个更具可读性,但是它肯定很漂亮,所以+1
Chris Marisic 2014年

1
在上一个示例中,您不需要在while循环中使用逗号,while ((el = el.parentNode) && (el.tagName != tagName.toUpperCase()))在这种情况下就可以了。
PitaJ

5

除此之外,我还没有发现它的实际用途,但是在以下情况下,James Padolsey在while循环中很好地使用了该技术来进行IE检测

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循环。
Casey Chu

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的处理方式tapramdajs.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();

  // ...
}

尽管我自己并不喜欢它,但是这是任何人得到的唯一例子,我认为一个人可以说比同等的单个表达式语句列表更好的可读性,而我并不认为它们完全是疯狂的。
分号2015年

1

假设您有一个数组:

arr = [];

当您使用push该数组时,您几乎不会对push的返回值(即数组的新长度)感兴趣,而对数组本身感兴趣:

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

使用逗号运算符,我们可以压入数组,将数组指定为逗号的最后一个操作数,然后将结果(数组本身)用于后续的数组方法调用,这是一种链接:

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

-2

可以使用逗号运算符的另一个区域是代码混淆

假设开发人员编写了如下代码:

var foo = 'bar';

现在,她决定混淆代码。使用的工具可能会更改如下代码:

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

演示: http //jsfiddle.net/uvDuE/


1
@gdoron请查看有关C ++中逗号运算符的答案stackoverflow.com/a/17903036/363573。您会注意到James Kanze关于混淆的评论。
斯蒂芬2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.