本文详细解析JavaScript中定义函数的三种主要方式:function语句、Function()构造函数以及函数直接量。重点对比了Function()构造函数在运行时动态编译的特性及其性能开销,以及函数直接量作为表达式在灵活性和词法作用域上的优势。通过代码示例说明匿名函数、递归函数及闭包场景下的最佳实践,帮助开发者根据性能需求和作用域要求选择合适的定义方式。
JavaScript函数定义的三种方式
JavaScript函数的定义
function语句定义
第一种方式是通过关键字function来声明函数。例如,定义一个计算两点间距离的函数: function distance(x1,x2,y1,y2) { var dx=x2-x1; var dy=y2-y1; return Math.sqrt(dx*dx+dy*dy); }
Function()构造函数
第二种方式是使用Function()构造函数。代码如下: var f=new Function("x","y","return x*y"); 这行代码创建了一个新函数,其功能与你使用标准语法定义的函数基本等价:
function f(x,y) { return x*y; }
Function()构造函数可以接收任意数量的字符串参数。最后一个参数代表函数的主体,其中可以包含任何JavaScript语句,语句之间以分号分隔。其余参数则用于指定函数的形式参数名。如果定义的函数不需要参数,只需向构造函数传递一个表示函数主体的字符串即可。 需要注意的是,传递给Function()构造函数的参数中不包含函数名。因此,通过Function()构造函数创建的函数通常被称为“匿名函数”。 读者可能会好奇Function()构造函数的实际用途。既然可以使用function语句,为何还要使用它?原因在于Function()构造函数允许在运行时动态地创建和编译函数,而不局限于function语句在预编译阶段确定的函数体。然而,这种灵活性带来了性能上的负面影响:每次调用Function()构造函数时,它都会对函数体进行编译。因此,在循环体内或频繁调用的函数中,应避免频繁使用此构造函数。 使用Function()构造函数的另一个理由是,它可以将函数定义为JavaScript表达式的一部分,而非独立的语句。这使得代码结构更加简洁,甚至在某些场景下显得更为精致。
函数直接量
第三种方式是使用函数直接量(Function Literal)。函数直接量是一个表达式,它可以定义匿名函数。其语法与function语句非常相似,但它是作为表达式使用,而非语句,且无需指定函数名。以下三行代码分别展示了使用function语句、Function()构造函数和函数直接量定义三个功能基本相同的函数:
function f(x){return x*x}; var f=new Function("x","return x*x;"); var f=function(x){reurn x*x};
尽管函数直接量创建的是未命名函数,但其语法允许指定一个名称,这在编写调用自身的递归函数时非常有用。例如: var f=function fact(x){if(x<=1)return 1;else return x*fact(x-1);}; 上述代码定义了一个未命名函数,并将其引用存储在变量f中。它并未真正创建一个名为fact()的全局函数,而是允许函数体内部通过该名称引用自身。但需注意,JavaScript 1.5之前的版本中,这种命名函数直接量的实现并不正确。 函数直接量的用法与Function()构造函数类似。由于两者均由JavaScript表达式创建而非语句,因此使用方式更加灵活,特别适用于那些只需使用一次且无需命名的函数。例如,可以将使用函数直接量定义的函数存储在变量中、传递给其他函数,或者直接调用:
a[0]=function(x){return x*x;};//定义一个函数并保存它 a.sort(function(a,b){return a-b;});//定义一个函数;把它传递给另一个函数 var tensquared=(function(x){return x*x;})(10);
与Function()构造函数一样,函数直接量创建的是未命名函数,且不会自动将函数存储在属性中。然而,相较于Function()构造函数,函数直接量具有一个显著优点:Function()构造函数的主体必须以字符串形式提供,这种方式在表达长而复杂的函数时显得非常笨拙。而函数直接量的主体使用的是标准的JavaScript语法。此外,函数直接量仅在解析时被处理一次,而作为字符串传递给Function()构造函数的JavaScript代码则在每次调用构造函数时都需要被解析和编译一次。
三种定义方式的核心区别
在JavaScript 1.1中,可以使用构造函数Function()来定义函数;而在JavaScript 1.2及后续版本中,还可以使用函数直接量来构造函数。开发者应注意这两种方法之间的重要差异。
首先,Function()构造函数允许在运行时动态地创建和编译JavaScript代码。相比之下,函数直接量是函数结构的一个静态部分,与function语句类似。 其次,作为上述差异的必然结果,每次调用Function()构造函数时,都会解析函数体并创建一个新的函数对象。如果构造函数的调用出现在循环中,或者出现在一个经常被调用的函数中,这种方法的效率非常低。另一方面,函数直接量或出现在循环和函数中的嵌套函数,不是在每次调用时都被重新编译,而且每当遇到一个函数直接量时,也不会创建新的函数对象。 Function()构造函数和函数直接量之间的第三点区别在于,使用Function()构造函数创建的函数不使用词法作用域。相反,它们总是被当作顶级函数来编译,如下面代码所示:
var y="global"; function constructFunction() { var y="local"; return new Function("return y");//不捕捉局部作用域。 } //这行代码将显示"global",因为Function()构造函数返回的函数并不使用局部作用域。 //假如使用一个函数直接量,这行代码则可能显示"local"。 alert(constructFunction());