Skip to content

Latest commit

 

History

History
323 lines (264 loc) · 6.09 KB

08函数的扩展.md

File metadata and controls

323 lines (264 loc) · 6.09 KB

函数的扩展

1.函数参数的默认值

  • 参数变量是默认声明的,所以不能用let或const再次声明
  • 使用参数默认值时,函数不能有同名参数
function foo(x = 5) {
  let x = 1; // error
  const x = 2; // error
}

// 不报错
function foo(x, x, y) {}

// 报错
function foo(x, x, y = 1) {}
// SyntaxError: Duplicate parameter name not allowed in this context

1.1 与解构赋值默认值结合使用

// 写法一
function m1({x = 0, y = 0} = {}) {
  return [x, y];
}

// 写法二
function m2({x, y} = { x: 0, y: 0 }) {
  return [x, y];
}

// 函数没有参数的情况
m1() // [0, 0]
m2() // [0, 0]

// x 和 y 都有值的情况
m1({x: 3, y: 8}) // [3, 8]
m2({x: 3, y: 8}) // [3, 8]

// x 有值,y 无值的情况
m1({x: 3}) // [3, 0]
m2({x: 3}) // [3, undefined]

// x 和 y 都无值的情况
m1({}) // [0, 0];
m2({}) // [undefined, undefined]

m1({z: 3}) // [0, 0]
m2({z: 3}) // [undefined, undefined]

1.2 参数默认值的位置

  1. 一般设置在尾部,因为设置了默认值可以省略
  2. 参数为undefined触发默认值
function foo(x, y = 6, z = 7) {
  console.log(x, y, z);
}

foo(5, undefined, null)
// 5 6 null

1.3 函数的 length 属性

length:默认值的参数之前的数量

(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2

(function (a = 0, b, c) {}).length // 0
(function (a, b = 1, c) {}).length // 1

1.4 作用域

作用域是单独的作用域,参数使用let声明

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

foo1() // 3
x // 1

function foo2(x, y = function() { x = 2; }) {
  x = 3;
  y();
  console.log(x);
}

foo2() // 2
x // 1

2.rest 参数

  1. 是数组
  2. 只能是最后一个参数
  3. 函数的length属性,不包括 rest 参数

3.严格模式

严格模式只能设置在有参数的函数体外。因为假如函数体设置为严格模式,但参数却应该先于函数体执行,参数并不是严格模式执行的

// 报错
// 执行参数不报错,执行到函数题才报错。正确应该一开始执行就报错
function doSomething(value = 070) {
  'use strict';
  return value;
}

4.name 属性

var bar = function baz() {};
bar.name // "baz"

var f = function () {};
f.name // ES5:""
f.name // ES6:"f"

(new Function).name // "anonymous"
(new Function).bind({}).name // bound anonymous

5.箭头函数

let fn = () => void 1;

5.1 使用注意点

  • 自身没有this,函数体内的this对象等于外层代码的this。
  • 自身没有arguments对象,函数体内的arguments对象对象等于外层代码的arguments对象。
  • 不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。
  • 不可以使用yield命令,因此箭头函数不能用作 Generator 函数。
// 只有函数foo的this
function foo() {
  return () => {
    return () => {
      return () => {
        console.log('id:', this.id);
      };
    };
  };
}

var f = foo.call({id: 1});

var t1 = f.call({id: 2})()(); // id: 1
var t2 = f().call({id: 3})(); // id: 1
var t3 = f()().call({id: 4}); // id: 1

5.2 不适用场景

  • 对象属性函数
  • 需要动态this
// 对象属性函数
const cat = {
  lives: 9,
  jumps: () => {
    this.lives--;
  }
}

// 需要动态this
document.getElementById('press').addEventListener('click', () => {
  this.classList.toggle('on');
});

5.3 嵌套的箭头函数

// Redux compose函数
const pipeline = (...funcs) =>
  val => funcs.reduce((a, b) => b(a), val);

const plus1 = a => a + 1;
const mult2 = a => a * 2;
const addThenMult = pipeline(plus1, mult2);

addThenMult(5)
// 12

6.尾调用优化

尾调用某个函数的最后一步是调用另一个函数

function f(x){
  return g(x);
}

6.1 尾调用优化

只保留内层函数的调用帧

function f() {
  let m = 1;
  let n = 2;
  return g(m + n);
}
f();

// 等同于
function f() {
  return g(3);
}
f();

// 等同于
g(3);

6.2 尾递归

函数调用自身,称为递归。如果尾调用自身,就称为尾递归。

function Fibonacci (n) {
  if ( n <= 1 ) {return 1};

  return Fibonacci(n - 1) + Fibonacci(n - 2);
}

Fibonacci(10) // 89
Fibonacci(100) // 超时
Fibonacci(500) // 超时

function Fibonacci2 (n , ac1 = 1 , ac2 = 1) {
  if( n <= 1 ) {return ac2};

  return Fibonacci2 (n - 1, ac2, ac1 + ac2);
}

Fibonacci2(100) // 573147844013817200000
Fibonacci2(1000) // 7.0330367711422765e+208
Fibonacci2(10000) // Infinity 实测会爆栈!!!

6.3 严格模式

ES6 的尾调用优化只在严格模式下开启,正常模式是无效的。 这是因为在正常模式下,函数内部有两个变量,可以跟踪函数的调用栈。严格模式禁用这两个变量

  • func.arguments:返回调用时函数的参数。
  • func.caller:返回调用当前函数的那个函数。

6.4 尾递归优化的实现

递归会爆栈,改用循环

function tco(f) {
  var value;
  var active = false;
  var accumulated = [];
  
  return function accumulator() {
    accumulated.push(arguments);
    if (!active) {
      active = true;
      while (accumulated.length) {
        value = f.apply(this, accumulated.shift());
      }
      active = false;
      return value;
    }
  };
}

var sum = tco(function(x, y) {
  if (y > 0) {
    return sum(x + 1, y - 1)
  }
  else {
    return x
  }
});

sum(1, 100000)
// 100001

7.函数参数的尾逗号

function clownsEverywhere(
  param1,
  param2,
) { /* ... */ }

clownsEverywhere(
  'foo',
  'bar',
);

8.Function.prototype.toString()

以前会忽略注释

function /* foo comment */ foo () {}

foo.toString()
// "function /* foo comment */ foo () {}"

9.catch 命令的参数省略

允许catch语句省略参数

try {
  // ...
} catch (err) {
  // 处理错误
}

// ES2019
try {
  // ...
} catch {
  // ...
}