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]

生如夏花般绚烂,死如秋叶般静美
原文地址:https://www.cnblogs.com/muTing/p/9191616.html