主要知识点有: 函数参数默认值, 剩余参数, 扩展运算符, new.target 属性, 块级函数, 箭头函数以及尾调用优化
函数. png
1. 函数参数默认值
函数参数默认值
- let defaultFunc = function(url,tomeout=2000,callback={}){};
- // 使用默认的 timeout 和 callback
- defaultFunc('/url');
- // 使用默认的 callback
- defaultFunc('/url',100);
- // 使用指定的 timeout 和 callback
- defaultFunc('/url',100,function(body){
- doSomething(body);
- })
函数参数默认值的指定顺序可以随意
- // 需要指定默认参数的可选参数 timeout, 排在 callback
- let defaultFunc = function(url,tomeout=2000,callback){};
只有在未传递参数, 或者参数为 undefined 时, 才会使用默认参数, null 值被认为是有效的
- //null 被认为是有效的, 不会使用 timeout 的默认值
- defaultFunc('/url',null,function(body){
- doSomething(body);
- })
函数参数默认值表达式
除了直接使用具体值赋给函数参数默认值外, 函数参数默认值还可以有表达式构成.
- function getValue(){return 5};
- function test(a,b=getValue()){
- return a+b;
- }
- // 调用 getValue
- console.log(test(1)); //6
- // 不使用 b 的默认值 s
- console.log(test(1,2)); //3
可以使用前面的参数, 来作为后面参数的默认值. 前面的参数默认值不能引用后面的参数值
- function add(a,b=1){return a+b};
- console.log(add(1,1)); //2
- function addition(a=b,b){return a+b};
- console.log(addition(undefined,1)); //Uncaught ReferenceError: b is not defined
函数参数默认值存在暂时性死区(TDZ), 在函数参数默认值表达式中, 还未初始化赋值的参数值无法作为其他参数的默认值
函数参数默认值对 arguments 对象的影响
- ES5 中, 在非严格模式下, arguments 对象能够反映出具名参数的变化, 当具名参数值更新的时候, arguments 对象中相应的元素值也会更新. 在严格模式下不能反映出具名参数的变化
- function testArgs(a,b){
- console.log(a===arguments[0]); //true
- console.log(b===arguments[1]); //true
- a='c';
- b='d';
- console.log(a===arguments[0]); //true
- console.log(b===arguments[1]); //true
- }
- testArgs('a','b');
- 在 ES6 中, 参数默认值与 arguments 对象分离, 即被赋予参数默认值的参数无法从 arguments 对象中获取. 另外, 无论是非严格模式下, 还是在严格模式下, 具名参数的更改都不会在 arguments 对象中更新.
2. 剩余参数
ES6 中, 当参数个数无法确定时, 可以使用剩余参数 (rest parameter) 来表示, 剩余参数就相当于一个容器, 调用函数时传入几个参数值, 这个容器就装载几个参数值. 剩余参数能够将多个独立的参数合并到一个数组中去, 剩余参数表示为...keys, 有三个点加上一个具名参数标识符组成.
- function rp(...keys){console.log(keys.length)}
- rp(1,2); //2
具名参数只能放在参数组最后面, 并且只能有且仅有一个剩余参数. 剩余参数不能作为对象字面量的 setter 属性
- let person ={
- set name(...names); //Setter function argument must not be a rest parameter
- }
在 Function 构造器中能够将函数体以字符串的形式作为函数的参数, 并且支持参数默认值以及剩余参数
- var add = new Function("first","second","return first+second");
- console.log(add(1,1)); //2
3. 扩展运算符
扩展运算符能够将数组分离, 将分割后单独的参数值传递给函数, 能够替代 apply()方法的使用.
console.log(Math.min(0,...[1,2,3,4])); //0
4. new.target 属性
能够使用 new.target 属性来判断函数是否利用 new 来进行调用
- function target(){
- if(new.target!=='undefined'){
- console.log('通过 new 来调用');
- }else{
- console.log('不是通过 new 来调用');
- }
- }
5. 块级函数
在代码块中能够声明函数, 函数也被称之为块级函数, 在严格模式下, 块级函数会提升到当前所处代码块的顶部, 在整个代码块中都能够被访问, 在代码块外的地方就不能被访问. 而在非严格模式下, 块级函数会被提升到全局作用域.
6. 箭头函数
箭头函数提供了一种更加简洁的函数书写方式, 并且与传统函数有很多不同的地方. 箭头函数基本的语法为: 参数 => 函数体.
根据参数的个数以及函数体的行数有多种变形:
无参数时, 可以使用圆括号 () 表示; 当只有一个参数时可以省略圆括号(); 当有多个参数时可以使用圆括号包裹, 并参数之间用逗号进行分隔;
当函数体有多行语句时, 使用大括号 {} 包裹起来, 就像写正常的函数一样; 当只有一行语句时, 并需要返回结果时, 可以省去大括号{}, 结果会自动返回.
如果需要返回对象的话, 需要使用圆括号 () 将对象包裹起来, 为了防止对象字面量被认为是函数体语句.
箭头函数的特性:
没有 this,super,arguments,new.target 绑定: this,super,arguments 以及内部函数的 new.target 的值由所在的最近的外部非箭头函数来决定;
没有 arguments 对象绑定, 但是能够访问包含它的外部函数的 arguments 对象;
- let outer = function(arg){
- return ()=>arguments[0];
- }
- let inner = outer(7);
- console.log(inner()) //7
不能使用 new 来调用: 箭头函数没有 [[Construct]] 方法, 因此不能被用为构造函数, 使用 new 调用函数会抛出错误;
没有原型: 没有使用 new, 因此没有 prototype 属性;
不能修改 this: 不能通过 call(),apply()以及 bind()方法修改 this;
不允许使用重复的具名参数: 箭头函数不允许拥有重复的具名参数, 无论是否在严格模式下; 而传统函数只有在严格模式下才禁止使用重复的具名参数;
7. 尾调用优化
尾调用是指在一个函数内最后的语句调用了另外一个函数, 这个函数会重新创建新的栈帧并置于调用栈之上, 如果调用次数过多, 会导致内存过大. 当满足以下条件时, 执行引擎会针对尾调用进行优化, 不再重新创建新的栈帧, 而是会复用当前栈帧:
尾调用不能引用当前栈帧中的变量;
进行尾调用的函数在尾调用返回结果后不能做额外任何操作;
尾调用的结果作为当前函数的返回值;
- // 尾递归优化
- 'use strict'
- function doSomething(){
- // 满足尾调用优化条件
- return doElse();
- }
- function doSomething(){
- let tmp = doElse();
- // 尾调用函数结果没有直接返回, 因此不满足
- // 尾调用优化条件
- return tmp;
- }
- function doSomething(){
- // 尾调用函数后有其他额外操作, 结果没有
- // 立即返回, 因此不满足尾调用优化条件
- return 1+doElse();
- }
由于闭包会持有外部函数的变量, 因此对闭包的尾调用优化很难处理, 但是, 在递归操作中, 可以利用到尾调用优化, 减少栈帧个数, 降低内存.
来源: http://www.jianshu.com/p/6bbaece161ed