1.函数参数的默认值

基本用法

ES6之前,不能直接为函数的参数指定默认值;ES6则允许,即直接写在参数定义的后面。

  1. function log(x,y='World'){
  2. console.log(x.y);
  3. }
  4. log('hello') //Hello World
  5. log('Hello','China') //Hello China

与解构赋值默认值结合使用

参数默认值可以与解构赋值的默认值,结合起来使用。

  1. function foo({x,y=5}){
  2. console.log(x,y);
  3. }
  4. foo({}); //undefined 5
  5. foo({x:1}) //1 5
  6. foo({x:1,y:2}) //1 2

参数默认值的位置

通常情况下,定义了默认值的函数参数应该是函数的尾参数。这样便于辨认哪些参数有默认值,哪些参数可以省略。

函数的length属性

指定了默认值之后,函数的length属性,将返回没有指定默认值的参数个数。

作用域

一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。等到初始化结束,这个作用域就会消失。这种语法行为,在不设置参数默认值时,是不会出现的。

应用

利用参数默认值,可以指定某一个参数不得省略,如果省略就抛出一个错误。

  1. function throwIfMissing(){
  2. throw new Error('Missing parameter');
  3. }
  4. function foo(mustBeProvided = throwIfMissing()){
  5. return mustBeProvided;
  6. }
  7. foo
  8. //Error:Missing parameter

可以将参数默认值设为undefined,表明这个参数是可以省略的。

2.rest参数

ES6引入rest参数(形式为...变量名),用于获取函数的多余参数。rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

  1. const sortNumbers = (...numbers) => numbers.sort();

3.严格模式

从ES5开始,函数内部可以设定为严格模式。

  1. function doSomething(a,b){
  2. 'use strict';
  3. //code
  4. }

ES2016做了一点修改,规定 只要函数参数使用了默认值、解构赋值或者扩展运算符,那么函数内部就不能显式设定为严格模式,否则会报错。
两种方法可以规避这种限制。第一种是设定全局性的严格模式,是合法的。
第二种是把函数包在一个无参数的立即执行函数里面。

  1. const doSomething = (function () {
  2. 'use strict';
  3. return function(value = 42){
  4. return value;
  5. }
  6. }());

4.name属性

函数的name属性,返回该函数的函数名。
Function构造函数返回的函数实例,name的属性值为anonymous。

  1. (new Function).name //"anonymous"

bind返回的函数,name属性值会加上bound前缀。

  1. function foo(){};
  2. foo.bind({}).name //"bound foo"
  3. (function(){}).bind({}).name //"bound"

5.箭头函数


基本用法

ES6允许使用“箭头”(=>)定义函数。

  1. var f = v => v;
  2. //等同于
  3. var f = function(v){
  4. return v;
  5. };

如果箭头函数不需要参数或者需要多个参数,就使用一个圆括号代表参数部分。

  1. var f = () => 5;
  2. var sum = (num1, num2)=> num1+num2;

如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。

  1. var sum = (num1,num2)=>{return num1+num2;}

注意

  1. 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
  2. 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
  3. 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用rest参数代替。
  4. 不可以使用yield命令,因此箭头函数不能用作Generator函数。

不适用场合

由于箭头函数似的this从动态变成静态,下面两个场合不应该使用箭头函数。

  1. 定义对象的方法,且该方法内部包括this。
  2. 需要动态this的时候。

嵌套的箭头函数

箭头函数内部还可以再使用箭头函数。

6.尾调用优化


定义

尾调用(Tail Call)指某个函数的最后一步是调用另一个函数。
以下三种情况,都不属于尾调用。

  1. //情况一
  2. function f(x){
  3. let y=g(x);
  4. return y;
  5. }
  6. //2
  7. function f(x){
  8. return g(x) +1;
  9. }
  10. //3
  11. function f(x){
  12. g(x);
  13. }

尾调用优化

尾调用之所以与其他调用不同,就在于它的特殊的调用位置。
我们知道,函数调用会在内存形成一个“调用记录”,又称“调用帧”(call frame),保存调用位置和内部变量等信息。如果在函数A的内部调用函数B,那么在A的调用帧上方,还会形成一个B的调用帧。等到B运行结束,将结果返回到AB的调用帧才会消失。如果函数B内部还调用函数C,那就还有一个C的调用帧,以此类推。所有的调用帧,就形成一个“调用栈”(call stack)。
尾调用由于是函数的最后一步操作,所以不需要保留外层函数的调用帧,因为调用位置、内部变量等信息都不会再用
到了,只要直接用内层函数的调用帧,取代外层函数的调用帧就可以了。

尾递归

函数尾调用自身称为尾递归。
递归会保存大量调用帧,很容易发生“栈溢出”错误。但对于尾递归来说,由于只存在一个调用帧,所以永远不会发生栈溢出错误。

严格模式

ES6的尾调用优化只在严格模式下开启。

7.函数参数的尾逗号

ES2017允许函数的最后一个参数有尾逗号(trailing comma)。

8.Function.prototype.toString()

toString()方法返回函数代码本身,以前会省略注释和空格。现在明确要求返回一模一样的原始代码。

9.catch命令的参数省略

JavaScript语言的try…catch结构,以前明确要去catch后面必须跟参数,接受try代码块抛出的错误对象。
ES2019做出了改变,允许catch语句省略参数。