zoukankan      html  css  js  c++  java
  • 函数声明和函数表达式的区别

    我们从一些小测试开始。以下情况都会弹出什么结果?

    题 1:

    function foo(){

        function bar() {

            return 3;

        }

        return bar();

        function bar() {

            return 8;

        }

    }

    alert(foo());

    题 2:

    function foo(){

        var bar = function() {

            return 3;

        };

        return bar();

        var bar = function() {

            return 8;

        };

    }

    alert(foo());

    题 3:

    alert(foo());

    function foo(){

        var bar = function() {

            return 3;

        };

        return bar();

        var bar = function() {

            return 8;

        };

    }

    题 4:

    function foo(){

        return bar();

        var bar = function() {

            return 3;

        };

        var bar = function() {

            return 8;

        };

    }

    alert(foo());

    如果你的答案不是8、3、3和 [Type Error: bar is not a function] 的话,就继续往下读吧……(即使答对了也要继续读哦)

    什么是 Function Declaration(函数声明)?

    Function Declaration 可以定义命名的函数变量,而无需给变量赋值。Function Declaration 是一种独立的结构,不能嵌套在非功能模块中。可以将它类比为 Variable Declaration(变量声明)。就像 Variable Declaration 必须以“var”开头一样,Function Declaration 必须以“function”开头。

    e.g.

    function bar() {

        return 3;

    }

    ECMA 5(13.0)定义语法:

    function Identifier ( FormalParameterList[opt] ) { FunctionBody }

    函数名在自身作用域和父作用域内是可获取的(否则就取不到函数了)。

    function bar() {

        return 3;

    }

    bar() //3

    bar  //function

    什么是 Function Expression(函数表达式)?

    Function Expression 将函数定义为表达式语句(通常是变量赋值)的一部分。通过 Function Expression 定义的函数可以是命名的,也可以是匿名的。Function Expression 不能以“function”开头(下面自调用的例子要用括号将其括起来)。

    e.g.

    //anonymous function expression

    var a = function() {

        return 3;

    }

    //named function expression

    var a = function bar() {

        return 3;

    }

    //self invoking function expression

    (function sayHello() {

        alert("hello!");

    })();

    ECMA 5(13.0)定义语法:

    function Identifieropt ( FormalParameterList[opt] ) { FunctionBody }

    (这个定义感觉并不完整,因为它忽略了一个条件:外围语句是表达式,并且不以“function”开头)

    函数名(如果有的话)在作用域外是不可获取的(与 Function Declaration 对比)。

    那 Function Statement 是什么?

    Function Statement 有时是 Function Declaration 的另一种说法。但是 kangax 指出,在 mozilla 中,Function Statement 是 Function Declaration 的一种拓展,使得 Function Declaration 语句可以在任何允许使用 statement(语句)的地方使用。但是 Function Statement 现在还不是标准,所以不建议应用在产品开发中。

    现在来解释下前面的测试。

    Question 1 用了 function declaration,也就是说它们 get hoisted(被提升)了……

    等一下,什么是 Hoisting?

    这里引用 Ben Cherry的话:“Function declaration和function variable(函数变量)通常会被 JavaScript 解释器移(‘hoisted’)到当前作用域顶部”。

    function declaration 被提升时,整个函数体都会随之提升,所以 Question 1 的代码经过解释器解释后是像这样运行的:

    //**Simulated processing sequence for Question 1**

    function foo(){

        //define bar once

        function bar() {

            return 3;

        }

        //redefine it

        function bar() {

            return 8;

        }

        //return its invocation

        return bar(); //8

    }

    alert(foo());

    但是,我们经常被告诉说,return 语句后面的代码是运行不到的啊……

    执行 JavaScript 过程中,有 Context(ECMA 5 将之分解为 LexicalEnvironment、VariableEnvironment 和 ThisBinding)和 Process(一系列按序调用的语句)两个概念。当程序进入执行域时,Declaration 会造成 VariableEnvironment。它们不同于 Statement(比如 return),也不遵循 Statement 的运行规则。

    Function Expression 会被提升吗?

    这取决于表达式。比如 Question 2 中的第一个表达式:

    var bar = function() {

        return 3;

    };

    等号左边的(var bar)是 Variable Declaration。Variable Declaration 会被提升,但是 Assignment Expression(赋值表达式)不会。所以当 bar 提升时,解释器会这样初始化:var bar = undefined。而函数定义本身不会被提升。

    (ECMA 5 12.2 带有 initialzier(初始化器)的变量是在 VariableStatement 执行时,由 AssignmentExpression 赋值的,而不是在变量被创建时。)

    因此 Question 2 的代码会按以下顺序运行:

    //**Simulated processing sequence for Question 2**

    function foo(){

        //a declaration for each function expression

        var bar = undefined;

        var bar = undefined;

        //first Function Expression is executed

        bar = function() {

            return 3;

        };

        // Function created by first Function Expression is invoked

        return bar();

        // second Function Expression unreachable

    }

    alert(foo()); //3

    你可能会说,这还能解释的通,但是 Question 3 的答案错了,我在 Firebug 运行会报错。

    把代码保存在 HTML 文件中,之后在 Firefox 上运行试试。或者在 IE8、Chrome 或 Safari 控制台中运行。显然 Firebug 控制台在“global(全局)”作用域(实际并不是全局的,而是特有的“Firebug”作用域——试着在 Firebug 控制台中运行“this == window”你就知道了)运行代码时,不会将函数提升。

    Question 3 和 Question 1 的逻辑相似。这次是 foo 函数被提升了。

    Question 4 就很简单了,根本就没有函数提升……

    可以这么说,但是如果根本没有提升的话,TypeError 会是“bar not defined”,而不是“bar not a function”。此例中确实没有函数提升,但是有变量提升。因此 bar 在开始就被声明了,但是它的值并没有定义。其它代码都是按顺序执行的。

    //**Simulated processing sequence for Question 4**

    function foo(){

        //a declaration for each function expression

        var bar = undefined;

        var bar = undefined;

        return bar(); //TypeError: "bar not defined"

        //neither Function Expression is reached

    }

    alert(foo());

    还应该注意什么?

    官方是禁止在非功能模块(比如 if)中使用 Function Declaration 的。但是所有浏览器都支持,但是各自的解释方式不同。

    例如下面的代码段在 Firefox 3.6 中会抛错,因为它将 Function Declaration 解释成了 Function Statement(见上文),所以 x 没有定义。但是在 IE8、Chrome 5 和 Safari 5 中,会返回函数 x(和标准的 Function Declaration 一样)。

    function foo() {

        if(false) {

            function x() {};

        }

        return x;

    }

    alert(foo());

    可以看出使用 Function Declaration 可能会引起混淆,那么它有什么优点吗?

    你可能会说 Function Declaration 很宽松啊——如果试图在声明前使用函数,提升确实可以修正顺序,以便函数可以正确调用。但是这种宽松不利于严谨的编码,从长远的角度来看,很有可能会促进而不是阻止意外的发生。毕竟,程序员按特定的顺序排列语句是有原因的。

    那么还有其它理由支持 Function Expression 的吗?

    你猜呢?

    1)Function Declaration 感觉像是要模仿 Java 风格的方法声明,但是 Java 方法和 JavaScript 并不一样。在 JavaScript 中,函数是含值的 living 对象。Java 方法仅是对元数据的存储。下面的两段代码都定义了函数,但是只有 Function Expression 看着像创建了对象。

    //Function Declaration

    function add(a,b) {return a + b};

    //Function Expression

    var add = function(a,b) {return a + b};

    2)Function Expression 用处更多。Function Declaration 只能作为“statement”孤立存在。它所能做的就是创建一个当前作用域下的对象变量。相比之下,Function Expression(根据定义)是大型结构的一部分。如果想要创建匿名函数、给 prototype(原型)添加函数或是将函数用作其它对象的 property(属性),都可以用 Function Expression。每当用高阶应用,比如 curry 或 compose,创建新的函数时都是在用 Function Expression。Function Expression 和 Functional Programming(函数式编程)分不开。

    //Function Expression

    var sayHello = alert.curry("hello!");

    Function Expression 有缺点吗?

    Function Expression 创建的函数大多是匿名的。比如下面的函数是匿名的,today 只是一个匿名函数的引用:

    var today = function() {return new Date()}

    这会有问题吗?多数情况下不会,但是就像 Nick Fitzgerald 指出的,调试匿名函数会很烦。他建议使用 Named Function Expressions (NFEs)作为工作区:

    var today = function today() {return new Date()}

    但是如 Asen Bozhilov 所说(和 Kangax 文档)NFEs 在 IE9 以下无法正确执行。

    结论

    随意放置的 Function Declaration 具有误导性,并且很少有(如果有的话)情况,用 Function Expression 给变量赋值无法替代 Function Declaration。但是如果必须使用 Function Declaration 的话,将其放在所属作用域顶部可以减少混淆。永远不要把 Function Declaration 放在 if 语句中

  • 相关阅读:
    A Simple Problem with Integers poj 3468 多树状数组解决区间修改问题。
    Fliptile 开关问题 poj 3279
    Face The Right Way 一道不错的尺取法和标记法题目。 poj 3276
    Aggressive cows 二分不仅仅是查找
    Cable master(二分题 注意精度)
    B. Pasha and String
    Intervals poj 1201 差分约束系统
    UITextField的快速基本使用代码块
    将UIImage转换成圆形图片image
    color转成image对象
  • 原文地址:https://www.cnblogs.com/ghlucky/p/5805128.html
Copyright © 2011-2022 走看看