ES6总结 第七章:函数扩展

一. 函数参数的默认值

  1. 函数参数默认值
  • function log(x ,y = 'World'){console.log(x,y};
  • 参数默认值不是传值的,每次都重新计算默认值表达式的值,即惰性求值:每次调用重新计算。也就是说,函数声明初始化时,参数会形成一个单独的作用域(context)。初始化结束后作用域消失。
  1. 可以与解构赋值默认值结合
  • 只有参数为对象{}时,变量x和y才会通过解构赋值生成。若不是对象,就会报错,即不能省略。
  1. 结合函数参数的默认值,结构赋值可以省略第二个参数

console.log(method);
}

- 函数参数默认值 -> 对象解构赋值默认值:**推荐** {x=0,y=0} = {} 

4. 参数默认值的位置
- 应该是函数的尾参数,非尾部参数设置默认值,实际上是无法省略的,即无法只省略该参数而不省略其后的参数,除非显式传入undefined。

5. length
-指定默认值之后,函数length属性返回没有指定默认值的参数个数,也就是说length将失真。
- 因为length含义为期望传入参数个数。
- rest参数不计入length。
- length不计入默认值之后的参数。
- length也不包括rest参数。

6. 函数变量注意事项
- 变量的默认参数y = xxxx()时,运行时才执行函数xxxx(),若参数已经赋值,默认值中的函数就不会运行。
- 参数默认为undefined表示可以省略。

## 二. rest参数(代表一个数组,可以用数组方法):自然简洁,但只能是最后一个参数
- 形式为“...变量名”,

function add(...values) {
let sum = 0;
for (let val of values) {
sum += val;
return sum;
}
add(2, 5, 3) // 10

- 利用rest参数可以向add函数中传入任意数目的参数。

// arguments变量写法
function sortNumbers() {
return Array.prototype.slice.call(arguments).sort();
}
// rest参数写法
guibiconst sortNumbers = (...numbers) => numbers.sort();

## 三.严格模式 
函数内部的严格模式会禁用默认值,解构赋值,扩展运算符。
规避这种限制方法:
- 设定全局性的严格模式
- 把函数包在一个无参数的立即执行函数里面

## 四.name属性
- ES6中,**匿名函数**赋值给一个变量,name会返回实际的函数名。ES5返回空字符串。

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

- 具名函数赋值给一个变量,ES5/6的name都会返回原本名字。
- bind返回的函数,name属性会加上bound前缀。

## 五.箭头函数
- 一个用处时简化回调函数
**注意事项**:
1. 函数体内的this对象就是定义时所在的对象,而不是使用时所在的对象。
this对象的指向是可变的,但在箭头函数中它是固定的。

2. 不可以当作构造函数。也就是说,不可以使用new命令,否则会抛出一个错误。

3. 不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用rest参数代替。

4. 不可以使用yield命令,因此箭头函数不能用作Generator函数。

**注意**:箭头函数可以让this指向固定化,这种特性非常有利于**封装回调函数**。实际原因是:**箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。这也是箭头函数不能用作构造函数的原因。**

**注意**:**this,arguments,super,new.target**四个变量在箭头函数中也是不存在的,分别指向外层函数的对应变量。

**注意**:箭头函数没有自己的this,因此不能用**call(),apply(),bind()**来改变this指向。

## 六.部署管道机制pipeline
即前一个函数的输出是后一个函数的输入。

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


## 七.λ演算
暂略

## 八.ES7绑定this::
- babel转码器支持foo::bar形式的绑定。作为箭头函数的补充。
- 双冒号左边对象,右边函数,运算符会自动将左边的对象作为上下文环境绑定到右边的函数上。
foo::bar;等同于bar.bind(foo)
foo::bar(...arguments);等同于bar.apply(foo, arguments);
- 如果双冒号左边为空,右边是一个对象的方法,则等于将该方法绑定在该对象上。

## 九.**尾调用优化**:严格模式启用
**尾调用**定义:某个函数最后一步是调用另一个函数,尾调用不一定出现在尾部,只要是最后一步操作即可。

**尾调用优化**:只保留内层函数的调用帧。

**尾递归**:尾调用自身。
尾递归的Fibonacci数列实现如下:

function Fibonacci (n, ac1 = 1, ac2 = 1) {
// 关键:返回值是ac2
if ( n <= 1) { return ac2 };
return Fibonacci ( n-1, ac2, ac1 + ac2 );
}

尾递归的阶乘函数实现如下:

function factorial ( n, total) {
// 关键:返回值为total
if ( n === 1 ) return total;
return factorial ( n - 1, n * total );
}
factorial (5, 1) //120


## 十.递归函数改写
1. Curring柯里化:多参数函数转换为单参数形式。

function tailFactorial (n, total) {
if ( n === 1 ) return total;
return tailFactorial ( n-1, n*total);
}
function currying ( fn, n ) {
return function (m) {
return fn.call( this, m, n);
}
const factorial = curring (tailFactorial, 1);
factorial(5) //120

2. 采用ES6的函数默认值。

function factorial (n, total = 1) {
if ( n === 1 ) return total;
return factorial ( n - 1, n * total );
}
factorial(5) // 120


## 十一.非严格模式下的尾递归优化
1. 蹦床函数:个人理解为通过返回函数新版本,从而消除调用栈,也就没有调用栈过大的问题了。

function trampoline (f) {
while (f && f instanceof function) {
f = f();
}
return f;
}
function sum (x, y){
if ( y > 0 ){
// 通过 bind(null) 来消除调用栈
return sum.bind(null, x+1, y-1);
}
}
trampoline (sum(1, 10000)); // 10001

2. 真正尾递归优化:
**关键**:奥妙在于状态变量actice。默认情况不被激活,进入尾递归优化的过程就被激活。然后每一轮递归sum的返回值都是undefined,所以就避免了递归执行;而accumulated数组存放每一轮sum执行的函数,总是有值的,这就保证了accumulator函数内部的while循环总会执行,很巧妙地将“递归”改成了“循环”,而后一轮的参数会取代前一轮的参数,保证了调用栈只有一层。

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, 10000)
// 10001

你可能感兴趣的:(ES6总结 第七章:函数扩展)