博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ECMAScript6 新特性——“函数的扩展”
阅读量:6909 次
发布时间:2019-06-27

本文共 5882 字,大约阅读时间需要 19 分钟。

1 函数参数的默认值

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

function log(x = "message.",y = "duration infomation.") {    console.log(x, y);}log(); //message. duration infomation.log("hello world.", "connecting."); //hello world. connecting.

参数变量是默认声明的,所以不能用let和const再次声明

function add(x = 0, y = 0) {    let x; //报错    const y; //报错    console.log(x + y);}add();

与结构赋值默认值结合使用

function add({x = 0, y = 0}) {    console.log(x + y);}add({}); //0add({y: 1}); //1//当add()时,则会报错add(); //报错//如何才能在add()时不报错呢?//这就需要用到双重默认值,即默认为函数传入一个对象function add({x = 0, y = 0} = {}) {    console.log(x + y);}add({}); //0add({y: 1}); //1add(); //0

如下:

function add({name = "your name here", age = "your age here"} = {}) {    console.log(`${name}, ${age}`);}add(); //your name here, your age hereadd({name: "Oliver"}); //Oliver, your age hereadd({name: "Troy", age: 18}); //Troy, 18

函数的length属性

指定了默认值以后,函数的length属性,将返回没有指定默认值的参数个数。

function add(x,y = 0) {    console.log(x + y);}console.log(add.length); //1 函数预期传入1个参数,因为有一个参数已经有默认值了

作用域

如果参数默认值是一个变量,则该变量所处的作用域,与其他变量的作用域规则是一样的,即先是当前函数的作用域,然后才是全局作用域。

var x = 10;function log(x, y = x) { //x已经在内部生成,使用的将是内部的x    console.log(y);}log(2); //2var x = 10;function log(y = x) { //x不存在,使用的将是外部的x    console.log(y);}log(); //10

实际应用

省略参数抛出错误:

function throwIfMissing() {    throw new Error("Missing parameter.");}function foo(x = throwIfMissing()) {    return x;}console.log(foo(10)); //10console.log(foo()); //Uncaught Error: Missing parameter.

2 rest参数

rest参数(形式为“...变量名”),用于获取函数的多余参数,这样就不需要使用arguments对象了

function foo(...values) {    let sum = 0;    for(let val of values) sum += val;    console.log(sum);}foo(1,2,3);

注意,rest参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。

function foo(x, ...values) {    let sum = 0;    for(let val of values) sum += val;    console.log(x, sum);}foo(1,2,3); //x是1,sum是2+3function foo(x, ...values, y) {    let sum = 0;    for(let val of values) sum += val;    console.log(x, sum);}foo(1,2,3); //Rest parameter must be last formal parameter

3 扩展运算符

扩展运算符(spread)是三个点(...)。它好比rest参数的逆运算,将一个数组转为用逗号分隔的参数序列。

let a = [1,2,3,4,5,6,7];console.log(...a); //1 2 3 4 5 6 7a.push(...a);console.log(a); //[1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7]

该运算符主要用于函数的调用

let a = [1,2,3,4,5,6,7];function add(...args) {    let sum = 0;    for (let val of args) console.log(sum += val);    console.log(`done. final sum: ${sum}`);}add(...a); //依次传入a数列中的值//1//3//6//10//15//21//28//done. final sum: 28

替代数组的apply方法

扩展运算符可以展开数组,所以不再需要apply方法

//ES5let a = [1,2,3,4,5,6,7];console.log(Math.max.apply(null,a)); //7//ES6console.log(Math.max(...a)); //7

又比如push函数

let a = [];let num = [1,2,3,4,5,6,7];a.push(...num);console.log(a.toString()); //1,2,3,4,5,6

扩展运算符的应用

合并数组

let a = [];let a1 = [1,2,3];let a2 = [4,5];let a3 = [6,7,8,9,10];a = [...a1, ...a2, ...a3];console.log(a.toString()); //1,2,3,4,5,6,7,8,9,10

与解构赋值结合

let [...rest] = [1,2,3,4,5,6];console.log(rest); //[1,2,3,4,5,6]

函数的返回值

var dateFields = readDateFields(database);var d = new Date(...dateFields);

字符串

console.log([...'hello']); //["h", "e", "l", "l", "o"]

扩展运算符能够识别32位Unicode字符

可以正确返回字符串长度:

console.log([...'hello']); //["h", "e", "l", "l", "o"]function len(...args) {    for (let val of args) console.log([...val].length);}len("hello", "another one 哈哈"); //14

类似数组的对象

var nodeList = document.getElementsByTagName("p");var array = [...nodeList];

含有Iterator接口对象

只要具有Iterator接口的对象,都可以使用扩展运算符

Map,Set,Generator等

4 name属性

函数的name属性,返回该函数的函数名。

如果将一个匿名函数赋值给一个变量,ES5的name属性,会返回空字符串,而ES6的name属性会返回实际的函数名。

var x = function() {};console.log(x.name); //"" ES5返回空字符串,ES6返回x

将一个具名函数赋值给一个变量,则ES5和ES6的name属性都返回这个具名函数原本的名字。

var x = function y() {};console.log(x.name); //"y"

Function构造函数返回的函数实例,name属性的值为“anonymous”。

console.log((new Function).name); //anonymous

bind返回的函数,name属性值会加上“bound ”前缀。

function foo() {};foo.bind({}).name // "bound foo"(function(){}).bind({}).name // "bound "

5 箭头函数

ES6允许使用“箭头”(=>)定义函数。

let add = (x,y) => x + y;console.log(add(1,2)); //3function add(x,y) {    return x + y;}console.log(add(1,2)); //3

如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用return语句返回。

let add = (x, y) => {    console.log("done");    return x + y;};console.log(add(1, 2)); //3

如果箭头函数直接返回一个对象,必须在对象外面加上括号。

let add = (x, y) => ({ result: x + y });console.log(add(1, 2).result); //3

箭头函数可以与变量解构结合使用。

let add = ({ name, age }) => ({ result: `${name},${age}` });console.log(add({ name: "Oliver", age: 18 }).result); //Oliver,18

箭头函数的一个用处是简化回调函数

var arr = [1, 2, 3, 4, 5];arr.map(function(item, index, array) {    console.log(item);});arr.map((item, index, array) => { console.log(item); });//上面两种函数写法功能相同,下面的明显较为简洁

使用注意点

  • 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。

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

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

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

6 ES7函数绑定

ES7中函数绑定运算符(::)作用是代替call、apply、bind调用。

7 尾调用优化

尾调用

尾调用(Tail Call)就是指某个函数的最后一步是调用另一个函数:

function g() {    console.log("done.");}function j() {    console.log("almost done.");}function log(x) {    if (x > 0) {        return g();    }    return j();}log(10); //done.log(0); //almost done.

g()和j()的调用都属于尾调用,因为都是函数的最后一步

尾调用优化

函数调用会在内存形成一个“调用记录”,又称“调用帧”(call frame),保存调用位置和内部变量等信息。如果在函数A的内部调用函数B,那么在A的调用帧上方,还会形成一个B的调用帧。等到B运行结束,将结果返回到A,B的调用帧才会消失。如果函数B内部还调用函数C,那就还有一个C的调用帧,以此类推。所有的调用帧,就形成一个“调用栈”(call stack)。

尾调用由于是函数的最后一步操作,所以不需要保留外层函数的调用帧,因为调用位置、内部变量等信息都不会再用到了,只要直接用内层函数的调用帧,取代外层函数的调用帧就可以了。

注意,只有不再用到外层函数的内部变量,内层函数的调用帧才会取代外层函数的调用帧,否则就无法进行“尾调用优化”。


function addOne(a) {    var one = 1;    function inner(b) {        return b + one;    }    return inner(a);}

尾递归

递归非常耗费内存,因为需要同时保存成千上百个调用帧,很容易发生“栈溢出”错误。但对于尾递归来说,由于只存在一个调用帧,所以永远不会发生“栈溢出”错误。

所以确保最后只调用自身就可以了,做法是将所有参数传入要调用的函数中去:

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

8 ES7函数参数的尾逗号

ES7提案允许函数的最后一个参数有尾逗号。

转载地址:http://arfcl.baihongyu.com/

你可能感兴趣的文章
Java实现文件复制
查看>>
Spark修炼之道(基础篇)——Linux大数据开发基础:第三节:用户和组
查看>>
Linux下Power Management开发总结
查看>>
安装服务器安全狗教程
查看>>
使用CodePush实时更新 React Native 和 Cordova 应用
查看>>
jstree -- 使用JSON 数据组装成树
查看>>
VM 安装 linux Enterprise_R5_U4_Server_I386_DVD教程图解
查看>>
PHP怎么获取系统信息和服务器详细信息
查看>>
iOS开发如何学习前端(1)
查看>>
『科学计算』层次聚类实现
查看>>
selenium用jquery改变元素属性
查看>>
一个支付流程要考虑到哪些测试点?
查看>>
某书2018笔试题之薯券
查看>>
对request.getSession(false)的理解(附程序员常疏忽的一个漏洞)
查看>>
手机端仿ios的日期组件脚本一
查看>>
Appium做Android功能自动化测试
查看>>
reentrantlock用于替代synchronized
查看>>
Nginx 安装部署
查看>>
三目运算符详解
查看>>
HTML中button和input button的区别
查看>>