zoukankan      html  css  js  c++  java
  • 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);	
    		}
    	}
    
  • 相关阅读:
    移动函数的封装示例
    如何从不均衡类中进行机器学习
    DPM(Deformable Parts Model)--原理(一)
    K-means聚类算法
    机器学习中对核函数的理解
    总结:Bias(偏差),Error(误差),Variance(方差)及CV(交叉验证)
    技术干货
    神经网络入门
    目标函数、损失函数、代价函数
    地铁客流检测训练问题记录
  • 原文地址:https://www.cnblogs.com/miku561/p/11883939.html
Copyright © 2011-2022 走看看