ES6函数增强

  函数参数可以拥有默认值。调用函数时,如果没有进行相应的实参传递,参数就会使用默认值。怎么给参数提供默认值呢?很简单,声明函数时候,给形参赋一个值就可以了,这个值就是参数的默认值。

// num2拥有默认参数值5,如果没有给num2形参传值,它的取值将会是5 
function sum (num1, num2 = 5) {
    return num1 + num2;
}

console.log(sum(1)) // 6 调用sum函数时, 只传递了一个参数1,所以函数中num1 =1, num2就会使用默认参数值5, 1+5 =6;
console.log(sum(1,2)) // 3 函数调用时,我们传递了两个参数,所以默认参数值不起作用, 函数使用我们传递过去的参数 1+2 =3

  当参数拥有默认值以后,它影响了argumets 对象。我们都知道,每一个函数内部都有一个arguments 对象,保存函数调用时传递过去的参数,第一个参数对应的就是arguments[0], 第二个参数对应的就是arguments[1]. 像上面的sum 函数, num1 == argument[0]; 但有了默认参数值,这种对于关系打破了. sum(1) 调用sum 函数的时候,我们只传递了一个值1,也就意味着arguments[1] 的值是undefined, 但是它对应的num2 形参,num2 参数由于默认值的存在,这里取5.  arguments[1]  就不等于num2 了。还有一点就是,arguments 只是保存了传递过去的值,如果在函数内部 参数的值有更改,那么arguments 也不会实时反应这种变化,还是上面的sum(1) 调用,arguments[0] 永远等于1。 初始的时候,num1 == arguments[0]; 但如果在函数体中 num1 重新赋值为2, arguments[0] 就不等于num1 了。  

function sum (num1, num2 = 5) {
    console.log(arguments.length); // 1, 只传递了一个参数
    console.log(num1 === arguments[0]); // true  初始时相等
    console.log(num2 === arguments[1]); // false 只传一个参数,arguments[1] 是undefined, num2 取默认值5
    num1 = 2;
    console.log(num1 === arguments[0]); // false arguments只保存调用时的初值。
    return num1 + num2;
}

console.log(sum(1));

  记住一点就可以了, arguments 对象只保存调用函数时传递过去的参数的初始值。不太理解也没有关系,arguments 对象几乎用不到了,因为ES6 提供了更好的参数保存方式(剩余参数rest),下面会介绍。继续默认参数的学习。默认参数值,不仅可以像上面一样使用原始值,还可以使用js表达式,比如调用一个函数。

function getValue() {
  return 5;
}
//参数second的默认值,是函数表达式
function add(first, second = getValue()) {
    return first + second;
}
console.log(add(1, 1)); // 2
console.log(add(1)); // 6

  默认参数为函数,这个函数的调用是惰性的,如add(1,1)传递了两个参数,函数就用不到的,不会调用。只有传递一个参数的时候,如add(1), 这个默认函数才会调用。其实getValue函数还可以接受参数, 最主要的是调用getValue函数的时候可以把first 传递给它。 

  function getValue(value) {
      return value + 5;
    }
    // 函数参数是第一个参数的值。
    function add(first, second = getValue(first)) {
        return first + second;
    }
    console.log(add(1, 1)); // 2
    console.log(add(1)); // 7

  当参数像上面这样取默认值的时候,一定要注意参数的顺序,如果second 和first  顺序调换了,就出错了。因为参数也形成了自己的作用域。add 函数的参数的声明就像下面一样

let first;
let second = getValue(first);·

  如果写反了,就变成了

let second = getValue(first);
let first;

  first 变量还没有声明,就使用了,造成了暂存死区。

  剩余参数 (rest)

  当我们调用函数的时候,我们可以传递任意数量的实参给函数,如果函数形参的数量少于实参的数量,我们就只能通过函数内部的arguments 获取多余的实参。ES6 提供了一个更简单的方法来获取这些多余的参数,就是剩余参数。我们在声明函数的时候,在一个参数的前面加上..., 这个参数就变成了一个数组,它会把多余的参数收集到它里面,变成它的元素。

