ES6&函数扩展

2017-06-14  本文已影响0人  lijaha

ES6函数的扩展

1.函数默认值

定义:ES6允许为函数设定默认值,即直接写在参数定义的后面

示例
function print(x,y='world'){
console.log(x,y);
}
print('hello'); //hello world
在ES5中则需要判断传参是否存在,再进行默认值赋值

参数变量是默认声明的,使用let和const再次进行声明会报错,var声明则不会,因为let定义不允许在相同作用域内重复声明一个变量。

2.函数的length属性

定义:函数的length属性将返回没有指定默认值的参数个数

示例
//在ES5中获取函数的参数个数需要函数return
function count(x,y){
return arguments.length;
}
//ES6中新增函数length属性
(function (x,y){}).length; //2
function count(x,y = "world"){
}
console.log(count.length); //1,指定默认值不算进去

3.函数的name属性

定义:获取函数的名称

var hello = function(){};
hello.name; //hello
//Function构造函数返回的示例name为anonymous
(new Function()).name; //anonymous

4.rest参数

定义:rest参数的写法为(...变量名),用于获取函数多余的变量,可替代arguments,rest参数中的变量是一个数组,arguments是类数组对象需要使用Array.from()方法转换成真正数组。

示例
function add(...numbers){
let count = 0;
for (let i of numbers){
count += i;
}
return count;
}
//或者使用numbers.forEach(x => count += x);
rest参数变量是一个数组,数组特有的方法都可以使用。
rest参数后不能再有其他的参数,rest参数必须为最后一个参数,使用函数属性length获取参数个数,rest参数不计入。
示例
function add(a,...numbers,c){
} //error
(function(a,b,...numbers){}).length; //2,rest参数不算

5.扩展运算符...

定义: 扩展运算符的写法为(...),将一个数组转为用逗号分隔的参数序列

示例
console.log(1,...[2,3,4,5],6); //1,2,3,4,5,6

使用扩展运算符替代apply()方法

//ES5
Math.max.apply(null,[4,6,0]); //Math.max不能接受数组为参数
Math.max(4,6,0);
//ES6
Math.max(...[4,6,0]);

使用扩展运算符合并数组

//ES5
var arr1 = [1,2,3];
var arr2 = [4,5,6,];
var arr = arr1.concat(arr2);
//ES6
var array1 = Array.of(1,2,3);
var array2 = Array.of(4,5,6);
var array = [...array1,...array2];

使用扩展运算符可将具有Iterator接口的数据结构转换成真正的数组(ES6数组扩展篇章也有提及)

[...'hello']; //将字符串转化成数组
var NodeList = [...document.querySelectorAll('p')];//类似数组对象
var someSet = new Set([4,5,6]);
var array = [...someSet.keys()]; //Set数据结构
var someMap = new Map([['name','lijaha'],['age',20]]);
var array = [...someMap.keys()]; //Map数据结构

6.箭头函数

定义: ES6允许使用箭头( => )定义函数

示例
var hello = x => x;
//如果箭头函数不需要参数或者需要多个参数就用()圆括号代表参数部分
var hello = () => 1; //return 1
var hello = (x,y) => x+y;
//如果箭头函数的代码块语句多于一句,需要使用大括号{}括起来
var hello = (x,y) => {return x+y};
//如果箭头函数返回的是对象,则需要在大括号外加上小括号({})
var hello = id => ({id: id,name: 'world'});

箭头函数体内的this对象是定义时所在的对象,而不是使用时所在的对象,称为箭头函数的this指向固定化。因为箭头函数内部是没有自己的this(不能作为构造函数,也不能使用apply、call、bind等方法改变this指向),所以它的this是外层代码块的this。

箭头函数内部没有arguments对象,需使用rest参数代替

var hello = (...numbers) => {console.log(...numbers)};

尾调用优化

定义:指某个函数在最后一步调用另一个函数

function hello(){

return hi();

} //尾调用的结尾一定是return xx();

函数调用会在内存中形成一个“调用帧”,保存调用位置和内部变量等信息。如果函数A中调用用了函数B,则会在函数A调用帧的上方形成一个B的调用帧,函数B执行完毕后,函数A中的调用帧B才会结束。如果函数B内调用了函数C,就会形成一个C地调用帧,所有的调用帧就会形成一个‘调用栈’。

//尾调用是函数的最后一步操作,所以不需要保留外层函数的调用帧,外层函数的调用位置和内部变量都不会用到。
function hello(){
let m = 1;
let n = 2;
return g(m+n);
}
//等同于
function hello(){
return g(3);
}
//等同于
g(3);
//上面代码中,如果函数g不是尾调用则函数hello需要保留内部变量m、n以及调用位置等信息,由于函数hello调用函数g后,hello函数就结束了,则可以删除hello()的调用帧,只保留g()的调用帧。

这就叫做‘尾调用优化’,即只保留内层函数的调用帧,如果所有的函数都是尾调用,则可以做到每次执行时调用帧只有一项。

//只有不再用到外层函数的内部变量,内层函数的调用帧才能取代外层函数的调用帧,实现尾调用优化。
function hello(a){
let b = 1;
function hi(a){
return a + b; //使用了hello函数的内部变量b
}
return hi(a);
}

尾递归

函数自己调用自己则叫递归,函数在尾部调用自己则称为尾递归

//使用递归实现阶乘,递归过程会比较耗费内存,因为保存大量的调用帧,容易发生栈溢出。
function factorial(n){
if(n == 1) return 1;
return nfactorial(n-1);
}
//使用尾递归
function factorial(n,total=1){ //使用默认值
if(n == 1) return total;
return factorial(n-1,n
total);
} //每次只保存一个调用记录
一般的递归的复杂度为O(n),使用尾递归的复杂度为O(1)

需要开启严格模式,尾调用优化才会生效,一旦使用尾调用优化,函数的内部对象arguments和caller都会失效,因为整个外层函数调用帧会被删除掉,这两个对象也就不存在了。严格模式下,这两个对象也是不可用的。

function hello(){
"use strict";
hello.arguments; //error
hello.caller; //error
}

本文章参考阮一峰《ES6 标准入门》

上一篇下一篇

猜你喜欢

热点阅读