js函数表达式和函数声明的区别

我们已经知道,在任意代码片段外部添加包装函数,可以将内部的变量和函数定义“隐 藏”起来,外部作用域无法访问包装函数内部的任何内容。

例如:

var a = 2;
function foo() { // <-- 添加这一行
var a = 3;
console.log( a ); // 3
} // <-- 以及这一行
foo(); // <-- 以及这一行
console.log( a ); // 2

虽然这种技术可以解决一些问题,但是它并不理想,因为会导致一些额外的问题。首先, 必须声明一个具名函数 foo(),意味着 foo 这个名称本身“污染”了所在作用域(在这个 例子中是全局作用域)。其次,必须显式地通过函数名(foo())调用这个函数才能运行其 中的代码。

如果函数不需要函数名(或者至少函数名可以不污染所在作用域),并且能够自动运行, 这将会更加理想。

幸好,JavaScript 提供了能够同时解决这两个问题的方案、

var a = 2;
(function foo(){ // <-- 添加这一行
var a = 3;
console.log( a ); // 3
})(); // <-- 以及这一行
console.log( a ); // 2

接下来我们分别介绍这里发生的事情。

首先,包装函数的声明以 (function... 而不仅是以 function... 开始。尽管看上去这并不 是一个很显眼的细节,但实际上却是非常重要的区别。函数会被当作函数表达式而不是一 个标准的函数声明来处理。 区分函数声明和表达式最简单的方法是看 function 关键字出现在声明中的位 置(不仅仅是一行代码,而是整个声明中的位置)。如果 function 是声明中 的第一个词,那么就是一个函数声明,否则就是一个函数表达式。

函数声明和函数表达式之间最重要的区别是它们的名称标识符将会绑定在何处。 比较一下前面两个代码片段。第一个片段中 foo 被绑定在所在作用域中,可以直接通过 foo() 来调用它。第二个片段中 foo 被绑定在函数表达式自身的函数中而不是所在作用域中。 换句话说,(function foo(){ .. }) 作为函数表达式意味着 foo 只能在 .. 所代表的位置中 被访问,外部作用域则不行。foo 变量名被隐藏在自身中意味着不会非必要地污染外部作 用域。

以下部分原文出处: http://www.cnblogs.com/isaboy/p/javascript_function.html

Javascript Function无处不在,而且功能强大!通过Javascript函数可以让JS具有面向对象的一些特征,实现封装、继承等,也可以让代码得到复用。但事物都有两面性,Javascript函数有的时候也比较“任性”,你如果不了解它的“性情”,它很可能给你制造出一些意想不到的麻烦(bugs)出来。

  Javascript Function有两种类型:

  1)函数声明(Function Declaration);

    // 函数声明
    function funDeclaration(type){
        return type==="Declaration";
    }

  2)函数表达式(Function Expression)。

    // 函数表达式
    var funExpression = function(type){
        return type==="Expression";
    }

  上面的代码看起来很类似,感觉也没什么太大差别。但实际上,Javascript函数上的一个“陷阱”就体现在Javascript两种类型的函数定义上。下面看两段代码(分别标记为代码1段和代码2段):

1     funDeclaration("Declaration");//=> true
2     function funDeclaration(type){
3         return type==="Declaration";
4     }
1     funExpression("Expression");//=>error
2     var funExpression = function(type){
3         return type==="Expression";
4     }
  用函数声明创建的函数funDeclaration可以在funDeclaration定义之前就进行调用;而用函数表达式创建的funExpression函数不能在funExpression被赋值之前进行调用。为什么会这样呢?!这就要理解Javascript Function两种类型的区别:用函数声明创建的函数可以在函数解析后调用(解析时进行等逻辑处理);而用函数表达式创建的函数是在运行时进行赋值,且要等到表达式赋值完成后才能调用。这个区别看似微小,但在某些情况下确实是一个难以发现的陷阱。出现这个陷阱的本质原因体现在这两种类型在Javascript function hoisting(函数提升)和运行时机(解析时/运行时)上的差异。关于变量提升,可参见我的另外一篇博文http://www.cnblogs.com/isaboy/p/javascript_hoisting.html。上面两段代码的函数提升可示意为下图: 