let sum = (obj, ...rest) => {
    console.log(rest)  // [2,3,4,5]
} 

sum({a:1},2,3,4,5)

  上面代码中的rest就是一个剩余参数,它把2,3,4,5 收集起来,变成了它的元素,它本身是一个数组。

  注意:一个函数中只能有一个剩余参数,且它必须放到所有参数最后,这很好理解,因为,它把所有参数都收集到一起了,一个就足够了,如果它后面还有参数,这些参数也获取不到数据了,所以也就没有必要设置参数了。

  扩展操作符(...)

  扩展操作符,把一个可迭代对象(如数组)扩展给一个一个的单体。

let array = [1,2,3,4,5]; 
console.log(...array)  // 1 2 3 4 5

  js 中的函数有两个内部的方法, [[Call]]  和[[Construct]] , 当我们调用函数的时候,没有使用new, 那[[Call]] 方法就会被调用,执行函数体。当调用函数的时候前面加了一个new, 那[[Construct]]  方法就会被调用,生成一个对象,调用函数,给对象赋值,并返回对象。当然并不是每一个函数都有[[Construct]] 方法,比如箭头函数就没有,所以箭头函数就不能使用new 进行调用。那怎么决定函数是用那种方法呢?

  ES6 增加了一个new.target,  如果一个函数通过new 调用,它内部会获取一个new.target 的元属性,它指向的就是我们的构造函数。 当然,如果这个函错误地通过一般函数调用,new.target 就是undefined. 这样我们就可以轻松地判断一个函数是不是通过new进行调用,从而避免了构造函数用普通方式进行调用产生的错误。

function Person(name) {
    if (typeof new.target !== "undefined") {
        this.name = name;
    } else {
        console.error("You must use new with Person.")
    }
}
var person = new Person("Nicholas");
var notAPerson = Person.call(person, "Michael"); // You must use new with Person.

   箭头函数

  语法: 参数列表 => 函数体;一个简单的箭头函数如下

(a,b) => a+b; 

  可以看到它只是一个匿名函数表达式,只有把它赋值给一个变量引用,才能对它进行调用,

let sum = (a,b) => a+b; 
sum(1, 2)

  箭头函数的参数列表,可以有一个参数,可以有多个参数,也可以没有参数。多个参数呢, 就像上面一样,把所有参数一一列出来,并用括号括起来,没有参数简单,直接一个() 就可以了,有一个参数呢,也可以使用()把这个参数包括起来,但通常会省略括号

let hello = () => console.log('hello'); // 箭头函数没有参数,直接用一个括号表示
let add10 = num => num +10; // 箭头函数只有一个参数num,通常直接写这个参数,不用括号括起来。

  箭头函数右侧的函数体,可以只有一句表达式,如上面的a +b, 这时,除了可以直接写这一句表达式外,它默认是会返回表达式的值,这也就是我们没有写return a +b 的原因,这里要注意一点,如果返回一个对象,这个对象要用() 括起来。

let obj = name =>({name:name}) // 如果不写外面的括号,{} 就会被当做块级作用域

  那如果函数体是一段可以执行的语名块呢?那就需要用{}把语名块包起来,如果语句块执行完毕,还要返回值,那就要在语句块的末尾显示调用return

let amount = n => {
    let sum =0;
    for(let i=0; i<=n; i++){
        sum = sum + i;
    }
    return sum;
}

  可以看到箭头函数大大简化了写法,尤其是回调函数的函数体只有一句表达式的时候,比如,sort 函数

var arr =[3, 7, 9, 5];
arr.sort(function(value1, value2){
    return value1 – value2 
})

// 箭头函数
arr.sort((value1, value2) => value1 –value2);

   箭头函数如果用到高阶函数上,也简化了高阶函数的书写。所谓的高阶函数,他或者接受一个或多个函数作为参数,它或者返回一个函数作为运行结果,只要两者满足其一,就是高阶函数。在高级程序设计书中,根据对象的属性对对象进行排序,就用到了高阶函数。

