1)函数参数的默认值
ES6 之前,不能直接为函数的参数指定默认值。只能采用变通的方法,即在函数体内部通过判断来赋默认值。
ES6 允许为函数的参数设置默认值(可以是值,也可以是表达式),即直接写在参数定义的后面。
function foo (x = 0, y = 0){}
注意:
(1)使用参数默认值时,函数不能有同名参数。
function foo (x, x, y = 1){} //报错
(2)参数默认值不是传值的,而是每次调用函数时重新计算默认值。
let x = 1;
function foo (y = x + 1){
console.log(y);
}
foo(); // y => 2
x = 2;
foo(); // y => 3;
(3)参数默认值可以与解构赋值的默认值,结合起来使用。详见解构赋值。
(4)通常情况下,定义了默认的参数,应该是函数的尾参数。因为这样比较容易看出来,到底省略了那些参数。如果非尾部的参数设置默认值,实际上这个参数是没法省略的。
function foo (x = 1, y) {}
foo(); // x => 1, y => undefined
foo(2); // x => 2, y => undefined
foo(,2); // 报错
foo(null,2); // x => null, y => 2
foo(undefined,2); // x => 1, y => 2
(5)指定了默认值后,函数的 length
属性,将返回没有指定默认值的参数个数,也就是说,指定了默认值后,length
属性将失真。 length
属性忽略默认值后面的参数以及 rest 参数。
(function (a) {}).length // => 1
(function (a = 5){}).length // => 0
(function (a,b,c = 5){}).length // => 2
(function (a = 5, b, c){}).length // => 0
(function (a, b = 5, c){}).length // => 1
(function (...args){}).length // => 0
(6)一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域。默认值表达式计算时变量遵循作用域链。
let x = 1;
function f(y = x) { let x = 2;}
f(); // y => 1;
上面代码中,参数 y = x
形成了一个单独的作用域。这个作用域里面,变量 x
本身没有定义,所以指向外层的全局变量 x
。函数调用时,函数体内部的局部变量 x
影响不到默认变量x
。若外层未定义 x
则报错。
let x = 1;
function f(x = x) {}
f(); // 报错,参数 x = x 为一个单独作用域,
//实际执行 let x = x ,由于暂时性死区原因,报错: x 未定义。
2)rest参数
ES6引入 rest 参数(形式为 ...变量名
),用于获取函数的多余参数,这样就不需要使用 arguments
对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。
function f(...values){}
f(1,2,3);
rest 参数中的变量代表一个数组,所以数组特有的方法都可以用于这个变量。
注意:rest 参数后面不能再有其他参数(即 rest 参数为最后一个参数),否则会报错。
函数的 length
属性,不包括 rest 参数。
3)严格模式
ES2016 规定只要函数参数使用了默认值、解构赋值、或者扩展运算,那么函数内部就不能显式设定为严格模式(use strict
),否则会报错。
4)name属性
函数的 name
属性,返回该函数的函数名。 Function
构造函数返回的函数实例, name
属性的值为 anonymous
。bind
返回的函数, name
属性会加上 bound
前缀。
function foo(){} foo.name => "foo"
var f = function (){} f.name => "f"
var f = function baz(){} f.name => "baz"
(new Function).name => "anonymous"
function foo(){}
foo.bind({}).name => "bound foo"
5)箭头函数
ES6 允许使用“箭头”(=>)定义函数。如果箭头函数不需要参数或者需要多个参数,使用圆括号代表参数部分。如果箭头函数的代码块部分多于一条语句,就要使用大括号将它们括起来,并且使用 return
语句返回。
var f = v => v;
var f = () => s;
var f = (num1,num2) => num1 + num2;
var f = (num1,num2) => {return num1 + num2;}
注意:
(1)由于大括号被解释为代码块,所以如果箭头函数直接返回一个对象,必须在对象外面加上括号。
var get = id => ({id : id, name : 'get'});
(2)箭头函数体内的 this
对象,就是定义使所在的对象,而不是使用时所在的对象。this
指向的固定化,并不是箭头函数内部有绑定 this
的机制,实际原因是箭头函数根本没有自己的 this
对象,导致内部的 this
就是外层代码块的 this
。由于箭头函数没有自己的 this
,所以也就不能用 call
、 apply
、 bind
这些方法去改变 this
的指向。
function foo() {
return () => {
return () => {
return () => {
console.log('id:', this.id); // this 指向最外层的foo函数的对象{id: 1}
};
};
};
}
var f = foo.call({id: 1}); // 给foo指定对象{id: 1}
// 以下如何给 f 指定对象,箭头函数内部 this 对象不会改变。
// 因为 f 是一个箭头函数,该函数的对象指向 foo 。
var t1 = f.call({id: 2})()(); // id: 1
var t2 = f().call({id: 3})(); // id: 1
var t3 = f()().call({id: 4}); // id: 1
---------------------------------------------------------------------------
(function() {
return [
(() => this.x).bind({ x: 'inner' })() // bind无效
];
}).call({ x: 'outer' }); // ['outer']
(3)不可以当做构造函数,也就是说,不可以使用 new
命令,否则会抛出一个错误。,这是因为箭头函数没有 this
,所以也就不能用作构造函数。
(4)除了 this
,以下三个变量在箭头函数中也是不存在的,arguments
、 supper
、 new.target
,它们都指向外层函数的对应变量。
function foo(){
setTime(() => {
console.log("args:", arguments);
},100);
}
foo(2,3,4,5); // args:[2,3,4,5]
(5)不可以使用 yield
命令,因此函数不能用作 Generator 函数。
(6)箭头函数内部,还可以再使用箭头函数,即嵌套的箭头函数。