ES6 函数的扩展

Posted gaogao999

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ES6 函数的扩展相关的知识,希望对你有一定的参考价值。

第三章 函数

函数形参的默认值

ES5模拟默认参数

function makeRequest (url, timeout, callback) 
  timeout = timeout || 2000;
  callback = callback || function () ;

为形参赋予默认值,说明了该参数为可选参数。

ES6的默认参数值

function makeRequest (url, timeout = 2000, callback = function () )
  // 函数执行

需要注意的是,在参数中,null也是合法的参数值,所以传入null的时候不会用默认值替代。

ES6真正的可选参数是参数后面带?声明。

默认参数值对arguments对象的影响

在ES5非严格模式中,函数命名参数的变化会体现在arguments对象中。

在ES5严格模式中,无论参数如何变化,arguments对象不再随之改变。

ES6默认处于严格模式,默认参数值的存在使得arguments对象保持与命名参数的分离。

默认参数表达式

默认参数值可以使用任意数据类型,甚至是函数执行返回的结果。

ES6的参数声明具有临时性死区的特性,不能在声明之前调用。

function add (first = second, second = 1) 
  return first + second;

// 这是一个运行时才会抛出的错误,只有当first传入了非法值,引发默认值赋值的时候,才会抛出error。
add(1, 2); //3,没有使用默认值
add();  // ReferenceError,使用默认值时候发现在second声明前使用了second

处理无命名参数

ES5中的无命名参数

ES5调用arguments对象来获取所有的实参,从而间接地对无命名参数操作。

不定参数

ES6引入在命名参数前的三个点...来表明不定参数(rest parameters)。所有自它之后传入的所有参数,都被纳入以这个参数命名的数组中。ES6中arguments依然存在,但是应当尽可能地使用不定参数来替代它的作用。

使用限制:

  • 不定参数一定要放在参数表的末尾。
  • 不能用于对象字面量setter中(因为它的参数有且只能有1个)。

增强的Function构造函数

ES6增强了Function构造函数的功能,支持在创建函数时定义默认参数和不定参数。

var add = new Function('first', 'second = 1', 'return first + second;');
add(1, 1);  // 2
add(1);       // 2 

name属性

ES6为所有函数添加了name属性,其特性为:

  • 函数自身的命名,权重最高,不会被覆盖。
  • 函数表达式所赋值的变量名,权重其次,会覆盖匿名函数表达式的name。
  • getter/setter会有get/set前缀,bind返回的函数表达式会有bound前缀。
  • Function构造函数创建的函数,名称为anonymous。

不能使用name来获取对于函数的引用。

展开运算符

和不定参数使用相对的是展开运算符,同样也是...声明。

它使用在传递实参的时候,将一个Array展开为参数表的形式。

let values = [25, 50, 75, 100];
// 两者等价
Math.max.apply(Math, values);
Math.max(...values);

明确函数的多重用途

JS函数有两个不同的内部方法: [[Call]]和[[Construct]]。

通过new关键字调用函数时,执行的是[[Construct]]函数,它负责创建一个实例对象,然后再执行函数体,将this绑定到实例上。

不通过new调用函数,则执行[[Call]]函数,直接执行代码中的函数体。

ES5判断函数被调用方法

使用this instanceof class的形式。

function Person (name) 
  // new关键字将this绑定到实例
  if(this instanceof Person) 
    this.name = name;
  
  // [[Call]]调用不会绑定this到实例
  else 
    throw new Error('必须使用new关键字调用Person');
  

元属性(Metaproperty)new.target

new.target表示new操作符的目标,即该构造函数。如果[[Call]]方式调用,则其为undefined。

块级函数

ES6引入块级作用域后,在块级作用域中声明的函数只能在块内使用,当跳出块后,该函数将无法访问。

严格模式下,function声明的块级函数会被提升到块级作用域的顶部。

非严格模式下,function声明的跨级函数会被提升到外层的函数作用域。

箭头函数(Lambda)

与传统函数的不同:

  • 没有this、super、arguments和new.target绑定,这些值由外层最近一层非箭头函数决定。
  • 不能通过new关键字调用,没有[[Construct]]方法。
  • 没有原型。
  • 不可以改变this绑定(因为始终从外层获取)。
  • 不支持重复的命名参数。

lambda表达式也有name属性,类似于函数。

没有this绑定

lambda表达式的this必须通过查找作用域链来决定其值。

箭头函数的返回值

没有用包裹时,lambda表达式只能有一条执行语句,并返回该语句的返回值。

var result = [].sort((a, b) => a - b);  // 等同于 return a - b

尾递归优化(TCO)

function TCO (fn) 
  let value,
      active = false,
      accumulated = [];
  
  // 在f内部进行递归调用的时候,其实调用的是这个返回的accumulator
  // 这里需要注意的是,要将f中的递归调用函数名使用accumulator被赋予的变量名
  return function accumulator(...args)
    accumulated.push(args);

    if(!active) 
      active = true;
      while(accumulated.length) 
        /**
        * 由于每次需要递归的fn.apply都会导致accumulated被push进一个新的arguments,
        * 所以这个while一直要到全部执行结束才会跳出,
        * 但这种结构只会保持最多两层的调用栈
        */
        value = fn.apply(this, accumulated.shift());
      
      active = false;
      return value;
    
  


var sum = TCO(function(x, y) 
  if(y > 0) 
    // 再次调用的其实是闭包返回的accumulator,而不是这个传入的function
    return sum(x + 1, y - 1);
   else 
    return x;
  
);

以上是关于ES6 函数的扩展的主要内容,如果未能解决你的问题,请参考以下文章

003-正则的扩展数值的扩展函数的扩展数组的扩展对象的扩展

ES6扩展——函数扩展之剩余函数

ES6-04:函数的扩展

ES6 之函数扩展和对象扩展

ES6新特性:Function函数扩展, 扩展到看不懂

ES6--函数扩展