代码1段JS函数等同于:
    function funDeclaration(type){
        return type==="Declaration";
    }
    funDeclaration("Declaration");//=> true

代码2段JS函数等同于:
    var funExpression;
    funExpression("Expression");//==>error
    funExpression = function(type){
        return type==="Expression";
    }

  上述代码在运行时,只定义了funExpression变量,但值为undefined。因此不能在undefined上进行函数调用。此时funExpression赋值语句还没执行到。为了进一步加深JS函数两种类型的区别,下面给出一个更具迷惑性的示例,请看下面的代码(代码段4):

 1     var sayHello;
 2     console.log(typeof (sayHey));//=>function
 3     console.log(typeof (sayHo));//=>undefined
 4     if (true) {
 5         function sayHey() {
 6             console.log("sayHey");
 7         }
 8         sayHello = function sayHo() {
 9             console.log("sayHello");
10     }
11     } else {
12         function sayHey() {
13             console.log("sayHey2");
14         }
15         sayHello = function sayHo() {
16             console.log("sayHello2");
17         }
18     }
19     sayHey();// => sayHey2
20     sayHello();// => sayHello

  分析:sayHey是用函数声明创建的,在JS解析时JS编译器将函数定义进行了函数提升,也就是说,在解析JS代码的时候,JS编译器(条件判断不形成新的作用域,两个sayHey函数定义都被提升到条件判断之外)检测到作用域内有两个同名的sayHey定义,第一个定义先被提升,第二个定义接着被提升(第二个定义在第一个定义之下),第二个定义覆盖了第一个sayHey定义,所以sayHey()输出sayHey2;而sayHello是用函数表达式创建的,其表达式的内容是在JS运行时(不是解析时)才能确定(这里条件判断就起到作用了),所以sayHello表达式执行了第一个函数定义并赋值,则sayHello()输出sayHello。

  代码段4的代码实际上等同于下面的代码(代码段5):

 1     var sayHello;
 2     function sayHey() {
 3             console.log("sayHey");
 4         }
 5     function sayHey() {
 6             console.log("sayHey2");
 7     }
 8     console.log(typeof (sayHey));//=>function
 9     console.log(typeof (sayHo));//=>undefined
10     if (true) {
11         //hoisting...
12         sayHello = function sayHo() {
13             console.log("sayHello");
14     }
15     } else {
16         //hoisting...
17         sayHello = function sayHo() {
18             console.log("sayHello2");
19         }
20     }
21     sayHey();// => sayHey2
22     sayHello();// => sayHello

  有的人也许会怀疑函数sayHey的定义是第二个覆盖第一个了么?我们可以把sayHey的源代码进行输出,有图有真相,如下图所示:

  总结

  Javascript 中函数声明和函数表达式是存在区别的,函数声明在JS解析时进行函数提升,因此在同一个作用域内,不管函数声明在哪里定义,该函数都可以进行调用。而函数表达式的值是在JS运行时确定,并且在表达式赋值完成后,该函数才能调用。这个微小的区别,可能会导致JS代码出现意想不到的bug,让你陷入莫名的陷阱中。

  最后附上代码段4中sayHello和sayHey两个函数的核心步骤(个人理解,若有异议欢迎留言探讨):

  上图为sayHello函数执行的主要步骤示意图。

时间: 2024-10-26 20:13:40

js函数表达式和函数声明的区别的相关文章

JS中函数表达式与函数声明的区别

hello,沐晴又来更新啦,今天呢,跟大家讲讲让人头疼的函数表达式和函数声明,反正我当初看那本高级程序的时候,是没怎么看太透,哈哈.我是个比较重基础的人,跟我一起探讨函数表达式和函数声明的世界吧. 首先呢,先看看他们的颜值:   函数声明:function 函数名(){}   函数表达式:function 函数名(可写可不写)(){}; (不写名叫做匿名函数表达式.写名叫做命名函数表达式.推荐用匿名的.) 这么一看长的好像没啥区别,反正颜值也都不高.那么我们如何区别是函数表达式还是函数声明呢?答

变量声明置顶规则、函数声明及函数表达式和函数的arguments属性初始化

