缘起

javascript和其他编程语言相比比较随意,所以javascript代码中充满各种奇葩的写法,有时雾里看花,当然,能理解各型各色的写法也是对 javascript语言特性更进一步的深入理解,那么他有几种写法呢?

 ( function(){…} )()

或者

 ( function (){…} () )

首先要明白两个知识点

js中函数是引用类型;
函数一般执行方式:函数名+();

下面的例子帮你理解引用类型

var a = function(x,y){
console.log(x + y);
};
var b = a;
a(1,2);
b(1,2); //b,a指向同一个函数对象 //b重新赋值
b = function(x,y){
console.log(x - y);
}
a(1,2);
b(1,2);

函数的几种定义方式

js函数有普通函数、构造函数、匿名函数,定义方式有三种,即函数声明方式(function declaration, abbreviation as FD)、函数表达式方式(function expression, abbreviation as FE)、函数对象方式(对象的方式定义函数,前面参数为函数的参数,最后为函数体。但是需要解析传入的字符串参数,导致两次解析,所以不推荐这种方式来定义函数)

1、函数声明方式

//不会报错,但是javascript引擎只解析函数声明,忽略后面的括号,函数声明不会被调用
function fnName(){
alert('Hello World');
}();
//可以执行
alert(sum(1,2));
function sum(x,y){
return x + y;
}

 2、函数表达式方法

//这段代码会报错
alert(sum(1,2));
var sum = function (x,y){
return x + y;
}
//函数表达式后面加括号,当javascript引擎解析到此处时能立即调用函数
var show=function(){
alert('Hello World');
}();

 3、函数对象方法

var sum = new Function('value1', 'value2', 'return value1 + value2');

在写递归的时候可以这样写

//如果直接用sum(x-1) + sum(x-2),如果sum被改名,或者重新赋值,产生bug
var sum = function fSum(x){
if(x<=2)
return 1;
else
return fSum(x-1) + fSum(x-2);
};
alert(sum(5));

 4、匿名函数

使用function关键字声明一个函数,但未给函数命名,所以叫匿名函数,匿名函数属于函数表达式,匿名函数有很多作用,赋予一个变量则创建函数,赋予一个事件则成为事件处理程序或创建闭包等等

function () {}

区别

1、FD是在构建函数的Execution Context时会被计算并作为Activation Object的一个属性被引用,因此就出现declaration hoisting的现象。而FE则是在函数的Runtime中才被计算,而且不会作为Activation Object的一个属性被引用,也就是说FD会被解析器通过函数声明提升的过程即function declaration hoisting置于原代码数的顶部,所以即使在函数前调用该函数也可以正常使用

2、而函数表达式方式除了不能在声明前调用外,与函数声明方式一样

3、函数对象方法可以直观地理解“函数是对象,函数名是指针”这个概念,但是它会造成解析器两次解析,一次是普通的ECMAScript代码,一次是解析传入 Function构造函数里的字符串,会影响js引擎性能

4、函数表达式后面可以加括号立即调用该函数,函数声明不可以

立即执行函数表达式

我们在使用JavaScript的时候经常会看见类似如下的函数调用方式

(function(){
console.log("test");
})();

或者

(function(){
console.log("test");
}());

比如jQuery

(function( window, undefined ) {
// code here
}) ( window );

这种写法有两种称呼

「自执行匿名函数」(self-executing anonymous function),
「立即执行函数表达式」(Immediately-Invoked Function Expression,以下简称IIFE)

还有一些奇葩的定义方式

// 如果本身就是expression,那么根本不需要做任何处理
var i = function(){ return 10; }();
true && function(){ /* code */ }();
0, function(){ /* code */ }(); // 如果你不在乎返回值,可以这么做
!function(){ /* code */ }();
~function(){ /* code */ }();
-function(){ /* code */ }();
+function(){ /* code */ }(); // 还有更奇葩的方式,但是不知道性能如何,来自 new function(){ /* code */ }
new function(){ /* code */ }()

为什么要用立即执行函数表达式

1、模拟块作用域

众所周知,JavaScript没有C或Java中的块作用域(block),只有函数作用域,在同时调用多个库的情况下,很容易造成对象或者变量的覆盖,比如

liba.js

var num = 1;
// code....

libb.js

var num = 2;
// code....

如果在页面中同时引用liba.jsliba.js两个库,必然导致num变量被覆盖,为了解决这个问题,可以通过IIFE来解决:

liba.js

(function(){
var num = 1;
// code....
})();

libb.js

(function(){
var num = 2;
// code....
})();

2、解决闭包冲突

闭包(closure)是JavaScript的一个语言特性,简单来说就是在函数内部所定义的函数可以持有外层函数的执行环境,即使在外层函数已经执行完毕的情况下,在这里就不详细介绍了,感兴趣的可以自行Google。我们这里只举一个由闭包引起的最常见的问题

var f1 = function() {
var res = [];
var fun = null;
for(var i = 0; i < 10; i++) {
fun = function() { console.log(i);};//产生闭包
res.push(fun);
} return res;
} // 会输出10个10,而不是预期的0 1 2 3 4 5 6 7 8 9
var res = f1();
for(var i = 0; i < res.length; i++) {
res[i]();
}

修改成:

var f1 = function() {
var res = [];
for(var i = 0; i < 10; i++) {
// 添加一个IIFE
(function(index) {
fun = function() {console.log(index);};
res.push(fun);
})(i);
} return res;
} // 输出结果为0 1 2 3 4 5 6 7 8 9
var res = f1();
for(var i = 0; i < res.length; i++) {
res[i]();
}

可以参考http://segmentfault.com/q/1010000003490094

