在ES6以前是不能为函数的参数指定默认值的,要想实现默认值只能经过判断赋值的方式来实现,在ES6中容许函数为参数设置默认值,主要是为了提升代码的可阅读性,有利于代码的优化。另外注意的是在参数赋值的时候,该参数不能重复使用,不能使用let const 进行定义。前端
// ES6 以前实现
function log(x, y) {
y = y || 'World';
if (typeof y === 'undefined') {
y = 'World';
}
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello World
// ES6 中实现
function log(x, y = 'World') {
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello
function Point(x = 0, y = 0) {
this.x = x;
this.y = y;
}
const p = new Point();
p // { x: 0, y: 0 }
function foo(x = 5,x) {
let x = 1; // 报错,不能同名参数,不能对参数进行let const 定义
const x = 2;
}
复制代码
若是函数在调用的时候没有提供参数,内部变量就不会产生,就会产生错误,经过提供函数的默认值能够解决这种问题,以下:编程
function foo({x, y = 5}) {
console.log(x, y);
}
foo() // 报错
foo({x:1}) // 1 5
foo({x:2,y:3) // 2 3
foo({}) // undefined 5
function foo ({x,y = 5} = {}){
console.log(x,y)
}
foo() // undefined 5
这样就是若是没有在调用的时候传值 就默认赋空对象。
复制代码
以下例子:segmentfault
function post(url, {b = '',type='get',h={}}){
console.log(type)
}
post('w.b.c',{}) // get
post('w.b.c') // 报错
// 改为这样就能够了
function post(url, {b = '',type='get',h={}} = {}){
console.log(type)
}
post('w.b.c',{}) // get
post('w.b.c') // get
复制代码
下面例子的区别数组
// 写法一
function m1({x = 0, y = 0} = {}) {
return [x, y];
}
// 写法二
function m2({x, y} = { x: 0, y: 0 }) {
return [x, y];
}
两个都是有默认值在调用的时候都传值或者都不传值的时候状况是同样的。
可是若是传空值,或者不传值的状况会有差别以下:
m1({}) // 由于自己有默认值 因此为 [0,0]
m2({}) // 默认值为空 解构赋值没有传值 因此 [undefined,undefined]
// 其余状况同上
m1({x: 3}) // [3, 0]
m2({x: 3}) // [3, undefined]
m1({z: 3}) // [0, 0]
m2({z: 3}) // [undefined, undefined]
复制代码
若是定义了默认值的参数,应该是函数的尾参数。并且这个参数是没法省略的,除非输入undefinedapp
函数参数指定了默认值以后,函数的length属性将会减去指定了默认值的参数个数。由于该属性认为,指定了默认值的参数将不包含在预期参数个数中。以下:ide
(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2
复制代码
若是函数中的参数设置了默认值,那么函数在声明初始化的时候,参数会造成一个单独的做用域,初始化完成后这个做用域就会消失,这种状况只在参数设置了默认值的状况下。以下:函数式编程
var x = 1;
function f(x, y = x) {
console.log(y);
}
f(2) // 2
// 由于 设置了默认值 因此在调用 f 的时候就造成了做用域,这时候由于将x赋值给y
传入的x 为 2 因此y是2,若是这时候 调用的时候不传值,
那么x将指向全局,因此y = 1
复制代码
利用参数默认值,能够指定某一个参数不得省略,若是省略就报错,以下函数
function throwIfMissing() {
throw new Error('Missing parameter');
}
function foo(mustBeProvided = throwIfMissing()) {
return mustBeProvided;
}
foo()
// Error: Missing parameter
foo(2) // 2
复制代码
ES6 中 增长了 rest 参数(...变量名),用于获取函数多余的参数,rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中。post
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(2, 5, 3) // 10
// 注意:rest 参数以后不能再有其余参数,另外rest参数也不计算在
函数的length属性中。
复制代码
ES6 中,若是函数参数使用了默认值,解构赋值,或者扩展运算符,那么函数内部将不能显式设定为严格模式,不然会报错。由于函数执行的时候 先执行函数参数,在执行函数体,可是由于只有在函数体中才能知道参数是否以严格模式执行,可是参数却应该先于函数执行。有两种方法能够规避:1、 设置全局严格模式,2、把函数包在一个无参数的当即执行函数里面。优化
返回函数的函数名,以下:
function foo(){}
foo.name // foo
var f = function(){}
// ES5
f.name // ''
// ES6
f.name // f
var f = function c(){}
f.name // c
复制代码
ES6 容许使用 “箭头” (=>)定义函数
var f = v => v;
// 等同于
var f = function (v) {
return v;
};
var f = () => 5;
// 等同于
var f = function () { return 5 };
var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
return num1 + num2;
};
// 若是箭头函数后面的语句较多就要用大括号包裹起来 并return返回
var sum = (num1, num2) => { return num1 + num2;
//rest 参数与箭头函数结合的例子。
const numbers = (...nums) => nums;
numbers(1, 2, 3, 4, 5)
// [1,2,3,4,5]
const headAndTail = (head, ...tail) => [head, tail];
headAndTail(1, 2, 3, 4, 5)
// [1,[2,3,4,5]]
复制代码
1. 函数体内的this对象,就是在定义时所在的对象,而不是使用时所在的对象。
2. 不能够看成构造函数,也就是说,不可使用new命令,不然会抛出一个错误。
3. 不可使用arguments对象,该对象在函数体内不存在。若是要用,能够用 rest 参数代替。
4. 不可使用yield命令,所以箭头函数不能用做 Generator 函数。
5. 因为箭头函数没有本身的this,因此固然也就不能用call()、apply()、bind()这些方法去改变this的指向。
复制代码
1. 定义对象的方法,且该方法内部包括this
2. 动态定义this 的场合,如点击事件中this 的指向
复制代码
箭头函数内部能够在嵌套使用箭头函数。
函数式编程的一个重要概念,指某个函数的最后一步是调用另外一个函数
function f(x){
return g(x);
}
// 一下都不属于
// 状况一
function f(x){
let y = g(x);
return y;
}
// 状况二
function f(x){
return g(x) + 1;
}
// 状况三
function f(x){
g(x);
}
复制代码
只保留内层函数的调用帧。若是全部函数都是尾调用,那么彻底能够作到每次执行时,调用帧只有一项,这将大大节省内存。这就是“尾调用优化”的意义。
function f() {
let m = 1;
let n = 2;
return g(m + n);
}
f();
// 等同于
function f() {
return g(3);
}
f();
// 等同于
g(3);
复制代码
注意,只有再也不用到外层函数的内部变量,内层函数的调用帧才会取代外层函数的调用帧,不然就没法进行“尾调用优化”。
函数调用自身,称为递归。若是尾调用自身,就称为尾递归。
在正常模式下,可使用减小调用栈,采用循环换掉递归的方法
欢迎关注 公众号【小夭同窗】
ES6入门系列
Git教程