es6学习之函数

一.函数的默认值

(1).只有当函数提供的参数是严格等于undefined的时候,才会应用默认值
(2).函数参数中的变量不能在函数体中重复声明,也不能在函数参数作用域内重复声明
(3).函数的默认值是惰性求值的,不应用到是不会求值的
(4).函数带有默认值的参数必须放在最后一个,否则js不知道哪一个是省略的,阅读也不友好
(5).es5中函数的参数length属性失真,es6只会计算含有默认值的参数的前面的参数个数
(6).参数有默认值的情况下,函数参数会有自己的作用域
(7).可以和解构赋值一起用

// 默认值
function log(x, y = false) {
  console.log(x, y);
}
/**
作用域,这里存在3个作用域,一个是函数外部作用域,一个是函数bar的参数作用域,
一个是函数内部作用域.函数bar的参数作用域中返回变量foo,但是函数bar的参数
作用域中并未定义foo变量,所以向上查找,找到外层作用域的foo,当执行的时候才返回
outer;但是,内部的console.log(foo)是先查找本作用域即函数内部作用域找到foo为
inner,所以返回inner
*/
let foo = 'outer';
function bar(func = () => foo) {
  let foo = 'inner';
  console.log(func());
  console.log(foo);
}
bar(); // outer
// inner
// 结合解构赋值
function fetch(url, { body = '', method = 'GET', headers = {} } = {}) {
  console.log(method);
}
fetch('http://example.com')
// "GET"
函数默认值推荐的应用

限制参数必须提供,否则报错

function throwIfMissing() {
  throw new Error('Missing parameter');
}
function foo(mustBeProvided = throwIfMissing()) {
  return mustBeProvided;
}
foo()
// Error: Missing parameter

二.函数rest参数(扩展运算符)

(1)rest参数是为替代arguments参数的,表示剩余的参数
(2)rest参数本身是数组,可遍历
(3)rest参数只能在函数参数的最后

// rest参数的写法
const sortNumbers = (...numbers) => numbers.sort();
console.log(sortNumbers(2,5,1,2,20,8,7)); // [1, 2, 2, 20, 5, 7, 8]

三.严格模式

es6改变了函数体内部可以声明严格模式的限制,在es6中,只要使用函数参数默认值,解构赋值或者扩展运算符的都不能在函数内部声明严格模式
原因如下:
函数体的严格模式同样适用于函数参数和函数体,函数执行的时候,是先执行函数参数,但是要等到函数体的时候才能知道是不是严格模式,这不合理
有效的规避方式:
方法一:

// 全局严格模式
'use strict';
function doSomething(a, b = a) {
  // code
}

方法二:

// 将函数包含在无参的立即执行函数中
const doSomething = (function () {
  'use strict';
  return function(value = 42) {
    return value;
  };
}());

四.name属性

es6更改了es5的函数name属性

var f = function () {};

// ES5
f.name // ""

// ES6
f.name // "f"
const bar = function baz() {};

// ES5
bar.name // "baz"

// ES6
bar.name // "baz"
(new Function).name // "anonymous"
function foo() {};
foo.bind({}).name // "bound foo"

(function(){}).bind({}).name // "bound "

五.箭头函数

(1)和es5不同,不会改变this对象,而是使用定义时所在的对象,本身无对象
(2)不能做构造函数,不能用new 命令
(3)不存在arguments,super,new.target,不能使用call,apply,bind方法
(4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

六.双冒号运算符

一个提案,不知道会不会成为标准

foo::bar;
// 等同于
bar.bind(foo);

7.尾调用优化

尾调用就是在函数执行的最后一布调用另一个函数,只能是最后一步,打不是必须放在函数的最后
已下三种不是尾调用

// 情况一,g函数执行不是最后执行的
function f(x){
  let y = g(x);
  return y;
}

// 情况二,g函数执行不是最后执行的
function f(x){
  return g(x) + 1;
}

// 情况三,没有返回g函数,
function f(x){
  g(x);
}

1.尾调用优化

原因:
函数调用会在内存形成一个“调用记录”,又称“调用帧”(call frame),保存调用位置和内部变量等信息。如果在函数A的内部调用函数B,那么在A的调用帧上方,还会形成一个B的调用帧。等到B运行结束,将结果返回到A,B的调用帧才会消失。如果函数B内部还调用函数C,那就还有一个C的调用帧,以此类推。所有的调用帧,就形成一个“调用栈”(call stack)。

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

只有不再用到外层函数的内部变量,内层函数的调用帧才会取代外层函数的调用帧,否则就无法进行“尾调用优化”。

// 不是尾调用优化,因为one变量被inner用了
function addOne(a){
  var one = 1;
  function inner(b){
    return b + one;
  }
  return inner(a);
}

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

至于更加深入的学习,请参考: http://es6.ruanyifeng.com/#docs/function#%E7%AE%AD%E5%A4%B4%E5%87%BD%E6%95%B0

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值