function compare(property) {
    return function(obj1, obj2) {
        var value1 = obj1[property];
        var value2 = obj2[property];

        if(value1 < value2) {
            return -1;
        }else if(value1 > value2) {
            return 1;
        }else {
            return 0
        }
    }
}

  用箭头函数进行简化, 箭头首先是一个函数表达式,也就是我们要把箭头函数赋值给一个变量,否则无法调用。let compare =  箭头函数; 箭头函数表示方法是()=》表达式,接受多个参数,返回一个值。let compare =(property) => 返回值,但这里返回值又是一个函数,  函数有又可以用箭头函数表示 (obj1, obj2) => {}, 

let compare = property => (obj1, obj2) = > {
     var value1 = obj1[property];
        var value2 = obj2[property];

        if(value1 < value2) {
            return -1;
        }else if(value1 > value2) {
            return 1;
        }else {
            return 0
        }
}

但当我看到这个箭头函数,怎样转化成一般的函数,我们怎么读懂连缀的箭头函数?

  箭头函数永远是 ()=》表达式, 所以右侧的连缀箭头可以一个一个分开。从左向右依次分开, property => 返回值。 箭头前面永远是函数调用需要的参数,后面是返回的值,后面有多个箭头,我们可以看作一个整体(返回值)。 let compare = property => 返回值。  那么调用时, compare(name)  ======》(obj1,obj2)=> 返回值,他又是函数,接着调用 compare(name)(obj1,obj2),函数执行了。像这种箭头函数,它返回的永远都是函数,所以是闭包,可以获得前面参数的值。

  箭头函数除了简化书写外,还有一个更重要的内容,就是this 指向。

  箭头函数内部没有自己的this,但是我们可以在它里面使用this,这时this 的指向继承自外围作用域,就是this 会顺着函数的作用域链向上进行查找,直到找到离它最近的一个this,然后使用它。我们都知道this只存在函数中,所以箭头函数内部的this 就指向它的父函数或祖先函数中this,这样我们内部的this值和外部的this 值就统一了,再也不用 let that = this , 用that 去代替this了。

var object = {
    f1: function(){
        console.log(this);
        var f2 = () => console.log(this); 
        f2();
        setTimeout(f2, 1000);
    }
}
object.f1();  // f1 函数内部的this 全都指向 object

  f2函数体中有this, 但它是箭头函数,所以只能向上找,使用包含它的函数中的this, 找到了f1函数, f2 中的this 和f1 中的this 保持一致。这里我们再改一下,把f1 也改成箭头函数,

var object = {
    f1: () => {
        console.log(this);
        var f2 = () => console.log(this); 
        f2();
        setTimeout(f2, 1000);
    }
}
object.f1();  // f1 函数内部的this 全都指向 object

  这时你会发现this 指向了window, 按照 object.f1() 的调用方式,f1 函数中的this 应该指向object.其实不是,箭头函数的this 是在它定义的时候,就已经确定了,就像使用了bind方法,而不是动态绑定了, 当箭头函数调用的时候,真正要确定的是它在定义的时候,它所能向上寻找到的包含它的最外围的函数中this. 我们再来分析一下,f2 向上找f1,  f1 也是箭头函数,它还要向上找,但你发现包含f1的函数没有了,只有全局对象window了,this 指向了window,  箭头函数在调用的时候,它真正确定的是包含f1函数的函数中this 的指向, 如果没有包含函数,就是window全局对象了。

  箭头函数始终保持它里面的this和它外围的包含它的函数中this 一致。再看一个例子

var object = {
    f1: function(){
        console.log(this);
        var f2 = () => console.log(this); 
        f2();
        setTimeout(f2, 1000);
    }
}
var anoter = object.f1;
anoter()  // this 指向了window

  anoter 函数在全局作用域中调用, f1 函数中的this 指向了window, f2 是箭头函数,和它外围的f1 保持一致,里面的this 指向window.

  

 

转载于:https://www.cnblogs.com/SamWeb/p/7140467.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值