一键搞定内网穿透 联行号查询|开户行查询 在线工具箱 藏经阁
当前位置:首页 / 杂记 / 正文
javascript js匿名函数/函数表达式/函数声明
参考:
JavaScript函数其三:分组中的函数表达式
http://www.nowamagic.net/librarys/veda/detail/1664
js中(function(){…})()立即执行函数写法理解
https://my.oschina.net/u/2331760/blog/468672?p=%7B%7BcurrentPage+1%7D%7D

函数表达式与函数声明

使用function关键字声明一个函数,再指定一个函数名,叫函数声明。
function a(){}
使用function关键字声明一个函数,但未给函数命名,最后将匿名函数赋予一个变量,叫函数表达式
var a = function(){};
函数声明和函数表达式都是在当前域(上下文环境)局部有效。
函数声明和函数表达式不同之处在于,
一、Javascript引擎在解析javascript代码时会将函数声明提升当前执行环境(作用域)上的函数声明,而函数表达式必须等到Javascirtp引擎执行到它所在行时,才会从上而下一行一行地解析函数表达式,
二、函数表达式后面可以加括号立即调用该函数,函数声明不可以,只能以fnName()形式调用 。以下是两者差别的两个例子。
三、函数表达式可以用在if等块中,函数声明不可以。
fnName();
function fnName(){
 ...
}//正常,因为‘提升’了函数声明,函数调用可在函数声明之前
 
fnName();
var fnName=function(){
 ...
}//报错,变量fnName还未保存对函数的引用,函数调用必须在函数表达式之后

var fnName=function(){
 alert('Hello World');
}();//函数表达式后面加括号,当javascript引擎解析到此处时能立即调用函数

function fnName(){
 alert('Hello World');
}();

匿名函数

使用function关键字声明一个函数,但未给函数命名,所以叫匿名函数
function(){}
function(){}();
//报错:Uncaught SyntaxError: Unexpected token (
//那是因为function开头,系统期待是函数声明,function后面应该是函数名,但现在是(。

function a(){}();
//报错:Uncaught SyntaxError: Unexpected token ) 这个函数会被js引擎解析为两部分:
 //1.函数声明 function a(){};
 //2.分组表达式 () 但是第二部分作为分组表达式语法出现了错误,因为括号内没有表达式,把“()”改为“(1)”就不会报错
 //但是这么做没有任何意义,只不过不会报错,分组表达式请见:
 //分组中的函数表达式http://www.nowamagic.net/librarys/veda/detail/1664
( function(){…} )()和( function (){…} () )这两种立即执行函数的写法,
最初我以为是一个括号包裹匿名函数,并后面加个括号立即调用函数,当时不知道为什么要加括号,
后来明白,要在函数体后面加括号就能立即调用,则这个函数必须是函数表达式,不能是函数声明。

分析

function(a){
  console.log(a); //报错,Uncaught SyntaxError: Unexpected token (
}(12);
(function(a){
 console.log(a); //firebug输出123,使用()运算符
})(123);
 
(function(a){
 console.log(a); //firebug输出1234,使用()运算符
}(1234));
 
!function(a){
 console.log(a); //firebug输出12345,使用!运算符
}(12345);
 
+function(a){
 console.log(a); //firebug输出123456,使用+运算符
}(123456);
 
-function(a){
 console.log(a); //firebug输出1234567,使用-运算符
}(1234567);
 
var fn=function(a){
 console.log(a); //firebug输出12345678,使用=运算符
}(12345678)   
//需要注意的是:这么写只是一个赋值语句,即把函数匿名函数function(a){...}()的返回值赋值给了fn,如果函数没有返回值,那么fn为undefined,
//下面给出2个例子,用来解答读者的疑惑:
var fn=function(a){
 console.log(a); //firebug输出12345678,使用=运算符
}(12345678);
console.info(fn);//控制台显示为undefined;
fn(123);//函数未定义报错,fn is undefiend 

var fn=function(a){
 console.log(a); //firebug输出12345678,使用=运算符
 return 111;
}(12345678);
console.info(fn);//会发现fn就是一个返回值111,而不是一个函数
fn(123);//报错,因为fn不是一个函数
可以看到输出结果,在function前面加!、+、 -甚至是逗号等到都可以起到函数定义后立即执行的效果,
而()、!、+、-、=等运算符,都将函数声明转换成函数表达式,消除了javascript引擎识别函数表达式和函数声明的歧义,告诉javascript引擎这是一个函数表达式,不是函数声明,可以在后面加括号,并立即执行函数的代码。
加括号是最安全的做法,因为!、+、-等运算符还会和函数的返回值进行运算,有时造成不必要的麻烦。
function(){}();
//报错:Uncaught SyntaxError: Unexpected token (
//那是因为function开头,系统期待是函数声明,function后面应该是函数名,但现在是(。

var a = function(){}();
//正确,因为系统会把=右侧按表达式去解析,这就没问题了,右侧是一个匿名函数并且被立即调用,最后调用的返回值赋给了a变量。
以下写法都正确,都是立即执行函数里的内容,解释如下:
(function(){ /*....*/ }());
//系统把()内的内容当作表达式来解析,这个写法类似于 var a = function(){}();
//var a = function(){}(); <===> var a = (function(){}());
(function(){ /*....*/ })();
//把"function (){ /*....*/ }"用()包起来,系统就不会当成它是函数名了而且当成函数,最后的()就是立即调用这个函数
[function(){ /*....*/ }()];//系统把[]内的内容当作表达式来解析
~ function(){ /*....*/ }();
! function(){ /*....*/ }();
+ function(){ /*....*/ }();
- function(){ /*....*/ }();
//系统把~!+-符号后面的内容当作函数表达式来解析,解析完后再和前面的符号计算,虽然这个计算无任何可用结果
delete function(){ /*....*/ }();
typeof function(){ /*....*/ }();
//相当于 typeof (function(){ /*....*/ }());,如果匿名函数返回值是3,那就是type 3,结果就是number
void function(){ /*....*/ }();
new function(){ /*....*/ }();//如果function无参,最后的()完全无用,去掉也可以
new function(){ /*....*/ };
new function(a){ /*....*/ }(3);//传参进去
//以上三种new写法最后都是new 函数对象,不是new 匿名函数的返回值。
//假如匿名函数返回值=3,new 完之后的结果是{}对象,并不是new 3
//new function(a){ /*....*/ }(3);类似于var tmp = function(a){},new tmp(3);

//虽然都立即执行函数里的内容,但加上前面关键词后的返回值是不一样的,
1 && function(){ /*....*/ }();
0 || function(){ /*....*/ }();//系统把 && || 右侧内容当成表达式解析

0 && function(){ /*....*/ }();//语法解析没错,但是&&右侧内容不对执行
1 || function(){ /*....*/ }();//语法解析没错,但是||右侧内容不对执行

//下面这些加a的和上面不加a的解释完全一样,看上面解释就行了
(function a(){ /*....*/ }());
(function a(){ /*....*/ })();
[function a(){ /*....*/ }()];
~function a(){ /*....*/ }();
! function a(){ /*....*/ }();
+ function a(){ /*....*/ }();
- function a(){ /*....*/ }();
delete function a(){ /*....*/ }();
typeof function a(){ /*....*/ }();
void function a(){ /*....*/ }();
new function a(){ /*....*/ }();
new function a(){ /*....*/ };