函数绑定运算符
  1. 使用方式:用并排的两个冒号来表示::,双冒号左边是一个对象,右边是一个函数.该运算符会自动将左边的对象作为上下文环境(this).
  2. 例如:foo::bar等价于bar.bind(foo)
  3. 如果双冒号左边为空,右边是一个对象的话,则等于将该方法绑定在该对象上.例如:var method=obj::obj.foo等价于var method=::obj.foo
尾调用优化
  1. 所谓尾调用就是指某个函数的最后一步是调用另一个函数
//尾调用的基本形式
 function f(x){
 	return g(x);	//这里如果是return g(x)+1;则不属于尾调用.也就是说尾调用之后不能有任何操作
 }
  1. 尾调用优化的原理:函数在调用的时候会在内存形成一个调用记录,又称调用帧,保存调用位置和内部变量等信息。如果在函数A内调用函数B,那么在A的调用帧上方还会有一个函数B的调用帧,等到函数B执行完成返回结果给A,函数B的调用帧才会消失。尾调用由于是函数的最后一步,所以不需要保留外层函数的调用帧,因为调用位置、内部变量不会再用到了,只需要直接用内部函数的调用帧取代外层函数的调用帧即可。这就叫尾调用优化,即只保留内层函数的调用帧
function f(){
 	let m=1;
 	let n=2;
 	return g(m+n);
 }
 f();
 //等价于
 function f(){
 	return g(3);
 }
 //等价于
 g(3);
  1. 如果尾调用还要用到外层函数的内部变量,则不能进行尾调用优化
function addOne(a){
 	var one=1;
 	function inner(b){
 		return b+one;	//用到了外层函数addOne的n内部变量one,因此不能优化
 	}
 	return inner(a);
 }
  1. ES6的尾调用优化只在严格模式下开启,正常模式无效,因为正常模式下函数内部的arguments和caller(返回调用当前函数的函数)可以跟踪函数的调用栈
尾递归
  1. 尾递归即尾调用自身
  2. 尾递归的优势:递归非常消耗内存,因为需要同时保护成百上千个调用帧,很容易发生栈溢出,但是尾递归只存在一个调用帧,所以不会发生栈溢出
  3. 举例:
//常规递归阶乘函数
 function f(n){
 	if(n===1) return 1;
 	return n*f(n-1);
 }
 //尾递归改写
 function f(n,total=1){
 	if(n===1) return total;
 	return f(n-1,n*total);
 }
 f(5) ==>120
 //常规斐波那契数列
 function f(n){
 	if(n<=2) return 1;
 	return f(n-1)+f(n-2);
 }
 //尾递归改写
 function f(n,a=1,b=1){
 	if(n<=2) return b;
 	return f(n-1,b,a+b);
 }