定义JS函数与函数声明、Function构造函数、函数表达式的比较

 2016年06月21日    114     声明


定义一个JavaScript函数有多种方式,其中function函数声明、Function构造函数创建、函数表达式是3种比较常用的方式,ECMAScript 6标准中又规定了几种新的函数定义方式。在JavaScript中每个函数都是一个Function对象,它不仅能像对象一样拥有属性和方法,而且可以被调用。

  1. 函数的定义
  2. 函数声明、Function构造函数、函数表达式的比较

1. 函数的定义

1.1 三种函数定义方式

定义一个函数有很多种方法:

函数声明 (函数语句)

使用函数声明(函数语名句)定义一个函数语法如下:

function name([param,[, param,[..., param]]]) {
  [statements]
}
  • name - 函数名
  • param - 传递给函数的参数,最多有255个
  • statements - 函数体

函数表达式

函数表达式函数声明非常类似,不同的是函数表达式可以省略函数名,从而定义一个匿名函数。

函数表达式语法结构如下:

function [name]([param1[, param2[, ..., paramN]]]) {
  statements
}
  • name - 函数名。可以省略,省略时即为匿名函数。
  • param - 传递给函数的参数,最多有255个
  • statements - 函数体

Function构造函数

也可以使用new操作符通过Function构造函数创建:

new Function ([arg1[, arg2[, ...argN]],] functionBody)
  • arg1, arg2, ... argN - 函数使用的参数
  • functionBody - 一个表示函数定义的JavaScript语句的字符串。

注意:Function构造函数使用字符串做为函数体,这会阻止JS引擎的优化并带来一些其它问题。因此,并不建议使用这种函数定义方式。


1.2 ES 6中新增的函数定义方式

在ES 6(ECMAScript 2015)中新增了三Generator 函数(生成器函数),这种函数同样有三种定义方式:

生成器函数声明 - function* 语句

类似与函数声名,只是需要在function关键字后面增加一个*号:

function* name([param[, param[, ...param]]]) { 
  statements 
}
  • name - 函数名
  • param - 传递给函数的参数,最多有255个
  • statements - 函数体

生成器函数表达式 - function* 表达式

同样的,生成器函数也可以使用表达式定义的方式:

function* [name]([param] [, param] [..., param]) { 
  statements 
}
  • name - 函数名。可以省略,省略时即为匿名函数。
  • param - 传递给函数的参数,最多有255个
  • statements - 函数体

生成器函数构造函数 - GeneratorFunction


new GeneratorFunction (arg1, arg2, ... argN, functionBody)
  • arg1, arg2, ... argN - 函数使用的参数
  • functionBody - 一个表示函数定义的JavaScript语句的字符串。

除了生成器函数外,ES6中还新增了一种更简短的函数定义方式-箭头函数

箭头函数-=>

([param] [, param]) => { statements } param => expression
  • param - 函数参数
  • statements 或 expression - 声明多个语句时需要用大括号括起来,而单个表达式则不用。


2. 函数声明、Function构造函数、函数表达式的比较

函数声明、Function构造函数、函数表达式几种函数定义示例如下:

使用构造函数定义函数,并赋值给multiply:

var multiply = new Function("a", "b", "return a * b");

使用函数表达式定义multiply函数:

function multiply(x, y) {
  return x * y;
}

使用函数表达式定义一个函数,并赋值给multiply:

var multiply = function(x, y) {
  return x * y;
 };

使用函数表达时个,也可以指定函数名。如,将名为func_named的函数赋值给multiply:

var multiply = function func_name(x, y) {
  return x * y;
};

一些差别

构造函数使用字符串做为函数体,这会阻止JS引擎的语法检查及优化等。因此,不推荐使用这种定义方式。而函数声明与函数表达式非常类似,但还是存在一些差别。这三种方式主要有以下几个方面的差别:

函数变量可以再赋值

使用函数声明定义的函数名不能被改变,而使用函数表达式定义的函数变量可以再被赋值。当函数表达式使用函数名时,这个名称只能在函数体内使用,在其它位置使用将会报错:

var y = function x() {};
alert(x); // throws an error

'new Function'定义时没有函数名,所以也不能在其内部访问。

函数声名会带来作用域的提升

函数声名会带来作用域的提升,所在可以在函数声名前使用函数声名中的函数名:

hoisted(); // "http://itbilu.com"

function hoisted() {
  console.log("http://itbilu.com");
}

函数表达式会创建一个闭包,它定义的函数继承了当前的作用域,并不会有作用域的提升:

notHoisted(); // TypeError: notHoisted is not a function

var notHoisted = function() {
  console.log("http://itbilu.com");
};

Function构造函数不会继承全局作用哉以外的任何作用域。

Function构造函数定义的函数会被解析多次

通过函数表达式定义的函数和通过函数声明定义的函数只会被解析一次。Function构造函数定义的函数确不同,构造函数被调用一次,其中的函数体字符串都要被解析一次。

虽然函数表达式每次都创建了一个闭包,但函数体不会被重复解析,因此仍然比构造函数定义的函数要快。