ES6之函数

ES6之函数

本文知识点主要有:

  • 函数默认参数
  • 展开运算符
  • 函数的其他优化
  • 箭头函数
  • 尾调用的内存优化

函数参数默认值

ES6之前,函数对与参数的默认值设置通常采用以下方式:

  function makeRequest (url, timeout, callback) {
    timeout = timeout || 2000;
	  callback = callback || function () {};
	  // ...
  }

但此方法有个小缺陷,就是timeout在传入 0 / false 时,都会默认采用2000。对此进行优化如下:

  function makeRequest (url, timeout, callback) {
  	  timeout = (typeof timeout !== "undefined") ? timeout : 2000;
	  callback = (typeof callback !== "undefined") ? callback : functipn () {};
    // ...
  }

上段代码中 undefined也可以换成void 0
看起来仍旧有点繁琐。为此,ES6提供了默认参数值的用法,更改如下:

  function makeRequest (url, timeout = 2000, callback = function () {} {
    // ... 
  }

看起来简洁了很多。代码中,url是必填值,timeout 和 callback是可选参数。可以选择不传、或者传undefined让其使用默认值。
此外,参数默认值也可以写成表达式。

  function add (first, second = getValue(first)) {
		return first + second;
  }
	function getValue(num) {
		return num + 1
	}
	add(1, 1)  // 2
	add(1)	   // 3

second的默认参数值,使用了first,且调用了getValue函数。值得注意的是,默认参数也存在临时死区,即未声明变量之前,无法进行访问。

	function add (first, second = first) {}

  等价于
	
	function add () {
		let first = arguments[0];
		let second = first;
		{
			// 函数体
		}
	}

参数的默认值是不可以访问函数体内声明的变量。因此,需要用代码块的形式将其隔离。

对于 arguments 的影响

在ES5 非严格模式中。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严格模式时,arguments便不会随着形参被重新赋值进行改变。ES6 与ES5严格模式保持一致。

展开运算符

展开运算符由三个点组成 ( … ),主要有两种用途:

  • 函数参数的收集
  • 数据展开

函数参数的收集

arguments 相同,可以收集传入函数中的多个无命名参数。不同点有以下几点:

  • arguments为一个类数组集合,而 … 为数组
  • arguments在形参声明时,可以省略。而 … 必须声明,且只能声明一个放在形参的最后。
  • arguments收集所有的参数。而 … 只收集其他形参声明后,剩余的参数。

最后, …不能在setter 函数中使用,set函数只能有一个参数。

  function add (...args) {
		let sum = 0;
		for (let i = 0; i < args.length; i++) {
			sum += args[i]
		}
  }
	add(1, 2, 3, 4, 5, 6); // 21

数据展开

展开运算符可以简化给函数传参的过程, 大多数使用apply() 方法的情况,使用展开运算符会更方便。

	let values = [21, 50, 75, 100];

	console.log(Math.max(...values));
	console.log(Math.max.apply(Math, values));

	// 100 100

函数的其他优化

  • 增强的Function构造函数 。我们可以在Function中使用默认参数和展开运算符。
	var add = new Function("first", "second = first", "...args", "return first + second");
	console.log(add(1, 2);
  • name属性:用于便于辨识函数。
	function func1 () { //... };

	var func2 = function () { // ... };

	var func3 = function func4 () { // ... };

	var person = {
		get func5 () { // ... }
	};
	var func6 = Object.getOwnPropertyDescriptor(person, "func5"); 

	console.log(func1.name);            // func1
	console.log(func2.name);            // func2
	console.log(func3.name);            // func4 (函数声明权重较高)
	console.log(func6.get.name);     // get func6

	console.log(func1.bind().name);     // bound func1

	console.log((new Function()).name); // anonymos

最后的四个console的结果需要注意。由于函数提升原则,函数的name值被提前绑定。而set(get)函数,保留其前缀。同样的,使用bind会产生bound前缀。使用Function构造函数的匿名函数,其name值为anonymos

  • new.target
    当使用new关键字调用函数时,会默认执行函数的[[Construct]]函数,创建一个实例的新对象,然后执行函数体。最后将this绑定到新对象上完成创建过程。
    不通过new关键字调用函数时,会执行函数的[[Call]]函数,直接执行函数体。
    ES6在函数中新增的new.target就为了判断函数是否通过new进行调用。
	function Person(name) {
		if (typeof new.target !== 'undefined') {
			this.name = name;
		} else {
			throw new Error("必须通过new关键字来调用Person")
		}
	}
  var p1 = new Person("leo");     
	var p2 = Person(p1); // 报错 
	var p3 = Person.call(p1, "leo"); // 报错

箭头函数

与传统的函数相比,有以下几个方面的不同:

  • arguments, new.target的绑定.
  • 不能使用 new关键字进行调用。(因为没有 [[Construct]]构造方法)
  • 没有原型,不存在prototype属性,
  • 不能改变this的指向。
  • 参数不能重名
	var sum = function (num1, num2)  => num1 + num2;

箭头函数的写法有以下简写规则:

  • 没有参数时,使用一对空的小括号 () => {}
  • 有一个参数时,直接使用 : 参数名 =>
  • 有两以上参数,用括号包裹: (参数1, 参数2) => {}
  • 只有一条执行语句时,函数体的花括号和return可以省略: () => 1

箭头函数更加纯粹,简洁且易用的表示方式应普遍使用。

  • 尾调函数的内存优化
    在递归,闭包函数通常会使用更多的内存来保存作用域,产生栈堆的大量占用。ES6对此进行优化,让重复的函数只使用一个栈堆。为了使用此内存的优化,需要符合以下的规则:
    • 尾调函数不访问当前栈内的变量(非闭包函数)
    • 尾调函数为最后一条语句
    • 尾调用的结果作为函数值返回

举例熟悉的斐波拉切函数

	"use strict";
	function factorila(n) {
		if (n <= 1) {
			return 1;
		} else {
			return n * factorila(n - 1);	
		}
	}

可优化为:

	"use strict";
	function factorila(n, p - 1) {
		if (n <= 1) {
			return 1 * p;
		} else {
			let result = n * p;
			return factorila(n - 1, result);	
		}
	}
posted @ 2019-11-18 18:21  miku561  阅读(207)  评论(0编辑  收藏  举报