zoukankan      html  css  js  c++  java
  • JavaScript函数表达式

    在ECMAScript中,有两个最经常使用的创建函数对象的方法,即使用函数表达式或者使用函数声明。对此,ECMAScript规范明白了一点。即是,即函数声明 必须始终带有一个标识符(Identifier),也就是我们所说的函数名,而函数表达式则能够省略。

    函数声明的语法是这种:

    <script>
    	function functionName(){
    		函数体
    	}
    </script>

    首先是functionkeyword,这个keyword表示我声明的是一个函数,或者说我将要声明一个函数了,告诉浏览器一下;然后是函数的名字,我们讲函数的名字一定要见名知义,接着里面是我们的函数体,也能够了解为函数表达式。在各大主流浏览器里面都给函数定义了一个非标准的name属性。通过这个属性能够訪问到给函数指定的名字,这个属性的值永远等同于function后面的标识符;

    function functionName(){
    		
    	}
    	alert(functionName.name);

     函数声明解析步骤例如以下:
      1. 创建一个new Function对象,FormalParameterList指定參数,FunctionBody指定函数体。将当前正在执行环境中作用域链作为它的作用域。
      2. 为当前变量对象创建一个名为Identifier的属性。值为Result(1)。


    函数声明提升

    关于函数有一个重要的特性就是函数声明提升.意思是在读代替码前会先读取函数声明。这就意味着能够把函数声明放在调用它额语句后面;比如一下;

    alert(functionName.name);
    	function functionName(){	
    	}
    这个样例不会抛出错误,由于在alert之前会先去读取这个函数声明;

    函数表达式

    另外一种创建函数的方法是使用函数表达式:

    函数表达式:
      (函数表达式分为匿名和具名函数表达式)
      function Identifier opt( FormalParameterList opt){ FunctionBody }  //这里是具名函数表达式
      具名函数表达式的解析步骤例如以下:
      1. 创建一个new Object对象
      2. 将Result(1)加入到作用域链的顶端
      3. 创建一个new Function对象,FormalParameterList指定參数,FunctionBody指定函数体。

    将当前正在执行的执行环境中作用域链作为它的作用域。

      4. 为Result(1)创建一个名为Identifier 的属性,其值为为Result(3),仅仅读,不可删除
      5. 从作用域链中移除Result(1)
      6. 返回Result(3)
    简单来说。ECMAScript是通过上下文来区分这两者的:假如 function foo(){} 是一个赋值表达式的一部分。则觉得它是一个函数表达式。

    而假设 function foo(){} 被包括在一个函数体内,或者位于程序(的最上层)中,则将它作为一个函数声明来解析。

    显然,在省略标识符的情况下。“表达式” 也就仅仅能是表达式了。

    function foo(){}; // 声明,由于它是程序的一部分
    var bar = function foo(){}; // 表达式。由于它是赋值表达(AssignmentExpression)的一部分
    new function bar(){}; // 表达式,由于它是New表达式(NewExpression)的一部分
    (function(){
        function bar(){}; // 声明,由于它是函数体(FunctionBody)的一部分
    })(); 
     另一种情况:
    (function foo(){})
      这样的情况也是函数表达式,它被包括在一对圆括号里的函数。在其上下文环境中,()构成了一个分组操作符,而分组操作符仅仅能包括表达式。很多其它的样例:
    function foo(){}; // 函数声明
    (function foo(){}); // 函数表达式:注意它被包括在分组操作符中
     try {
    (var x = 5); // 分组操作符仅仅能包括表达式,不能包括语句(这里的var就是语句)
    } 
    catch(err) {
    // SyntaxError(由于“var x = 5”是一个语句,而不是表达式——对表达式求值必须返回值,但对语句求值则未必返回值。——译
    }
      以下简单说说函数声明与函数表达式的异同。

    声明和表达式的行为存在着十分微妙而又十分重要的区别。

      首先,函数声明会在不论什么表达式被解析和求值之前先行被解析和求值。

    即使声明位于源码中的最后一行,它也会先于同一作用域中位于最前面的表达式被求值。

    简单总结,差别在什么地方呢?
    1. 声明总是在作用域開始时先行解析; 
    2. 表达式在遇到时候才运算。
    函数声明还有另外一个重要的特点,即通过条件语句控制函数声明的行为并未标准化。因此不同环境下可能会得到不同的结果。即是: 
    // 千万不要这样做!
    // 不同浏览器会有不同返回结果。
    if (true) {
    function foo() {
    return 'first';
    }
    }
    else {
    function foo() {
    return 'second';
    }
    }
    foo();
    // 记住。这样的情况下要使用函数表达式:
    var foo;
    if (true) {
    foo = function() {
    return 'first';
    };
    }
    else {
    foo = function() {
    return 'second';
    };
    }
    foo();
     那么。使用函数声明的实际规则究竟是什么? 
      FunctionDeclaration(函数声明)仅仅能出如今Program(程序)或FunctionBody(函数体)内。

    从句法上讲,它们 不能出如今Block(块)({ ... })中。比如不能出如今 if、while 或 for 语句中。由于 Block(块) 中仅仅能包括Statement(语句), 而不能包括FunctionDeclaration(函数声明)这种SourceElement(源元素)。

      还有一方面。细致看一看产生规则也会发现,唯一可能让Expression(表达式)出如今Block(块)中情形。就是让它作为ExpressionStatement(表达式语句)的一部分。可是。规范明白规定了ExpressionStatement(表达式语句)不能以keywordfunction开头。

    而这实际上就是说。FunctionExpression(函数表达式)相同也不能出如今Statement(语句)或Block(块)中(别忘了Block(块)就是由Statement(语句)构成的)。

     
      因为存在上述限制,仅仅要函数出如今块中(像上面样例中那样),实际上就应该将其看作一个语法错误,而不是什么函数声明或表达式。
      那么我们应该在什么时候使用函数声明或函数表达式呢?函数声明仅仅能出如今“程序代码”中。意味着仅仅能在其他函数体中或者全局空间;它们的定义不能不能赋值给一个变量或属性。或者作为一个參数传递出如今函数调用中;以下的样例是函数声明的同意的使用方法,foo(),bar()和local()都是通过函数声明模式声明: 
    // 全局环境
    function foo() {}  
     
    function local() {  
    // 局部环境  
        function bar() {}  
            return bar;  
    }
    当你在语法上不能使用函数声明的时候。你就能够使用函数表达式。比方:传递一个函数作为參数或者在对象字面量中定义一个函数: 
    // 这是一个匿名函数表达式
    callMe(function () {  
     
    //传递一个函数作为參数
    });

    // 这是一个具名函数表达式
    callMe(function me() {  
    // 传递一个函数作为參数,函数名为me
    });  
    // 其它函数表达式
    var myobject = {  
        say: function () {  
     
    // I am a function expression  
    }  
    }; 
    这样的情况看起来好像是常规的变量赋值语句,即创建一个函数并将它复制给变量functionName.这样的情况下创建的函数叫匿名函数。

    由于functionkeyword后面没有标识符。

    匿名函数的name属性是空字符串。函数表达式与其它表达式一样,在使用前必须先复制。

    并且函数表达式并不会函数生, 提升,即先运行函数表达式,再声明会报错。

    Javascript有非常多有趣的使用方法,在Google Code Search里能找到不少。举一个样例:

    <script>
    ~function() {
        alert("hello, world.");
    }();
    </script>
    试一下就知道这段代码的意思就是声明一个函数,然后立马执行,由于Javascript中的变量作用域是基于函数的,所以这样能够避免变量污染,但这里的位运算符『~』乍一看让人摸不到头脑,假设去掉它再执行则会报错:SyntaxError。

    为什么去掉位操作符『~』后执行会报错。这是由于从语法解析的角度看。Javascript不同意在函数声明的后面直接使用小括号。而函数表达式则没有这个限制。通过在函数声明前面加上一个『~』操作符。就能够让语法解析器把后面看成是函数表达式,相同的。在函数声明前面加上『!。+,-』等操作符也是可行的。

    那我们为什么不使用以下这样的函数表达式的方式呢?

    <script>
    var foo = function() {
        alert("hello, world.");
    }();
    </script>

    尽管从语法解析的角度看没有问题,可是上面的代码存在弊端,它引入了一个变量,可能会污染现有的执行环境。带来潜在的问题。

    使用位操作符“~”的方法显得有点奇技淫巧,事实上把函数声明用小括号套起来更易读:

    <script>
    (function() {
        alert("hello, world.");
    })();
    </script>

  • 相关阅读:
    BZOJ3997:[TJOI2015]组合数学(DP,Dilworth定理)
    BZOJ4807:車(组合数学,高精度)
    BZOJ4008:[HNOI2015]亚瑟王(DP,概率期望)
    BZOJ1499:[NOI2005]瑰丽华尔兹(DP,单调队列)
    洛谷1514 引水入城
    洛谷 1018 乘积最大
    八数码难题
    CODEVS 1069关押罪犯
    CODEVS 1067 机器翻译
    洛谷 P1417 烹调方案
  • 原文地址:https://www.cnblogs.com/blfbuaa/p/7140534.html
Copyright © 2011-2022 走看看