• [翻译]函数:声明和表达式


      昨天看了一篇文章《命名函数表达式探秘》,但我对函数表达式和声明之间的区别还是不太清楚,所以查了些资料,翻译了这篇文章

      文章很基础,但是讲的很清晰。

      以下译文:

      

    函数:声明和表达式

    函数,像变量一样,可以在代码的任意地方定义它。
    JS 提供了几个方法去定义它们。
         1.函数声明     (Function Declaration)
         2.函数表达式  (Function Expression)
         3.通过调用new Function 返回。

    语法
         创建一个函数的基本方法是通过一个函数声明。语法如下:
         
    function f(arg1, arg2, ...) {
       ... code ...
    }
         
         实现起来像这样子:
              
    1 function sayHi(name) {
    2   alert("Hi, "+name)
    3 }
    4  
    5 sayHi('John')
         
         上面的例子声明了一个带有一个参数name,函数名为sayHi的函数,并且调用了它。

    返回值

         我们用return语句来返回一个值。
         
    1 function sum(a, b) {
    2   return a+b
    3 }
    4  
    5 var result = sum(2,5)
    6 alert(result)

         如果函数不返回任何东西,它的结果 会是一个特殊的值,undefined。你可以从下面的代码看到。

         
    1 function getNothing() {
    2   // no return
    3 }
    4  
    5 var result = getNothing()
    6  
    7 alert(result)
         
         一个空的返回也是一样:
         
    1 function getNothing() {
    2   return
    3 }
    4 alert( getNothing() === undefined ) // true

    局部变量

         一个函数可以包括变量,通过var来定义。这些变量被称为局部变量,而且只能在函数内部使用。
         
    1 function sum(a, b) {
    2   var sum = a + b
    3  
    4   return sum
    5 }

    函数声明

         函数的声明的解析是在预执行(pre-execution)阶段,也就是浏览器准备执行代码的时候。
         
         因此,通过函数声明来定义函数,可以在定义前或后被调用。
         
         下面的代码是可以跑的:

         
    1 function sayHi(name) {
    2   alert("Hi, "+name)
    3 }
    4  
    5 sayHi("John")

         换个顺序也可以:
         
         
    1 sayHi("John")
    2  
    3 function sayHi(name) {
    4   alert("Hi, "+name)
    5 }

         一个函数可以在代码任意位置声明。

         


    比如,我们可能要根据条件来声明不同的函数:

    1 sayHi()
    2  
    3 if (1) {
    4   function sayHi() {  alert(1)  }
    5 else {
    6   function sayHi() {  alert(2)  } // <--
    7 }
    在写这篇文章的时候(作者本人没有给出他这这篇文章的时间——译者注),尝试在不同浏览器,火狐会抛出异常,其他两个浏览器都会给出结果 2。

    这是因为声明是在代码执行前解析的。根据规范(p.10.5),后面函数会覆盖前面存在的同名函数。

    到了执行时期,声明会被忽略。所以if语句没有起作用。

      函数表达式

         在JS里面,函数function和number、string一样,是一阶值(原文为:first-class value,有人也译作:‘一等公民’)
         
         只要可以放置变量的地方,都可以放置函数。在那个‘地方’用函数表达式的语法来声明:function (arguments) {}

         例如,你可以这样使用变量:
         var f = 5
         也可给f赋值一个函数表达式:
         
    1 var f = function(name) {
    2     alert("Hi, " + name + "!");
    3 }

         

    'function' 关键字什么时候用作 表达式,什么时候又用作 声明

    规则很简单

    当js解析器看到function出现在主码流( main code flow 不知如何翻译),function被认为是声明。

    当 function 作为语句(statement)的一部分出现的,都会是表达式。

    (译者认为,可以看能不能加分号来判断,能加分号的都为语句(statement)。)
         函数表达式会在执行流( execution flow)执行到它的时候才会创建函数。所以,函数表达式必须被执行了(此时函数才定义了)才能被调用。

         
    01 var sayHi
    02  
    03 // sayHi() <-- can't call here, there is no sayHi yet
    04  
    05 if (1) {
    06   sayHi = function() {  alert(1)  }
    07 else {
    08   sayHi = function() {  alert(2)  }
    09 }
    10  
    11 sayHi()

         上面的例子,在所有浏览器执行的结果都一样。

    请用函数声明

         有经验的开发者,会将函数通过表达式来定义。
    ... code ...
    var f = function() { ... }
    ...
         
         函数声明会更短更有可读性。
    ... code ...
    function f() { ... }
    ...

         此外函数这样定义可以在定义前调用。
         
         在你真正需要用函数表达式的使用它!特别是在有条件判断的时候定义函数。

    函数是一个值

         函数在JS里面是一个基本类型。我们甚至可以输出它:
         
    1 function f() { alert(1) }
    2  
    3 alert(f)
         
         上面的例子会输出这个function,通常是输出这个函数的源代码

         论是函数声明还是表达式,对函数来说,只是创建的时间不同而已。

    传递一个函数
         
         想所有值一样,不管是以哪种方式定义,函数可以被赋值,作为参数传递到另外的函数等等。
         
    1 function sayHi(name) {
    2   alert("Hi, "+name)
    3 }
    4  
    5 var hi = sayHi // assign a function to another variable
    6  
    7 hi("dude")     // call the function

         函数的传递是引用类型。也就是说,函数会存在内存的某个位置,sayhi是它的一个引用(如果你懂指针的话)。当把sayhi赋给hi的时候,两者都会引用到同一个函数。

         函数可以接受另一个函数作为参数。
         
    1 function runWithOne(f) {  // runs given function with argument 1
    2   f(1)
    3 }
    4  
    5 runWithOne(
    6   function(a){ alert(a) }
    7 )

         逻辑上,函数是一个行为(action)。所以传递一个函数就是传递了一个可以被其他程序启动的行为。这个特性在js中广泛使用。

         在上面的例子中,我们创建了一个没有名字的函数,而且没有把它赋给其他变量。这种函数被称为匿名函数。

    就地运行

         我们可以使用函数表达式创建一个函数并马上执行它。

         
    1 (function() {
    2  
    3   var a, b    // local variables
    4   
    5   // ...      // and the code
    6  
    7 })()

         我们涉及到局部变量的时候常用这种技巧。我们不想让局部变量污染到全局变量,所以我们把代码包在一个函数里面。

         在代码执行后,全局变量还是干净的。这是一个最佳实践。

         为什么函数被包在括号里面?这是因为js只能就地运行函数表达式。

         函数声明不能像上面一样执行。
              
    1 function work() {
    2   // ...
    3 }()  // syntax error

         即使我们把名字去掉,JS还是会发现function这个关键字,把它解析成函数声明。
         
    1 function() { // error, function declaration without name.
    2   // ...
    3 }()

         所以,只有把函数包在括号面才可行。然后解析器会认为它是语句中的一部分,把它提升为函数表达式。

         如果函数明显就是一个表达式,那么也不用包起来。
         
    1 var result = function(a,b) { return a+b }(2,2)
    2 alert(result) // 4

         上面的代码,函数会被创建并且马上执行。

         


    下面代码的结果是啥?为啥?

    1 var a = 5
    2  
    3 (function() {
    4   alert(a)
    5 })()

    P.S. 想清楚哦童鞋,有陷阱哦~Ok, 我已经提醒过你了。 Smile

    Solution

    答案是 error. Try it:

    1 var a = 5
    2  
    3 (function() {
    4   alert(a)
    5 })()
    因为var a = 5 后面没有加分号,JS会这样处理的。   
    1 var a = 5(function() {
    2   alert(a)
    3 })()

    所以,JS尝试把5当做函数来调用,这会导致错误。真正能用的代码如下。

    1 var a = 5;
    2  
    3 (function() {
    4   alert(a)
    5 })()

    这应该是JS没有分号的最危险的陷阱了。

    还有一种方法去定义还是就是直接调用Function构造器:

    1 var sayHi = new Function('name'' alert("Hi, "+name) ');
    2  
    3 sayHi("Benedict");

    它很少用,基本不用。

    命名函数表达式

         一个函数表达式可以有名字:
         
    var f = function sayHi(name) {
      alert("Hi, "+name)
    }

         这个语法被称作命名函数表达式(named function expression or NFE), 但是在IE<9下表现都不正常。
         在支持这个语法的浏览器下,这个函数的名字只可以在函数内部被访问。
    1 var f = function sayHi(name) {
    2   alert(sayHi) // outputs function
    3 }
    4  
    5 alert(sayHi) // error: undefined variable 'sayHi'

         IE在这种情况下却创造了两个函数对象:sayHi 和 f
    1 var f = function g() {  }
    2  
    3 // false in IE, error (g is undefined) in other browsers
    4 alert(f=== g)

         NFE 存在的目的是让匿名函数可以递归。
         看下面的包含在setTimeout里面的factorial函数:

    setTimeout(function factorial(n) {
      return n == 1 ? n : n*factorial(n-1)
    }, 100)

    下面代码的结果是什么?为什么?

    function g() { return 1 } )
     
    alert(g)
    Solution

    答案是 error:

    1 function g() { return 1 } )
    2  
    3 alert(g)

    关键点是要理解(function ... )是一个函数表达式而不是函数定义.

    所以这是一个命名函数表达式。

    命名表达式的名字只能在函数内部访问。

    除了IE<9 ,其他浏览器都支持 NFEs, 所以它们会抛出 ‘undefined variable’ 异常, 因为函数名 g 只能在内部被访问。

    IE<9 不支持 NFE, 所以它们会输出整个函数(源代码).

     函数命名
         函数是行为(action),所以应该起一个动词做名字像get,red,calculateSum这些。
         下面的情况是允许短函数名的:
         1.函数在内嵌函数里面暂时使用的,和变量的逻辑一样。
         2.函数会在整个代码里的多次被调用。一方面,你不会忘了它是干嘛用的,另一反面,你可以少些点代码。
         现实世界中,像'$','$$','$A',这些,会经常在JS函数库里被使用。

         其他情况下,还是应该用动词,或者动词做开头的函数名字

    总结

         函数在JS里面是基本类型。它们可以根据需要被赋值,传递和调用。
         一个不返回任何东西的函数其实返回了undefined
         用动词做函数名字。

         

    Function DeclarationFunction Expression
    function 在主代码流里面 function 作为函数表达式的一部分
    函数在代码执行前创建,可以在定义前被调用。 当代码执行到的是创建,只能在定义后调用。
      可以就地调用

    转载:
    http://www.cnblogs.com/Xdoable/archive/2011/09/08/2171512.html

  • 相关阅读:
    Redis分布式锁服务(转)
    redis分布式锁(转)
    MySQL+InnoDB semi-consitent read原理及实现分析(转)
    MySQL加锁处理分析(转)
    实战经验丨PHP反序列化漏洞总结
    脚本语言丨Batch入门教程第四章:调用与传参
    福利狂欢已开启,请做好准备!
    脚本语言丨Batch入门教程第三章:逻辑判断
    WinRAR存在严重的安全漏洞影响5亿用户
    Batch入门教程丨第二章:认识变量相关概念
  • 原文地址:https://www.cnblogs.com/yuzhongwusan/p/2331693.html
Copyright © 2020-2023  润新知