JavaScript中创建函数的多种方式

  • Post author:
  • Post category:java

在JavaScript中,我们可以通过多种方式来定义一个函数。下面,我们就对这些定义函数的方式分别进行说明。

Function构造器

利用Function构造器可以创建一个新的Function对象。但是通过构造器动态创建函数,存在安全性和性能方面的问题。这种方式,我们很少,或基本上不会用到。

var addition = new Function('a', 'b', 'return a + b;');
console.log('函数名:' + addition.name); // anonymous
console.log(addition(1, 2)); // 3

此外,我们也可以将Function构造器当作函数调用(不使用new)来创建函数,这和使用new关键字的效果是一模一样的:

var addition = Function('a', 'b', 'return a + b;');
console.log('函数名:' + addition.name); // anonymous
console.log(addition(1, 2)); // 3

每个JavaScript函数实际上都是一个Function对象。我们可以通过以下代码进行验证:

(function(){}).constructor === Function // true

(()=>'').constructor === Function // true

函数声明

函数声明是我们常用的一种定义函数的方式。

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

console.log('函数名:' + addition.name); // 函数名:addition
console.log(addition(1, 2)); // 3

注意,函数声明会被提升到封闭函数或全局范围的顶部。即我们可以在函数声明之前使用该函数:

console.log('函数名:' + addition.name); // 函数名:addition
console.log(addition(1, 2)); // 3

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

函数表达式

函数表达式使得我们可以在表达式中定义函数。这也是我们极其常用的一种定义函数表达式的方式。

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

console.log('函数名:' + addition.name); // 函数名:addition
console.log(addition(1, 2)); // 3

这种定义函数的方式也叫函数直接量

像上面我们是将一个匿名函数赋值给一个变量。此外,我们还可以使用一个命名函数赋值给一个变量:

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

console.log('函数名:' + addition.name); // 函数名:add
console.log(addition(1, 2)); // 3
console.log('函数名:' + add(1, 2)); // Uncaught ReferenceError: add is not defined

注意,这里的函数名add只能在函数体中使用,常用作递归调用:

var i = 0;
var recursion = function call() {
    console.log(++i);
    if (i < 3) {
        call();
    }
};

recursion(); // 1 2 3

当然,我们也可以使用变量名addition作为递归调用:

var i = 0;
var recursion = function call() {
    console.log(++i);
    if (i < 3) {
        recursion();
    }
};

recursion(); // 1 2 3

函数表达式与函数声明很类似,它们的主要区别是:

箭头函数

箭头函数是ES2015开始引入的一种新的定义函数的方式。我们可以将箭头函数看作是一种简写紧凑型的“函数表达式”,它和函数表达式一样都不会被提升。但与传统的函数表达式相比,箭头函数有以下几个不同点:

  • 箭头函数不绑定thisarguments
  • 箭头函数不能用作构造函数,即不能使用new来创建对象
  • 箭头函数没有prototype
var addition = (a, b) => {
    return a + b;
};

console.log('函数名:' + addition.name); // 函数名:addition
console.log(addition(1, 2)); // 3

当函数体中只有return语句时,我们还可以使用“简洁体”代替常规的“块体”:

var addition = (a, b) => a + b;

console.log('函数名:' + addition.name); // 函数名:addition
console.log(addition(1, 2)); // 3

但是注意,如果函数体中返回的是对象字面量时,需要把对象字面量包裹在小括号中。否则,大括号中的代码会被解析为代码(对象的属性名会被解析为标签)。

比如下面的函数体会被解析为代码运行,故没有返回值:

var func = () => { foo: 1 };
console.log(func()); // undefined

又比如下面的函数体因被解析为代码,故其中的函数声明必须要指定函数名称:

var func = () => { foo: function() {} };
// Uncaught SyntaxError: Function statements require a function name

对于上面的两个例子,正确的写法是用小括号包裹函数体。

例子一:

var func = () => ({ foo: 1 });
console.log(func()); // {foo: 1}

例子二:

var func = () => ({ foo: function() {} });
console.log(func()); // {foo: ƒ}

ES2015还引入了class关键字,虽然它的实现只是一个语法糖,但配合箭头函数使用,就可以让我们清楚地知道是在定义一个函数还是在定义一个类,这可以避免我们理解上的混乱。


版权声明:本文为wuxianjiezh原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。