第三章 函数数组
函数形参的默认值闭包
ES5模拟默认参数app
function makeRequest (url, timeout, callback) { timeout = timeout || 2000; callback = callback || function () {}; }
为形参赋予默认值,说明了该参数为可选参数。函数
ES6的默认参数值优化
function makeRequest (url, timeout = 2000, callback = function () {}){ // 函数执行 }
须要注意的是,在参数中,null也是合法的参数值,因此传入null的时候不会用默认值替代。this
ES6真正的可选参数是参数后面带?声明。url
默认参数值对arguments对象的影响rest
在ES5非严格模式中,函数命名参数的变化会体如今arguments对象中。code
在ES5严格模式中,不管参数如何变化,arguments对象再也不随之改变。对象
ES6默认处于严格模式,默认参数值的存在使得arguments对象保持与命名参数的分离。
默认参数表达式
默认参数值能够使用任意数据类型,甚至是函数执行返回的结果。
ES6的参数声明具备临时性死区的特性,不能在声明以前调用。
function add (first = second, second = 1) { return first + second; } // 这是一个运行时才会抛出的错误,只有当first传入了非法值,引起默认值赋值的时候,才会抛出error。 add(1, 2); //3,没有使用默认值 add(); // ReferenceError,使用默认值时候发如今second声明前使用了second
处理无命名参数
ES5中的无命名参数
ES5调用arguments对象来获取全部的实参,从而间接地对无命名参数操做。
不定参数
ES6引入在命名参数前的三个点...来代表不定参数(rest parameters)。全部自它以后传入的全部参数,都被归入以这个参数命名的数组中。ES6中arguments依然存在,可是应当尽量地使用不定参数来替代它的做用。
使用限制:
加强的Function构造函数
ES6加强了Function构造函数的功能,支持在建立函数时定义默认参数和不定参数。
var add = new Function('first', 'second = 1', 'return first + second;'); add(1, 1); // 2 add(1); // 2
name属性
ES6为全部函数添加了name属性,其特性为:
不能使用name来获取对于函数的引用。
展开运算符
和不定参数使用相对的是展开运算符,一样也是...声明。
它使用在传递实参的时候,将一个Array展开为参数表的形式。
let values = [25, 50, 75, 100]; // 二者等价 Math.max.apply(Math, values); Math.max(...values);
明确函数的多重用途
JS函数有两个不一样的内部方法: [[Call]]和[[Construct]]。
经过new关键字调用函数时,执行的是[[Construct]]函数,它负责建立一个实例对象,而后再执行函数体,将this绑定到实例上。
不经过new调用函数,则执行[[Call]]函数,直接执行代码中的函数体。
ES5判断函数被调用方法
使用this instanceof class的形式。
function Person (name) { // new关键字将this绑定到实例 if(this instanceof Person) { this.name = name; } // [[Call]]调用不会绑定this到实例 else { throw new Error('必须使用new关键字调用Person'); } }
元属性(Metaproperty)new.target
new.target表示new操做符的目标,即该构造函数。若是[[Call]]方式调用,则其为undefined。
块级函数
ES6引入块级做用域后,在块级做用域中声明的函数只能在块内使用,当跳出块后,该函数将没法访问。
严格模式下,function声明的块级函数会被提高到块级做用域的顶部。
非严格模式下,function声明的跨级函数会被提高到外层的函数做用域。
箭头函数(Lambda)
与传统函数的不一样:
lambda表达式也有name属性,相似于函数。
没有this绑定
lambda表达式的this必须经过查找做用域链来决定其值。
箭头函数的返回值
没有用{}包裹时,lambda表达式只能有一条执行语句,并返回该语句的返回值。
var result = [].sort((a, b) => a - b); // 等同于 return a - b
尾递归优化(TCO)
function TCO (fn) { let value, active = false, accumulated = []; // 在f内部进行递归调用的时候,其实调用的是这个返回的accumulator // 这里须要注意的是,要将f中的递归调用函数名使用accumulator被赋予的变量名 return function accumulator(...args){ accumulated.push(args); if(!active) { active = true; while(accumulated.length) { /** * 因为每次须要递归的fn.apply都会致使accumulated被push进一个新的arguments, * 因此这个while一直要到所有执行结束才会跳出, * 但这种结构只会保持最多两层的调用栈 */ value = fn.apply(this, accumulated.shift()); } active = false; return value; } } } var sum = TCO(function(x, y) { if(y > 0) { // 再次调用的实际上是闭包返回的accumulator,而不是这个传入的function return sum(x + 1, y - 1); } else { return x; } });