一次在写js的时候遇到这样一个问题,如果定义了一个function函数,而调用在定义之前,能不能成功调用呢?
如果是按正常的思维应该是不可以的,因为javascript是解释型语言,解释过程是从上至下依次解析js语句的,在定义之前调用是找不到function的,但是往往javascript总是给人带来无尽的困惑。看下面的例子:
1 f(); //f()是存在的,正确调用 2 3 function f(){ 4 alert("正确调用了f()"); 5 }
感觉奇怪了,这似乎超出了我对javascript以往的理解,然而查阅多方信息原来javascript中的函数定义是分为“函数声明”和“函数表达式”两种方式的。
什么是函数声明?就是上面那种方式,我们用function关键字来定义一个有名字的函数:function f(){...}
什么是函数表达试?是用var来定义一个变量来指向一个函数:var f=function(){...}
这两种方式虽然都可定义一个函数,但却有本质的区别,请看下面的例子:
1 f(); //f()是存在的,正确调用 2 test(); //出错,因为test还未定义 3 4 /** 5 * 函数表达式 这种方式只在定义之后才可调用 6 */ 7 var test = function(){ 8 alert("正确调用了test()"); 9 }; 10 11 /** 12 * 函数声名 如果是函数声明,在js文件解析时就已经定义了 13 */ 14 function f(){ 15 alert("正确调用了f()"); 16 }
到这里似乎明白了些,js在解析时,是将函数声名部分从上至下预先解析的,这样不论何时调用函数,哪怕是在定义之前都是可以的,而函数表达式却不可以。
至此可以算是明白了这两种方式的区别,但javascript总会出一些令人纠结的问题,比如在一次前端面试的时候就遇到类似这样的问题,请看代码:
1 f(); 2 3 function f(){ 4 alert("正确调用了f() 第1次"); 5 } 6 7 function f(){ 8 alert("正确调用了f() 第2次"); 9 } 10 11 var f=function(){ 12 alert("正确调用了f() 第3次"); 13 }; 14 15 f(); 16 17 var f=function f(){ 18 alert("正确调用了f() 第4次"); 19 } 20 21 f();
如果按照上面的理解,此程序应当是什么结果?
你可能会说结果是“正确调用了f()第2次”、“正确调用了f()第3次”、“正确调用f()了第4次”,可真的是这样吗?如果你是用Chrome来运行这段代码是100%正确的,但我希望你将这段代码拷到ie中再试一试你就会失望了。在ie中结果居然是“正确调用了f()第4次”、“正确调用了f()第3次”、“正确调用f()了第4次”!是Chrome错了,还是ie错了?
刚刚说过了javascript的函数定义分为表达式和声名之分,但在代码的第17行处:var f=function f(){...},居然声名了一个有名字的函数,而又有一个变量指向了这个有名字的函数,说真的当我第一次看到种近乎变态的写法时认为这是错的,但在浏览器中运行他却是真真切切能运行起来的,只不过Chrome与ie却按不同的方式理解罢了。
如果是在Chrome中,这种写法被理解为“表达式”,而在ie中却是“函数声明”,这就不难解释为什么ie的结果与Chrome不同了。
上面的问题是我在一次前端面试时遇到的,考官事前就说了这题有陷阱,但还是对js了解太浅,还是掉进去了,其实对于js的函数声名还有更深层的知识可学,比如网上传说的这道腾迅面试题:
1 f = function() {return true;}; 2 g = function() {return false;}; 3 (function() { 4 if (g() && [] == ![]) { 5 f = function f() {return false;}; 6 function g() {return true;} 7 } 8 })(); 9 alert(f()); // true or false ?
结合上面对函数定义的理解,这道题的结果是什么?在ie与非ie浏览器中可是不一样的。
本人功力太浅薄,网上找到一个对js函数定义讲解的非常深入的文章,贴出来与大家分享:
http://www.cn-cuckoo.com/main/wp-content/uploads/2009/12/named-function-expressions-demystified.html