3、模拟单例

在JavaScript的OOP中,我们可以通过IIFE来实现,如下

var counter = (function(){
var i = 0;
return {
get: function(){
return i;
},
set: function( val ){
i = val;
},
increment: function() {
return ++i;
}
};
}()); counter.get(); // 0
counter.set( 3 );
counter.increment(); // 4
counter.increment(); // 5

参考:
http://benalman.com/news/2010/11/immediately-invoked-function-expression/#iife
http://blog.coolaj86.com/articles/how-and-why-auto-executing-function.html
http://stackoverflow.com/questions/592396/what-is-the-purpose-of-a-self-executing-function-in-javascript
http://www.cnblogs.com/TomXu/archive/2011/12/31/2289423.html
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function

JavaScript函数的多种定义方法的更多相关文章

  1. 解析JavaScript函数的多种写法

    本文主要分析了JavaScript中函数的几种写法,具体如下: 1.函数的声明和表达式(旧方法,也是最常见的方法) 2.通过Function构造器 这也是一种从一开始就存在方法,但是因为书写麻烦等原因 ...

  2. ASP.NET后台中调用前台Javascript函数的几种方法

    做web开发,用的技术是aspx.net,可是由于比较习惯于ASP现在做起来,觉得非常别扭,原因在于有很多功能其实在前台可以处理的,但是因为用到了很多webcontrol,导致不断postback.如 ...

  3. js 匿名函数 js-函数定义方法

    1.任何函数都是有返回值的,没有返回值的,在某些语言里称之为过程例如PL/SQL 2.js中的函数如果没有return 关键字指明给出的返回值,那么当调用完函数后,会返回“undefined" ...

  4. Javascript 函数和模块定义

    匿名函数 // calculator.js(function(root) {  var calculator = {    sum: function(a, b) { return a + b; }  ...

  5. JavaScript 函数定义方法

    JavaScript 函数定义方法. 函数声明 在之前的教程中,你已经了解了函数声明的语法 : function functionName(parameters) { 执行的代码 } 函数声明后不会立 ...

  6. JavaScript 函数定义

    JavaScript 使用关键字 function 定义函数. 函数可以通过声明定义,也可以是一个表达式. 函数声明 在之前的教程中,你已经了解了函数声明的语法 : function function ...

  7. JavaScript函数认识,Js中的常见函数

    JavaScript函数: 也称为方法,用来存储一块代码,需要的时候调用. 函数是由事件驱动的或者当它被调用时执行的可重复使用的代码块. 函数需要包含四要素:返回类型,函数名,参数列表,函数体 拓展: ...

  8. JavaScript 函数与对象的 简单区别

    直接上例子 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <met ...

  9. JS如何定义方法及调用 精选

    简单搜索了下,遇到点问题1,经常在JS中看到如var foo = function(){}的形式foo是方法名还是对象名,如果想调用此方法,是用foo(),foo.function(),还是该如何正确 ...

随机推荐

  1. git 查看commit提交的内容

    在使用git的过程中,我们经常需要查看某次commit修改了哪些内容,与之相关的命令就是: git log git show 首先,需要通过git log打印所有commit hashID,之后的gi ...

  2. Lucene TFIDF打分公式

    还没读TFIDFSimilarity的代码,读了一下lucene的文档,没有特复杂,感觉还是非常严谨的. 对于查询q和文档d,如果查询为纯token查询,套用向量空间模型(VSM),相似度度量使用余弦 ...

  3. 【BZOJ】【2435】【NOI2011】道路修建

    DFS/DP 本来以为是一道傻逼题,然而跪了好久……一直RE…… 直接dfs就好了……x->y val=c  :  ans+=abs(n-size[y]-size[y])*c; 然而为啥会一直R ...

  4. 使用自由软件Icarus Verilog Simulator进行仿真

    Icarus Verilog Simulator(http://iverilog.icarus.com/home)使用iverilog作为源代码编译器,编译生成vvp程序文本,使用vvp作为运行时引擎 ...

  5. 80端口占用异常解决方法java.net.BindException: Address already in use: JVM_Bind:80(或8080)

    1:Tomcat(或其他Web容器)启动时控制台报错如下示: 2007-8-2 15:20:43 org.apache.coyote.http11.Http11Protocol init 严重: Er ...

  6. Android中Fragment的简单介绍

    Android是在Android 3.0 (API level 11)引入了Fragment的,中文翻译是片段或者成为碎片(个人理解),可以把Fragment当成Activity中的模块,这个模块有自 ...

  7. spring cloud 报错Error creating bean with name 'hystrixCommandAspect' ,解决方案

    spring cloud 升级到最新版 后,报错: org.springframework.beans.factory.BeanCreationException: Error creating be ...

  8. 思维导图软件xmind和mindmanager哪个更好

    思维导图是一种将放射性思考具体化的方法,可以将人们的创造性思维及时捕捉并呈现,目前便捷的网络为人们带来了众多的思维导图软件,而在这些软件中只有亲身实践体验过,才能知道到底思维导图哪个好,哪个又适合自己 ...

  9. (转)Unity3D Android手机开发环境配置,可真机发布调试

    此方法配置好,在可以在unity直接发布到手机上,并可以实时调试. 1.配置eclipse环境:首先在官网下载安装包:http://developer.android.com/sdk/index.ht ...

  10. 将Tp-link无线路由器桥接到Dlink无线路由器上

    笔者家中原有两台笔记本和两台IPad,通过一台Dlink无线路由器(型号DIR-612,以下简称Dlink)上网,Dlink以PPPOE方式连到小区宽带.一直还可以. 后来为了练习Linux,启用了一 ...