逗号运算符何时有用?

时间:2012-03-06 07:27:58

标签: javascript comma-operator

我在表达式(,)和关于它的this中读到了关于“逗号运算符”的MDN docs问题,但我想不出它有用的场景。

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

14 个答案:

答案 0 :(得分:106)

以下内容可能不是很有用,因为您不自己编写,但是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则不是这样。

答案 1 :(得分:31)

逗号运算符允许您将多个表达式放在需要一个表达式的位置。由逗号分隔的多个表达式的结果值将是最后一个逗号分隔表达式的值。

我个人并不经常使用它,因为没有多少情况需要多个表达式,并且编写代码的方式不会比使用逗号运算符更容易混淆。一个有趣的可能性是在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++可以放在允许一个表达式的位置。在这种特殊情况下,多个表达式用于副作用,因此复合表达式具有最后一个的值并不重要,但在其他情况下,这可能实际上很重要。

答案 2 :(得分:28)

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

  4. 最后,对reduce的调用将合并每个&#34;属性数组&#34; (['a', 1])回到最终对象。

  5. 最终结果是options参数的转换版本,它只包含适当的键,其值可由调用函数使用。

    只看

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

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

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

答案 3 :(得分:16)

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

例如,如果您在repl或控制台中评估myVariable = aWholeLotOfText,它将打印您刚刚分配的所有数据。这可能是页面和页面,如果您不想看到它,则可以改为评估myVariable = aWholeLotOfText, 'done',并且repl / console将只打印&#39;完成&#39;。

Oriel正确地指出自定义toString()get()功能甚至可能使其有用。

答案 4 :(得分:12)

逗号运算符并非特定于JavaScript,而是以C and C++等其他语言提供。作为二元运算符,当第一个操作数(通常是表达式)具有第二个操作数所需的副作用时,这很有用。来自维基百科的一个例子:

i = a += 2, a + b;

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

答案 5 :(得分:5)

除此之外我还没有找到它的实际用法,但是这里有一个场景,其中 James Padolsey 在while循环中很好地使用了这个technique for IE detection

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]

在逗号运算符里面,虽然可以以某种方式使它们成为单独的语句,但它们都会被评估。

答案 6 :(得分:5)

我不同意Flanagan,并说,逗号非常有用,可以编写更易读,更优雅的代码,特别是当你知道自己在做什么时:

以下是逗号用法的greatly detailed article

来自那里的几个例子用于证明示范:

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 :(得分:4)

在JavaScript中可以通过使用逗号运算符间接调用函数来完成“奇怪”。

这里有一个很长的描述: Indirect function call in 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在直接调用时与间接调用的方式不同。

答案 8 :(得分:2)

我发现在编写此类助手时,逗号运算符最有用。

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

您可以用||代替逗号或&&,但是您需要知道该函数返回什么。

更重要的是,逗号分隔符传达意图-代码不在乎左操作数的计算结果,而替代方法可能有另一个原因因为在那里。反过来,这使得它更易于理解和重构。如果函数的返回类型发生变化,则上面的代码将不会受到影响。

自然地,您可以通过其他方式实现相同的目的,但并非那么简单。如果|| &&&找到了常用的地方,逗号运算符也找到了。

答案 9 :(得分: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();

  // ...
}

答案 10 :(得分:1)

它使您免于在嵌套条件中使用 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
                                                            )
}

答案 11 :(得分:0)

假设您有一个数组:

arr = [];

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

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

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

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

答案 12 :(得分:0)

我今天刚刚在查看管道运营商提案和部分应用的提案时遇到了这个...

<块引用>

此外,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)

这是另一个使用它来组合函数的例子:

const double = (x) => 2 * x;
const add = (x, y) => x + y;
const boundScore = (min, max, score) => Math.max(min, Math.min(max, score));


const calculateScore = ($) => (
  $= double($),
  $= add($, 20),
  $= boundScore(0, 100, $),
  (console.log($), $)
)

const score = calculateScore(28)

答案 13 :(得分:-3)

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

假设开发人员写了一些这样的代码:

var foo = 'bar';

现在,她决定对代码进行模糊处理。使用的工具可能会改变代码:

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

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