在Javascript定义一个函数一般有如下三种方式:
函数关键字(function)语句:
代码如下 | 复制代码 |
function fnMethodName(x){alert(x);} |
函数字面量(Function Literals):
代码如下 | 复制代码 |
var fnMethodName = function(x){alert(x);} |
Function()构造函数:
代码如下 | 复制代码 |
var fnMethodName = new Function('x','alert(x);') |
匿名函数,就是没有名字的函数。如:
代码如下 | 复制代码 |
function (){ alert('a function');} |
但是,上面的代码会报错。firebug提示:function statement requires a name,也就是:函数必须要有个名字。
奇怪的是,如果我用一对()把这个没有名字的函数包起来,就不会报错了。如:
代码如下 | 复制代码 |
(function (){ alert('a function');}) |
(注意包裹函数的()!).虽然这样不会报错,但谁能知道这个函数是否声明成功了呢?是不是因为根本没声明所以才不报错呢?我们这样来测试:让函数自己执行一次:
代码如下 | 复制代码 |
(function (){ alert('a function');}()) |
可以看到,函数执行了,表明函数是存在的。
同样,如果这个时候去掉包裹函数的(),则依然会报前面那个错,函数也执行不了。。。
代码如下 | 复制代码 |
function (){ alert('a function');}() |
匿名函数
先撇开Google的代码,看一下JavaScript其实是支持这种形式的行数定义的:
代码如下 | 复制代码 |
function(msg){ alert(msg); }("hello world"); |
这其实是两步:第一步,定义了一个函数,
相当于:
代码如下 | 复制代码 |
var abc = function(msg) { alert(msg);} |
第二步,立刻执行它:
代码如下 | 复制代码 |
abc("hello world"); |
把两个语句拼接在一起,然后去掉abc这个函数的名字,就成了现在的语法形式。
封装
另外的一个好处,有可能是为了更好的封装。比如在这个函数里面,有很多的函数定义,比如
代码如下 | 复制代码 |
q(); p(); m(); g(); i() |
还有大量的变量:
代码如下 | 复制代码 |
var j; var h; var l; |
等等。在JavaScript里面没有简单的private这样的定义私有函数或者私有函数的方法。如果调用者可以随心所欲的访问到这些中间的(随时可能变化,甚至移除)成员。对于这么一个开肠破肚,一览无余的对象,从一个API提供者的角度来看(和使用者的角度来看),的确是个挺恐怖的事情。
如果放在一个匿名的函数里面,外界就再也没有办法直接访问到里面的函数了。内部的逻辑被完美的封装了起来。这样一来,这些函数和变量多么的安全!
函数字面量和Function()构造函数的区别
虽然函数字面量是一个匿名函数,但语法允许为其指定任意一个函数名,当写递归函数时可以调用它自己,使用Function()构造函数则不行。
代码如下 | 复制代码 |
var f = function fact(x) { if (x < = 1) return 1; else return x*fact(x-1); };Function() |
构造函数允许运行时Javascript代码动态的创建和编译。在这个方式上它类似全局函数eval()。
Function()构造函数每次执行时都解析函数主体,并创建一个新的函数对象。所以当在一个循环或者频繁执行的函数中调用Function()构造函数的效率是非常低的。相反,函数字面量却不是每次遇到都重新编译的。
用Function()构造函数创建一个函数时并不遵循典型的作用域,它一直把它当作是顶级函数来执行。
代码如下 | 复制代码 |
var y = "global"; function constructFunction() { var y = "local"; return new Function("return y"); // 无法获取局部变量 } alert(constructFunction()()); // 输出 "glob |