一.变量声明和变量赋值: if (!("a" in window)) { var a = 1; } alert(a);//a为? 你可能认为alert出来的结果是1,然后实际结果是"undefined".要了解为什么,我们需要知道JavaScript里的3个概念: 1.所有的全局变量都是window的属性,语句 var a = 1;等价于window.a = 1; 可以用如下方式来检测全局变量是否声明: "变量名称" in window 2.声明

为什么if else 语句里不能用函数声明定义函数,而可以用函数表达式定义函数

在<JavaScript高级程序设计>第三版第7章函数表达式部分讲到,定义函数有两种方式:一种是函数声明,另一种就是函数表达式.函数声明的语法是这样的.function functionName(arg0, arg1, arg2) {//函数体} 关于函数声明,它的一个重要特征就是函数声明提升( function declaration hoisting),意思是在执行代码之前会先读取函数声明.这就意味着可以把函数声明放在调用它的语句后面.sayHi();function sayHi(){al

函数表达式和函数声明~(自己的记录)

在ECMAScript中,创建函数的最常用的两个方法是函数表达式和函数声明,两者期间的区别是有点晕,ECMA的规范明确了一点~函数声明必须带有标示符 (Identifier)就是大家说的(函数名),而函数表达式则可以省略函数名. 那么没有函数名那肯定就是函数表达式了,那么有函数名时,如何区分它是函数表达式还是函数声明呢~~如果是作为赋值表达式的一部分则为函数表达式,如果是包含在一个函数体内,或者位于程序的最顶部的话则作为函数声明~ function foo(){} // 声明,因为它是程序的一部

函数表达式和函数声明

学习文章----汤姆大叔的博客 总结笔记 函数声明: 1.函数声明必须要有名称 2.位置只能在全局上下文和函数体内,不能出现在代码块(if)中 函数表达式: 特点:不会影响上下文的全局对象VO var foo = {}; (function initialize() { var x = 10; foo.bar = function () { alert(x); }; })(); foo.bar(); // 10; alert(x); // "x" 未定义 1.表达式可以没有名称,表达式

javascript笔记05:函数表达式和函数语句的区别

1.首先是函数语句: myfunc(); function myfunc() { //执行一些语句 } 当函数语句被定义的时候,在一个脚本代码被优先考虑,因此,无论该函数是定义之前或者定义之后都可以被脚本使用,就像上面的语句一样. 2.函数表达式: myfunc(); var myfunc = function() { //执行的语句 } 这上面的语句是无法成功运行的,只有像下面的语句才可以成功运行额: var myfunc = function() { //执行的语句 } myfunc();

理解函数表达式的函数名称

函数表达式: function 函数名称(可选)(参数:可选){函数体}. 之前不怎么理解:既然函数表达式里的函数名称可选,那有函数名与没函数名又有什么区别呢. 按照EMCA标准,函数表达式格式如下: FunctionExpression : function Identifier opt ( FormalParameterList opt ){ FunctionBody } 这种函数表达式又称有名函数表达式(named function expression),这个名称只能在函数内部使用 va

JS函数-函数声明和函数表达式之间的恩怨

"函数声明"和"函数表达式"其实说白了 就是定义函数的方式 函数声明: function 函数名(){.....} 通过上面方式定义的函数就是函数声明 函数表达式:通过函数表达式定义函数的方式比较多 1. var a = function test(){...} // 这是命名的函数表达式 var a = function(){...} // 这是匿名的函数表达式 2. 将"函数声明"定义的函数 用一对小括号括起来,这样也形成了函数表达式 (fu

javascript 函数声明和函数表达式的区别(学习笔记)

javascript中声明函数的方法有两种:函数声明式和函数表达式. 区别如下: 1).以函数声明的方法定义的函数,函数名是必须的,而函数表达式的函数名是可选的. 2).以函数声明的方法定义的函数,函数可以在函数声明之前调用,而函数表达式的函数只能在声明之后调用. 3).以函数声明的方法定义的函数并不是真正的声明,它们仅仅可以出现在全局中,或者嵌套在其他的函数中,但是它们不能出现在循环,条件或者try/catch/finally中,而 函数表达式可以在任何地方声明. 下面分别用两种方法定义函数: