zoukankan      html  css  js  c++  java
  • ES6随笔--各数据类型的扩展(3)--函数

    ES6随笔--各数据类型的扩展(3)--函数

    1. 函数参数的默认值

    可以在函数参数中指定默认值,函数内不能再用letconst声明;

    使用参数默认值时,不能出现同名参数;

    参数默认值惰性求值,每次调用函数会重新计算参数默认值表达式;

    let x = 99;
    function foo(p = x + 1) {
      console.log(p);
    }
    
    foo() // 100
    
    x = 100;
    foo() // 101
    

    可以与解构赋值默认值结合使用:

    function ({x, y = 5} = {}) {
        console.log(x, y);
    }
    function(); // undefined 5
    // 以下对比默认值设置不同方式
          function bar({x, y} = {x:0, y:0}) {
            console.log(x, y);
          }
          function bar1({x=0, y=0} = {}) {
            console.log(x, y);
          }
          bar();     //0  0
          bar1();    //0  0
          bar({x:2, y:3});    //2  3
          bar1({x:2, y:3});   //2  3
          bar({x:2});   // 2  undefined
          bar1({x:2});  // 2  0
          bar({y:2});   // undefined   2
          bar1({y:2});  // 0  2
          bar({z:4});   // undefined  undefined
          bar1({z:4});   // 0  0
          bar({});   // undefined  undefined
          bar1({});   // 0  0
    

    设置了默认值的参数一般是函数的尾参数,该参数可以省略;如果位于中间,则无法只省略该参数继续为后面的参数赋值,必须要设置为undefined,否则直接省略而为后面的参数赋值会报错;
    undefined可以触发默认值;
    函数的length属性中不包含指定了默认值的参数。

    指定了参数默认值的函数,函数进行声明初始化时该参数会形成单独的作用域;函数执行完毕后该作用域消失;

    // 函数内部 let 声明的变量与参数变量不同作用域
          var x = 1;
          function foo(y = x) {
            let x = 2;
            console.log(y); 
          }
    
          foo() // 1
    // 参数的作用域与全局和函数内部都不同;
          var x = 1;
          function foo(x, y = x) {
            x = 2;
            console.log(y); 
          }
          foo(); // undefined
          foo(2); // 2
          console.log(x); // 1
    // 参数设置默认值时指向全局变量x, 函数内部修改了全局变量,但并不影响参数y--不在同一个作用域;
          var x = 1;
          function foo(y = x) {
            x = 2;
            console.log(y); 
          }
          foo(); // 1
          console.log(x); // 2
    
    // 暂时性死区报错(参数默认值相当于 let x = x,此时会忽略全局里的定义)
          var x = 1;
          function foo(x = x) {
            console.log(x); 
          }
    
          foo() // ReferenceError: x is not defined
    

    函数的参数默认值可以用来设置哪些参数不能省略;

    2. rest函数

    形式为...varialbleName,获取函数的多余参数放入数组中;

    // 改写 push 方法的例子 
          function push(array, ...items) {
            items.forEach(function(item) {
              array.push(item);
              console.log(item);
            });
            return array.length;
          }
          var a = [];
          console.log(push(a, 1, 2, 3)); // 1 2 3 3
    

    rest参数只能是最后一个参数;函数的length属性,不包含rest函数;

    3. 严格模式

    只要函数参数使用了默认值,解构赋值或者扩展运算符,函数内部就不能显式设定严格模式;
    替代的办法 1) 使用全局性的严格模式;2) 使用立即执行的无参函数,使用严格模式并返回该函数;

    4. name 属性

    函数的name属性,返回该函数的函数名;

    如果把匿名函数赋值给一个变量,匿名函数的name属性返回该变量名(ES5 则为空字符串);

    如果把一个具名函数赋值给一个变量,该函数的name属性返回原本的函数名。

          const f = function () {};
          console.log(f.name); // f
          const f1 = function func() {};
          console.log(f1.name); // func
    

    Function构造函数返回的函数实例, name属性返回anonymous;

    使用bind()返回的函数,name属性会自动在函数名前面加bound

    5. 箭头函数

    使用箭头=>定义函数;funcName(args) => {code block}function funcName(args) = {code block}相同;

    注意:
    1. 箭头函数中的this绑定函数定义时所在的作用域,而不是运行时的作用域;
    2. 不能使用new命令;
    3. 不可以使用argumets对象,可以使用rest参数代替;
    4. 不可以使用yield命令,因此箭头函数不能用作Generator函数;

    箭头函数本身没有自己的this,在定义时指向外部作用域的对象;也不能通过call,bind,apply改变this的指向。

          var f = function () {console.log(this.id);}
          var f1 = () => {console.log(this.id)};
          f.call({id:11});  // 11
          f.call({id:22});  // 22
          f1.call({id:11}); // undefined
          f1.call({id:22}); // undefined
    
    // 大括号被解释为代码块,所以需要返回一个对象的情况下需要在大括号外加小括号;(再加一层大括号无效);
          var f = (x) => {a:x};
          console.log(f(3)); // undefined
          var f = (x) => ({a:x});
          console.log(f(3)); // a : 3
    

    嵌套函数;

    部署管道机制;

    改写λ演算;

    
    

    6. 双冒号运算符(提案)

    用来取代bind, apply, call的作用,绑定对象作用域;

    // 将左边对象,作为上下文环境(即this 对象),绑定到右边的函数上
    object::function
    
    // 双冒号左边为空,右边为一个对象的方法,代表将此方法绑定到该对象上;
    var method = obj::obj.foo;
    // 等同于
    var method = ::obj.foo;
    

    7. 尾调用优化

    尾调用(Tail Call)函数的最后一步是调用另一个函数。

    // 这种情况不属于尾调用,相当于调用g(x)后返回undefined;‘return g(x)’算是一种尾调用。
          function f(x){
            g(x);
          }
    

    在函数内部调用其他函数时,所有当前被调用而未结束的函数的调用帧会形成调用栈;

    尾调用由于是最后一步操作,所以不需要保留外部函数的调用帧,因为调用位置、内部变量等信息都不会再用到。

    尾调用优化,即只保留最后调用函数的调用帧。但是需要做到内部调用函数不再使用外部函数的内部变量;

    函数尾调用自身,即尾递归;

          //尾调用 Fibonacci数列案例
          function fib(n) {
            if (n<=1) return 1;
            return fib(n-1)+fib(n-2);
          }
          function fib1(n, ac1=1, ac2=1) {
            if (n<=1) return ac2;
            return fib1(n-1, ac2, ac1+ac2);
          }
          console.log(fib(10)); //~20ms 是优化函数运行时间至少10倍;
          console.log(fib1(10));  //~1ms
          console.log(fib(100));   // 无法计算,栈溢出
          console.log(fib1(100));   // <10ms
    

    尾递归优化只能在严格模式下开启,正常模式无法执行;

    尾递归优化在非严格模式下无效;实现方法可以使用循环代替:

    // 非严格模式下实现尾调用优化--循环
    // 递归函数
            function test(x, y) {
              if (y > 0) {
                return test(x+1, y-1);
              } else {
                return x;
              }
            }
    // 蹦床函数
            function trampoline(fn) {
                while (fn && fn instanceof Function) {
                  fn = fn();
                }
                return fn;          
            }
    //改写递归函数
            function test1(x, y) {
              if (y > 0) {
                return test1.bind(null, x+1, y-1);
              } else {
                return x;
              }
            }
    // 通过蹦床函数调用递归函数
            var a1 = trampoline(test1(1, 100000));
            console.log(a1); // 100001
    

    8. 函数参数的尾逗号

    ECMA2017允许函数的最后一个参数后面有逗号;

    附加个人随记:bind方法

    bind可以直接指定绑定的对象,以及对应的参数,返回绑定后的函数,后面加括号才会执行;

    function testbind(str) {
      return str + this;
    }
    var a1 = {name:"Bai"};
    console.log(testbind.bind(a1)); 
    // ƒ testbind(str) {
    //   return str + this;
    // }
    console.log(testbind.bind(a1,"huahua")); 
    // ƒ testbind(str) {
    //   return str + this;
    // }
    console.log(testbind()); //undefined[object Window]
    console.log(testbind.bind(a1,"huahua")()); //huahua[object Object]
    console.log(testbind.bind(a1)("huahua")); //huahua[object Object]
    

    生如夏花般绚烂,死如秋叶般静美
  • 相关阅读:
    迅雷的工作原理 [揭密迅雷]
    揭密迅雷BT式下载本质 [揭密迅雷]
    2010年8月09日_周一_Toc control
    2010年8月08日_周日_MapCopyrightText control
    2010年8月08日_周日_Magnifier control
    2010年8月07日_周六_HoverExpandExtender control
    2010年8月11日_周三_ZoomLevel_control
    ArcGIS Server网站发布后地图显示空白的原因之一
    2010年8月10日_周二_TaskManagercontrol
    搭建一个简单的callBack函数
  • 原文地址:https://www.cnblogs.com/muTing/p/9191616.html
Copyright © 2011-2022 走看看