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({}); //0
add({y: 1}); //1
//当add()时,则会报错
add(); //报错
//怎样才能在add()时不报错呢?
//这就须要用到两重默许值,即默许为函数传入一个对象
function add({x = 0, y = 0} = {}) {
console.log(x + y);
}
add({}); //0
add({y: 1}); //1
add(); //0
以下:
function add({name = "your name here", age = "your age here"} = {}) {
console.log(`${name}, ${age}`);
}
add(); //your name here, your age here
add({name: "Oliver"}); //Oliver, your age here
add({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); //2
var 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)); //10
console.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+3
function 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 7
a.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要领
//ES5
let a = [1,2,3,4,5,6,7];
console.log(Math.max.apply(null,a)); //7
//ES6
console.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)); //3
function 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)); //120
function log(n, total) {
if (n === 1) {
return total
};
return log(n - 1, n * total);
}
console.log(log(5, 1)); //120
8 ES7函数参数的尾逗号
ES7提案许可函数的末了一个参数有尾逗号。