让我们从一个有趣的微博开始吧。
末尾的c是优先级最低的逗号操作符。逗号操作符是操作符优先级的最后一行,并且很少有文章记录,它隐藏着它的锋芒。它可能不是JavaScript强势操作符,但是我喜欢它。它简单、优雅并且你应该让它成为你的朋友。所以,我们开始吧--你需要更多了解JavaScript这个害羞英雄。
它是做什么的?
逗号操作符评估它的操作数(从左到右)并返回第二个操作数的值。(MDC)
var a = (7, 5);
a; //5
var x, y, z;
x = (y=1, z=4);
x; //4
y; //1
z; //4
你为什么把这些变量放在括号里?
因为运算符的优先级。一个JavaScript语句可以保护多个不同的运算符,下面语句包含三个运算符(*,+和,);
return 5 * 2 + 3, 22;
在一个语句中运算符优先级决定了运算符执行的顺序。运算符优先顺序的完整列表在这里。逗号运算符是所有运算符优先级最低的操作符。让我们模拟上面的例子怎么运行的:
// 原始的
return 5 * 2 + 3, 22;
// 运行*操作符
return 10 + 3, 22;
//运行+操作符
return 13, 22;
//运行 , 操作符
return 22;
现在让我们使用这些知识来看看如果我们不把这些变量放在括号里会发生什么:它有效的拥有最高的优先级。确保操作运算符优先执行:
//原始语句
var a = (7, 5);
// 执行组
var a = 5;
//原始语句
var a = 7, 5;
//运行 = 运算符
var a, 5; //a 现在等于7
//SyntaxError: missing variable name
通过将等号右边表达式放在括号里,我们创建了一个组 - 它有效的拥有最高的优先级。这个确保了逗号运算符能首先执行:
//原始语句
var a = (7,5);
//运行组
var a = 5;
在实践中,最低的运算符优先级确实使逗号操作符非常强大。事实上逗号操作符说:去吧,先去看看所有其他小的操作符,然后再来我这决斗结果。
一些语句中包含多个逗号。它们怎么运行的?
上面的规则仍然适用。在语句中每个逗号操作符从左到右有序的运行着。
var a = (1, 2, 3, 4);
a; //4
这个等于:
var a = (((1, 2), 3), 4);
a; //4
逗号用于文本类型和声明怎么样?
这些是逗号分隔符而不是逗号操作符。逗号分隔符的目的是将成员划分到列表中。例如:
//设置4个数组元素
var arr = [1, 2, 3, 4];
//创建拥有2个属性的对象
var obj = {
a: 22,
f: function(){return this.a * this.a}
}
//定义3个不同的变量
var a = 1, b = 2, c = 3;
// 调用一个函数并传递2个参数
Math.max(4, 7);
为什么使用逗号操作符?
因为他们让你指定多个表达式,而JavaScript预计只有一个。逗号操作符很少是必要的,但是通常很有用,偶尔地非常优雅:
1 var r = [], n = 0, a = 0, b = 1, next;
2
3 function nextFibonacici(){
4 next = a+b;
5 return b =(a=b, next);
6 }
7
8 while(n++ < 10){
9 r.push(nextFibonacici();)
10 }
11
12 r; //[1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
function getRandomPrime(){
while(n = Math.round(Math.random()*1000000000), !isPrime(n));
return n;
}
var isPrime = function(n){
d = Math.ceil(Math.sqrt(n));
while(n%(d--) && d);
return !d;
}
getRandomPrime(); //298207139
getRandomPrime(); //308547853
难道逗号操作符只是一个伪装的分号?
分号操作符分隔语句。逗号操作符分隔语句中的表达式。
为什么不使用逻辑与&&操作符来评估多个表达式的顺序?
逗号操作符是逻辑与&&和逻辑或操作符的亲密的表弟。三个操作符都会返回他们评估的最后一个表达式。他们的区别也简单:
1 //(LHE: left hand expression, RHE right hand expression)
2
3 LHE && RHE
4 1. 总是会执行左边的表达式
5 2. 如果左边是true,执行右边
6
7 LHE || RHE
8 1. 总是会执行左边表达式
9 2. 如果左边是false,执行右边
10
11 LHE, RHE
12 1. 总是执行左边表达式
13 2. 总是执行右边表达式
当两个表达式都必须要执行时,选择逗号操作符。
来点更多的例子咋样?
好的。前面我提到了逗号操作符使你指定多个表达式,而JavaScript只预定一个。这个或许是在for循环的范围:
for loops
这个是另外一个版本的斐波那契,也使用逗号操作符:
for(
var i = 2, r = [0,1];
i<15;
r.push(r[i-2]+r[i-1]), i++
);
r; // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]
另一个例子,一个实用实例,帮助商品店员为客户选择钞票和硬币。下面是一个基本的版本。我们使用逗号操作符来等分for循环的第二个表达式。这使我们在测试限制表达式之前增加货币计算器:
function toCurrency(total, values){
total *= 100;
for(
var i =0, counts = [];
counts[i] = total/values[i], total = total % values[i];
i++);
return counts.map(Math.floor);
}
toCurrency(32.47, [500, 100, 25, 10, 5, 1]); //[6, 2, 1, 2, 0, 2]
现在下面是相同的实用实例,增加了对用户友好的格式:
function toCurrency(total, values, sym){
total *= 100;
// do the calc
for(
var i=0, counts = [];
counts[i]=total/values[i],total=total%values[i];
i++
);
//format
var results = counts.map(function(s,i){
return s>=1&&[Math.floor(s),"x",(sym || '$')+(values[i]/100).toFixed(2)].join(' ');
});
return results.filter(Boolean).join(', ');
}
toCurrency(19.77, [500,100,25,10,5,1]);
//"3 x $5.00, 4 x $1.00, 3 x $0.25, 2 x $0.01"
toCurrency(19.77, [500,100,50,20,10,5,1], '£');
//"3 x £5.00, 4 x £1.00, 1 x £0.50, 1 x £0.20, 1 x £0.05, 2 x £0.01"
toCurrency(19.77, [500,100,50,20,10,5,2,1], '€');
//"3 x €5.00, 4 x €1.00, 1 x €0.50, 1 x €0.20, 1 x €0.05, 1 x €0.02"
下面函数在一个for循环中使用逗号操作符来同时增加和减少两个计数器。计数器用来在控制台上渲染迷人的曲线:
function renderCurve(){
for(var a=1,b=10;a*b;a++,b--)
console.log(new Array(a*b).join('*'));
}
renderCurve()
/*
*********
*****************
***********************
***************************
*****************************
*****************************
***************************
***********************
*****************
*********
*/
while循环
你可以使用逗号操作符来创建一个简洁的do-while循环版本。搜索tag匹配的元素祖先。我们再次使用逗号来检查限制表达式:
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">
三元条件
三元语法允许每三个组件只有一个表达式。作为一般规则,如果你要使用多个声明,你需要考虑使用if else。然而有时在三元表达式中使用逗号操作符来比较简单的表达式更具有可读性:
//player loses
lives ? (lives--, go()):(gameOver(), exit());
调试
逗号操作符提供了一种低调的方式来注入console logs到你的代码中,而不必重新格式化(你能发现在每种情况下需要调试的错误吗?)。。。
//包含一个故意的错误!!!!
// 当i>n时输出总数
var i=10,n=0,total=0;
while(console.log(i,n),i-->n++);{
total += i*n
}
/*
10 0
9 1
8 2
7 3
6 4
5 5
24
*/
//包含一个故意的错误!!!!
// 总数是数组
var arr = [1,2,3];
for(
var i=0, total=0;
i<arr.length;
console.log(i,total), total += arr[i++]
);
/*
0 0
1 1
2 3
*/
//包含一个故意的错误!!!
//数组成员增加4,并求和
//(是的有更容易的方法来做到这一点)
var testArray = [3, 5, 8, 4], total = 0;
var plusFour = testArray.map(function(e){e + 4});
plusFour.forEach(function(n){console.log(n), isNaN(n) || (total += n)});
/*
undefined
undefined
undefined
undefined
*/
迭代器绑定
var colorIndex = 0,
colors = ["FF0000", "008000", "FF0086", "A2FF00", "0000FF", "800080"];
function selectNextColor(){
return colors[colorIndex++] || colors[colorIndex = 0, colorIndex++];
}
间接调用eval
eval1通常在他们包含上下文调用(例如在运算代码中的this值与周围代码的this值一样)。这是有问题的,因为无法保证重复的eval调用源于相同的上下文。
如kangax描述,我们可以使用时尚的逗号操作符来间接调用eval这将迫使它在全局上下文中执行2:
var a = {};
//attempt eval in context of object <code>a</code>
(function() {
eval("this.alert('If you can read this I must be global!')");
}).call(a);
//TypeError: this.alert is not a function
//force eval in global context
(function() {
(0,eval)("this.alert('If you can read this I must be global!')");
}).call(a);
//alerts: 'If you can read this I must be global!'
¹ eval的优点的讨论超出了本文的范围。
² 虽然ES5标准确认间接调用eval应该在全局范围内运行,但是不是所有浏览器都兼容(例如,IE<=8)。
总结
你可能没有使用逗号操作符,但可以写很好的JavaScript代码。这是否意味着我只是浪费了你的时间?我希望不是。正如一个广泛的词汇让我们成为更好的演讲者和作者,所以广泛的接触语言特征让我们成为更好的程序员。我们可支配的技术越多,我们就有更多的能力写出优雅、简洁的、可读的代码。
用逗号操作符编写有趣的代码,请分享你的整洁的用法示例!