美文网首页
第二十四节: ES6 函数扩展

第二十四节: ES6 函数扩展

作者: 时光如剑 | 来源:发表于2020-10-26 19:44 被阅读0次

    1. 函数参数的默认值

    1.1. 函数的默认参数
    function show(a,b){
        console.log(a,b);
    }
    show("welcome","wuwei");  // welcome wuwei
    

    既然是函数封装,那函数封装是不是要具有通用性,

    比如 我突然不想传第二个参数

    function show(a,b){
        console.log(a,b);
    }
    show("welcome","");  // welcome 
    show("welcome");    //welcome undefined
    
    1.1.1. ES6之前设置默认参数

    我们以前是怎么处理的,是不是用我们的短路算法

    function show(a,b){
        b = b || "china"
        console.log(a,b);
    }
    show("welcome","");  //  welcome china
    show("welcome");     //  welcome china
    

    如果我第一个参数不传,我是不是也要一个默认值啊

    function show(a,b){
        a = a || "welcome";
        b = b || "china";
        console.log(a,b);
    }
    show("您好","wuwei");  //  您好 wuwei
    show("","wuwei");     //  welcome wuwei
    
    1.1.2. ES6设置默认参数

    ES6的默认参数,

    function show(a = "欢迎",b = "无为"){
        console.log(a,b);
    }
    show();          //  欢迎 无为
    show("welcome"); //welcome 无为
    
    1.1.3. 默认参数的位置

    起一个不传不行,会挂掉的

    show( , "wuwei"); // 就报错
    

    通常情况下,定义了默认值的参数,应该是函数的尾参数。因为这样比较容易看出来,到底省略了哪些参数。如果非尾部的参数设置默认值,实际上这个参数是没法省略的。

    // 例一
    function f(x = 1, y) {
      return [x, y];
    }
    
    f() // [1, undefined]
    f(2) // [2, undefined])
    f(, 1) // 报错
    f(undefined, 1) // [1, 1]
    
    // 例二
    function f(x, y = 5, z) {
      return [x, y, z];
    }
    
    f() // [undefined, 5, undefined]
    f(1) // [1, 5, undefined]
    f(1, ,2) // 报错
    f(1, undefined, 2) // [1, 5, 2]
    
    1. 2. 默认参数结合对象解构赋值

    遇到我们无法上面的情况怎么办,还记得我们之前的对象解构吗

    function show({a = "欢迎",b = "无为"}){
        console.log(a,b);
    }
    show({a:"welcome",b:"wuwei"});  // welcome wuwei
    show({b:"wuwei"});              //欢迎 wuwei
    show({});                       // 欢迎 无为
    
    // 但是这样就报错
    function show({a = "欢迎",b = "无为"}){
        console.log(a,b);
    }
    show();                  // 报错
    
    // 所以我们对象也得给一个默认值
    function show({a = "欢迎",b = "无为"} = {}){
        console.log(a,b);
    }
    show();                  // 欢迎 无为
    
    1.3. 函数的参数默认已经定义了,你不能再用let,const声明了
    function show(age = 18){
        let age = 20;
        console.log(age);
    }
    show();    // 程序报错,age已经被定义过了
    // Uncaught SyntaxError: Identifier 'age' has already been declared
    

    2. 默认参数对函数length的影响

    指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length属性将失真。

    (function (a) {}).length // 1
    (function (a = 5) {}).length // 0
    (function (a, b, c = 5) {}).length // 2
    
    • fn.length 返回形参个数
    • arguments.length 返回实参个数

    3. 默认值与作用域

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

    var x = 1;
    
    function f(x, y = x) {//AO{x:2,y:2}
      console.log(y);
    }
    
    f(2) // 2
    

    上面代码中,参数y的默认值等于变量x。调用函数f时,参数形成一个单独的作用域。在这个作用域里面,默认值变量x指向第一个参数x,而不是全局变量x,所以输出是2

    let x = 1;
    
    function f(y = x) { //AO{y:1,x:2}
      let x = 2;
      console.log(y);
    }
    
    f() // 1
    

    上面代码中,函数f调用时,参数y = x形成一个单独的作用域。这个作用域里面,变量x本身没有定义,所以指向外层的全局变量x。函数调用时,函数体内部的局部变量x影响不到默认值变量x

    var x = 1;
    
    function foo(x = x) {   //AO{x:1}
      // ...
    }
    
    foo() // ReferenceError: x is not defined
    

    上面代码中,参数x = x形成一个单独作用域。实际执行的是let x = x,由于暂时性死区的原因,这行代码会报错”x 未定义“。

    var x = 1;
    function foo(x, y = function() { x = 2; }) {
      var x = 3;
      y();
      console.log(x);
    }
    
    foo()//3
    x//1
    

    如果将var x = 3var去除,函数foo的内部变量x就指向第一个参数x,与匿名函数内部的x是一致的,所以最后输出的就是2,而外层的全局变量x依然不受影响。

    var x = 1;
    function foo(x, y = function() { x = 2; }) {
      x = 3;
      y();
      console.log(x);
    }
    
    foo() // 2
    x // 1
    

    4. rest 参数

    ES6 引入 rest 参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。reset 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

    4.1. 扩展运算符,reset运算符

    写法就是... 三个点

    两个作用,扩展和剩余

    4.1.1 扩展运算符
    let arr = ["apple","banana","arange"];
    console.log(arr);      //  ["apple", "banana", "arange"]
    console.log(...arr);   //   apple banana arange
    
    // ...arr  是将数组arr的内容每一项扩展出来
    

    ... 能展开数组,

    4.1.2 重置运算符,配合函数使用

    刚讲的扩展运算符,是将[1,2,3,4,5] ==> 1,2,3,4,5

    function show(a){
        console.log(a);  // 1
    }
    show(1,2,3,4,5);
    
    // 如果我想讲 1,2,3,4,5  == > [1,2,3,4,5]
    function show(...a){
        console.log(a);  // [1, 2, 3, 4, 5]
    }
    show(1,2,3,4,5);
    
    4.1.3 解决了arguments问题

    arguments对象不是数组,而是一个类似数组的对象。所以为了使用数组的方法,必须使用Array.prototype.slice.call先将其转为数组。rest 参数就不存在这个问题,它就是一个真正的数组,数组特有的方法都可以使用。下面是一个利用 rest 参数改写数组push方法的例子。

    function show(){
        console.log(arguments);  // Arguments(5) [1, 2, 3, 4, 5, callee: ƒ, Symbol(Symbol.iterator): ƒ]
    }
    show(1,2,3,4,5);
    
    
    // 如果想把arguments这个类数组变成数组
    function show(){
        let arr = Array.prototype.slice.call(arguments)
        console.log(arr);  // [1, 2, 3, 4, 5]
    }
    show(1,2,3,4,5);
    
    // 如果想给这个数组排序
    function show(){
        let arr = Array.prototype.slice.call(arguments)
        arr.sort(function(a,b){return a-b});
        console.log(arr); 
    }
    show(1,102,32,14,5);     //[1, 5, 14, 32, 102]
    
    

    但是如果用ES6排序

    function show(...arr){
        arr.sort(function(a,b){return a-b});
        console.log(arr);  
    }
    show(1,102,32,14,5);   // [1, 5, 14, 32, 102]
    
    4.1.4 传参时扩展

    我们还可以在传参的时候扩展

    // 解构方法
    function show([a,b,c]){
        console.log(a,b,c);  
    } 
    show([1,9,6]);     //  1 9 6
    
    // 扩展运算符,我在实参的时候就让你扩展
    function show(a,b,c){
        console.log(a,b,c);  
    } 
    show(...[1,9,6]);  // 1 9 6
    
    4.1.5 又叫剩余运算符
    function show(a,b,c){
        console.log(a,b,c);  
    } 
    show(1,2,3,4,5);  // 1 2 3
    

    这样我们abc拿到的就是1,2,3其他的是不是就没管啊

    剩余运算符就是将剩余的参数放到一个数组中

    function show(a,b,...c){
        console.log(a,b,c);  
    } 
    show(1,2,3,4,5);  // 1 2 [3,4,5]
    
    // 这个时候发现c是出了a,b对应的那两项外剩余的所有参数组成的数组
    

    如果当成剩余运算符来用必须放最后

    注意哦!下面的写法会报错

    function show(a,...b,c){
        console.log(a,b,c);  
    } 
    show(1,2,3,4,5);  // 报错
    

    如果我们需要赋值一份数组,

    var arr = [2,3,6,5,8];
    var arr2 = [...arr];
    
    // 以前Array身上还有一个from方法,拷贝数组
    var arr = [2,3,6,5,8];
    var arr2 = Array.from(arr);
    
    // 这个方法还可以将字符串变成数组
    var str = "abc";
    var arr = Array.from(str); 
    console.log(arr);   //["a", "b", "c"]
    

    总结

    1. 展开数组

      ...[1,2,3] ==> 1,2,3

    2. 重置为数组

      ... 1,2,3 ==> [1,2,3]; 只能在传参的时候用

    3. 剩余参数,必须放到最后

      function(a,b,...c){}

    4.1.6 函数的length不包括rest参数
    (function(a) {}).length  // 1
    (function(...a) {}).length  // 0
    (function(a, ...b) {}).length  // 1
    

    5. 箭头函数

    5.1. 基本用法

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

    箭头函数

    ​ () => {

    ​ }

    5.1.1 箭头函数里面只有return语句
    //  之前函数的写法
    function show(){
        return 1;
    }
    
    // 箭头函数的写法
    let show = () => 1;
    
    // 这里的1 就是return 1
    
    function show(a,b){
        return a + b;
    }
    
    // ES 6
    let show = (a,b) => a + b;
    

    注意如果我想return一个对象,这样写就会出问题

    // 报错
    let getTempItem = id => { id: id, name: "Temp" };
    

    由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。

    // 不报错
    let getTempItem = id => ({ id: id, name: "Temp" });
    
    5.1.2 常用的使用方法

    如果想写语句,那么语句的写法是这样的

    () => {

    ​ 语句;

    ​ return;

    }

    这才是一个完整的箭头函数的写法

    let show = (a,b) => {
        console.log(a,b);
        return a + b;
    }
    show();
    
    5.2. 箭头函数注意点

    箭头函数需要注意一下几点:

    5.2.1 箭头函数的this指向
    let obj = {
        age: 18,
        show: function(){
            console.log(this.age);
        }
    }
    obj.show();   // 毋庸置疑 打印18 this 指向obj
    
    // 如果换成箭头函数呢
    var age = 20;
    let obj = {
        age: 18,
        show: () =>{
            console.log(this.age);
        }
    }
    obj.show();
    
    
    // 关于this指向定义时作用域中的this
    var a = 11;
    function test(){
        this.a = 22;
        let b = () =>{
            console.log(this.a)
        }
        b()
    }
    var obj = new test();
    
    // 如果我里面添加定时器
    let obj = {
        age : 18,
        show: function(){
            setTimeout(function(){
                alert(this.age); // 此时弹出undefined,此时this指向window
            },2000)
        }
    }
    obj.show();
    
    // 如果使用箭头函数呢
    let obj = {
        age:18,
        show: function(){
            setTimeout(()=>{
                alert(this.age);  // 这里会弹出18  ,为什么呢
            },2000)
        }
    }
    obj.show();
    

    此时就是this的问题,以前我们讲this的时候,说this指向运行时,也就是调用这个函数运行的对象,

    但是箭头函数的this指向,函数定义时所在函数的this对象

    因为箭头函数this指向的固定化,并不是因为箭头函数内部有绑定this的机制,实际原因是箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。正是因为它没有this,所以也就不能用作构造函数。

    5.2.2 箭头函数中的arguments

    在箭头函数中没有arguments

    // 普通函数
    function show(){
        console.log(arguments);
    }
    show(1,2,3,4,5);  // Arguments(5) [1, 2, 3, 4, 5, callee: ƒ, Symbol(Symbol.iterator): ƒ]
    
    // 箭头函数
    let show = () => {
        console.log(arguments);
    }
    show(1,2,3,4,5); // 报错
    // Uncaught ReferenceError: arguments is not defined at show
    

    那在箭头函数里面没有arguments,那用什么呢,可以用剩余参数

    // 箭头函数
    let show = (...args) => {
        console.log(args);
    }
    show(1,2,3,4,5);  // 打印 [1, 2, 3, 4, 5]
    
    5.2.3 箭头函数不能到构造函数

    因为箭头函数内部没有this,所有不能当成构造函数使用

    // 普通的构造函数
    function Show(){
        this.name = "abc";
    }
    let show = new Show();
    console.log(show.name);  // abc
    
    
    let Show = () => {
        this.name = "abc"
    }
    let show = new Show();
    console.log(show.name);  // 报错  类型错误
    // Uncaught TypeError: Show is not a constructor
    
    5.2.4 总结一下
    1. 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
    2. 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。
    3. 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
    5.3. 函数参数的尾逗号

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

    这样的规定也使得,函数参数与数组和对象的尾逗号规则,保持一致了。

    知道一点东西

    function show(a,b,c,){
        console.log(a,b,c)
    }
    show(1,2,3,)
    // 以前这么写会报错,参数最后不能加逗号结束
    // 但是ES2017 以后就不报错了
    

    相关文章

      网友评论

          本文标题:第二十四节: ES6 函数扩展

          本文链接:https://www.haomeiwen.com/subject/osmgmktx.html