Monthly Archive for 七月, 2011

关于一些编程语言的运算符“优先级”

Tips:这个问题是铁军师兄发现并提出的,我写篇博客梳理一下。
当我们在学习一门编程语言的时候,我们经常会看到类似这样的一句话:运算符优先级是一套规则。该规则在计算表达式时控制运算符执行的顺序。具有较高优先级的运算符先于较低优先级的运算符执行。
那么事实是不是果真如此呢,下面让我们用Javascript等几门语言为例来验证一下。
首先,在官方的ECMA-262/Core JavaScript手册以及权威的MDN Docs中,分别是这样介绍Javascript运算符优先级的:

All operators have a pre-defined execution priority or precedence. Within a given complex expression, operations will be performed in order of priority.

Operator precedence determines the order in which operators are evaluated. Operators with higher precedence are evaluated first.

两段介绍的意思大致是一样的,都是说运算符优先级决定了运算符执行的先后顺序。运算符优先级高的运算符会被先执行(计算)。我们以往的编程经验似乎也在告诉我们这是真理,然而请先看下面一段代码:

var a=1;
function fn(){
    console.log(a);
}
var b = a++ + fn();

按照Javascript官方手册上对运算符优先级的定义以及的运算符优先级表(如图所示或参考http://rx4ajax-jscore.com/ecmacore/operator/predence.html),我们可以推算运行结果:在最后一行代码中,由于函数调用的括号运算符“()”优先级比自增运算符“++”和算术运算符“+”都要高,所以会先被执行。所以在控制台中被log的a的值应该是1。而事实上,结果是2。这说明了什么?——虽然“()”运算符的优先级比“++”要来得高,但a++却先于fn()被执行。
javascript优先级
图节选自Javascript运算符优先级表(http://rx4ajax-jscore.com/ecmacore/operator/predence.html)
那么在Javascript中是这种状况,在其他语言中又会是什么情况呢?随后,我们分别测试了PHP、C/C++、Java以及C#版本的代码(Python和Ruby中没有自增运算符)。猜猜结果各是什么?
PHP版:

$a=1;
function fn($a1){
    echo $a1;
}
$b = $a++ + fn($a);

C++版:

#include "stdafx.h"
#include <iostream>
using namespace std;
int fn(int a1)
{
	cout<<a1;
	return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
	int a = 1;
	int b = a++ + fn(a);
	int i;
	cin>>i;
	return 0;
}

揭晓答案:
只有C/C++输出了1,其他语言都输出了2。
这样看来似乎最传统的编译型语言C/C++能得出与其运算符优先级定义相符合的结果。而事实上关于运算符优先级,PHP手册是这样定义的:

The precedence of an operator specifies how “tightly” it binds two expressions together.

意思是运算符优先级决定了两个表达式之间“紧密程度”——也就是说代码的组合方式。这么一来PHP为什么输出2就能解释了。而C#和Java的代码,如果这么解释也能解释得清楚。不过在Java的运算符中,并没有函数调用的“()”运算符。C#当中却是有的。更有趣的是,在C/C++中,前自增/减运算符“++/–”的优先级居然排在了函数调用运算符“()”的前面,仅次于“::”运算符。
结论:混乱啊。。

一道Javascript面试题

var n = 2;
var showNumber = function(){
    n = 1;
    alert(n);
}
(function(){
    n=3;
    alert(n);
})()

你能说出alert出的内容并解释其原因吗?

事件委托在jQuery和KISSY内核中的应用

一直觉得jQuery中的 .live() 方法很神奇,居然可以对动态加入的元素进行绑定。今天终于弄明白了,原来这是通过使用事件委托实现的。

   $('a').live('click', function(){
      //code block1
   })
   

上面的这段代码可以约等于(但不完全等于,总结中说明了为什么):

   $(document).bind('click', function(e){
      if(e.srcElement.tagName == 'a'){
         //code block1
      }
   })
   

此外,jQuery还有一个 .delegate() 方法,可以实现接近 .live() 的效果:

   $('#container').delegate('a','click',function(){
      //code block2
   })
   

jQuery在文档中查找#container,并使用click事件和’a'这一CSS选择器作为参数,把函数绑定到$(‘#container’)上。任何时候只要有事件冒泡到$(‘#container’)上,它就查看该事件是否是click事件,以及该事件的目标元素是否与CSS选择器相匹配。如果两种检查的结果都为真的话,它就执行绑定的函数。
在KISSY 1.2版本中也引入了 .delegate() 方法。
这两种方法中,个人觉得还是 .delegate() 方法效率高。因为 .live() 方法需要把选择器匹配到的元素都包装成jQuery对象;而 .delegate() 方法只在委托的节点上监听冒泡事件。感觉用 .live() 方法进行事件委托比较鸡肋啊,可能这也是为什么其他js框架没有类似方法的原因吧。