对于任何语言来说,函数都是一个重要的组成部分。在ES6以前,从JavaScript被创建以来,函数一直没有大的改动,留下了一堆的问题和很微妙的行为,导致在JavaScript中使用函数时很容易出现错误并且可能需要多余的代码实现一些基本的函数行为。在ES6中,函数有一个质的飞跃的改进,它充分考虑到过去数年间JavaScript开发者的需求和抱怨。与ES5相比,在ES6中使用函数开发不容易出错,而且更加灵活。
带默认参数的函数(Functions with Default Parameter Values)
在JavaScript中每个函数都是唯一的,函数不允许重载。不管函数声明时的参数有多少个,你都可以传入任意数量的实参。这允许你定义可以处理不同参数个数的函数,当参数没提供时,系统会使用默认的参数值。
在ES5中模拟默认参数值(Simulating Default Parameter Values in ECMAScript6)
在ES5或之前,通常如下创建一个带默认参数值的函数:
function makeRequest(url, timeout, callback) { timeout = timeout || 2000; callback = callback || function() {}; }
在这个例子中,通常timeout和callback是可选的参数,因为它们都可以使用默认的值。当第一个操作数为false时,或操作就会返回第二个操作数。因为函数如果没有提供形参对应的实参,形参会被设置为undefined,所以通常用或操作为未传入值的参数设置默认值。然而上面的这个函数可能会有问题,当timeout传入0时,timeout的值仍然会被赋值为2000。一个更安全的方法是检查参数的类型:
function makeRequest(url, timeout, callback) { timeout = (typeof timeout !== "undefined") ? timeout : 2000; callback = (typeof callback !== "undefined") ? callback : function () {}; }
尽管上面的方法更安全,但是需要更多的代码去执行一个基本的操作。
ES6中的默认参数值(Default Parameter Values in ECMAScript6)
在ES6中,你可以通过给形参初始化的方式轻松为形参提供默认值。第一个示例函数在ES6可以简单写成:
function makeRequest(url, timeout = 2000, callback = function() {}){ }
这个函数只有第一个函数参数是希望永远传递的,其他两个参数都有默认的值。与在ES5的写法相比,函数更小,因为你不需要任何额外的代码去检查缺失的参数值。当向makeRequest函数传递三个参数时,默认值都没被使用。下面是一些调用示例:
//使用默认的timeout和callback makeRequest("/foo"); //使用默认的callback makeRequest("/foo", 500); //不使用任何默认值 makeRequest("/foo", 500, function(body) { doSomething(body); });
实际上在ES6中,你可以为任何参数提供默认值,提供默认值的参数可以出现在未提供默认值的参数前面:
function makeRequest(url, timeout = 2000, callback) { }
只有当你不传入第二个参数,或者第二个参数显示地传入undefined,第二个参数才会使用默认值:
//使用默认值 makeRequest("/foo", undefined, function(body) { doSomething(body); }); //使用默认值 makeRequest("/foo"); //不使用默认值 makeRequest("/foo", null, function(body) { doSomething(body); });
默认参数值对arguments对象的影响(How Default Parameter Values Affect the arguments Object)
在右默认参数值的情况下,arguments对象的行为是不同的。在ES5中not-strict模式下,arguments对象可以反映函数参数的变化。下面是一个例子:
function mixArgs(first, second) { console.log(first === arguments[0]); console.log(second === arguments[1]); first = "c"; second = "d"; console.log(first === arguments[0]); console.log(second === arguments[1]); } mixArgs("a", "b");
上面代码的输出结果如下:
true true true true
在ES5中non-strict模式下,arguments对象总是会随着形参数值的变化而变化。因此给first和second两个参数的值更新后,后面两个比较仍然是true。在ES5中,如果使用strict模式,那么arguments就不会随着形参的变化而变化了。
在ES6中不管是strict模式还是non-strict模式,arguments对象的行为跟在ES5中使用strict模式一样。
来看下面的代码:
function mixArgs(first, second="b") { console.log(arguments.length); console.log(first === arguments[0]); console.log(second === arguments[1]); first = "c"; second = "d"; console.log(first === arguments[0]); console.log(second == arguments[1]); } mixArgs("a");
上面的代码输出如下:
1 true false false false
因为向mixArgs函数只传了一个参数,所以arugments的长度为1。这也意味着arguments[1]是未定义的。所以前两个比较表达式的结果为true和false。后面改变first和second参数的值,arugments并未变化。因此后面两个比较表达式的结果都为false。在ES6中不管是strict还是non-strict模式中执行上面的代码,结果都是一样的。
默认参数表达式(Default Parameter Expressions)
实际上在ES6中,默认参数的值并不一定需要是一个确定的值,它可以是一个函数:
function getValue() { return 5; } function add(first, second = getValue()) { return first + second; } console.log(add(1, 1)); // 2 console.log(add(1)); // 6
不仅如此,前面的参数还可以作为后面参数的默认值:
function getValue(arg) { return 5 + arg; } function add(first, second = getValue(first)) { return first + second; } console.log(add(1, 1)); // 2 console.log(add(1)); // 7
当然后面的参数不能作为前面参数的默认值,否则程序会报错。