ECMAScript 6(12)函数的扩展(1)——函数参数的默认值之详细解析

0、一句话总结

  1. 函数参数有默认值的情况下,先取赋值,无则取默认值;

1、函数参数

1.1、参数的默认值

1.1.1、是什么?

function [name]([param1[ = defaultValue1 ][, …, paramN[ = defaultValueN ]]]) { statements }3

简单来说,es6让js也可以像c++一样,给函数参数一个默认值了(原本默认值可以认为是undefined)。

具体解释的话:

  1. 假如函数有参数a;
  2. 该参数a有默认值;
  3. 调用该函数;
    3.1. 给a赋值了,a = 赋值的那个值;
    3.2. 没有给a赋值,a = 默认值;
function test(a = 1) {
    console.log(a);
}
test(); //1,取默认值
test(10);   //10,取传给他的值
1.1.2、自由给想要默认值的参数赋值

另外以下这种写法是可以的,即随意指定给某几个参数赋值(但是在c++里是不行的)

function test(a = 1, b) {
    console.log(a, b);
}
1.1.3、前置参数对后置可见,相反则不行

前置参数对后置参数是可见的:

···
function test(a = 1, b = a) {
console.log(a, b);
}
test(5); //5 5,a被赋值5,然后b=a也等于5
test(10, 5); //10 5,a被赋值10,b因为有赋值5,所以等于5而非等于a
···

但相反不行,并且可能出错

function test(a = b, b = 1) {
    console.log(a, b);
}
test(5); //5 1,a被赋值5,所以a=b不生效,b无赋值所以等于默认值
test(10, 5);   //10 5,分别被赋值10和5
test(); //Uncaught ReferenceError: b is not defined,后置参数对前置不可见
1.1.4、传undefined时,取默认值

当赋值undefined时,取默认值

function test(a = 1) {
    console.log(a);
}
test(undefined);    //1
1.1.5、函数参数不能用用let或const再次声明

不管参数有没有默认值,都不能用let或const再次声明:

function test(a = 1) {
    let a = 10;
}
test();    //Uncaught SyntaxError: Identifier 'a' has already been declared
1.1.6、使用变量时,每次求值使用实时的值

假如函数参数使用函数外的变量,那么每次需要取该参数的默认值时,会去查看该变量的值,再拿来使用,而不是取函数声明时的变量的值;

var m = 1;
function test(a = m) {
    console.log(a);
}
test(); //1,查看m,值是1,所以赋值给a,所以a是1
m++;
test(); //2,查看m,此时m已经是2,所以把2赋值给a,所以a是2
1.1.7、可以使用arguments或this等作为默认值

参数的默认值还可以使用例如arguments,this之类的值。如代码:

function test(a = arguments, b = this) {
    console.log(a, b);
}
test(undefined, undefined);
//第一个参数是一个数组,它是arguments(表示所有参数),就像我们直接在函数里面获取这个值一样;
//第二个参数是this,因为直接调用函数,所以this指向window
1.1.8、注意按引用传递的问题

如果默认值是一个全新对象的话,那么相当于创建了一个新对象赋值给他。
如果是一个变量(该变量指向某个对象),那么是按引用传递。
使用的时候需要略微小心,如示例代码:

function test(a = {}) {
    return a;
}
test() === test();  //false

var m = {};
function test(a = m) {
    return a;
}
test() === test();  //true
1.1.8、默认值与解构赋值

如果忘记解构赋值的同学,请回去复习一下。

简单来说,解构赋值可以将一个对象或数组通过赋值的形式给另外一个对象或数组,从而让对象或数组内的变量,同时多个赋值。

关键是他可以给变量赋值,而函数的参数就是变量,并且需要赋值。因此解构赋值可以和传参一起用。

如代码:

function test({a, b}) {
    console.log(a, b);
}
test({a: 1, b: 2}); //1 2

然而,解构赋值是可以赋默认值的。例如:

let {a, b = 1} = {a: 10};
console.log(a, b);  //10 1

因此当函数参数使用解构赋值时,也可以使用默认值

function test({a, b = 10}) {
    console.log(a, b);
}
test({a: 1}); //1 10

甚至可以参数使用默认值,而默认值的赋值方式使用解构赋值,如代码:

//参数使用默认值进行解构赋值
function test({a = 10} = {}) {
    console.log(a);
}
//不传参
test(); //10
//传参,则会使用参数替代默认值,即变形为{a = 10} = {a:1}
test({a: 1}); //1

另外不建议使用以下这种方式来写,原因是这种写法,当传的属性没有a时,a的值就是undefined了

//参数使用默认值进行解构赋值
function test({a} = {a: 10}) {
    console.log(a);
}
test({b: 10}); //undefined,相当于{a} = {b: 10}
1.1.9、函数的length属性

函数的length属性指什么呢?指的是函数的参数的个数,如下:

console.log((function () {}).length);    //0
console.log((function (a) {}).length);    //1
console.log((function (a, b) {}).length);    //2

而当函数使用默认值之后,length属性则指的是 从第一个参数开始算起,直到遇见第一个有默认的值的参数,在此过程中没有默认值的参数 的个数,如代码:

console.log((function (a = 1, b) {}).length);    //0,第一个就是有默认值,于是结束,是0
console.log((function (a, b = 1) {}).length);    //1,    //第一个没,第二个有,于是1
console.log((function (a = 1, b = 1) {}).length);    //0,第一个有,结束,是0

因为这种规则,因此类似还有

//这里的rest是一个数组,在这里他表示剩下的所有参数
console.log((function (...rest) {}).length);    //0

//为了避免有人忘了这个怎么用,放一段代码做提示
let [head, ...tail] = [1, 2, 3, 4];
head // 1
tail // [2, 3, 4]
1.1.10、函数的作用域

首先,函数的作用域有三个:

  1. 函数外部的作用域;
  2. 函数参数的作用域(只有参数有默认值的时候才会出现,没有默认值就算出现也没有意义);
  3. 函数内部作用域;

函数参数可以访问到外部作用域;
函数内部可以访问到参数的作用域;
但函数参数不能访问到函数内部作用域;

这里给一个比较典型的情况:

var x = 1;
function foo(x, y = function () {
                 x = 2
             }) {
    //这里赋值x=3
    var x = 3;
    // 这里x=2,但这里实际改的是函数参数作用域里面的x的值是2
    // 但由于在函数内部作用域里又声明了一个x,因此2个x是不同的
    y();
    console.log(x);
}
foo();

因此,当函数参数的默认值,在函数参数作用域找不到同名变量时,他就会去函数外部的作用域去找同名的变量,但绝对不会去函数内部的作用域去找;

当然,如果外部没有同名变量就会报错undefined。

var z = 1;
function test(x = z) {
    console.log(x);
}
test(); //1
function test(x = y) {
    console.log(x);
}
test(); //y is not defined

另外,从种种现象来看,函数参数的作用域里,声明变量的形式应该是let,理由如下:

  1. 不存在变量提升,也就是说,先使用再声明是报错,而不是值为undefined
//1、用var,变量提升
var b = a;
var a = 1;
console.log(b); //undefined

//2、用let,报错
var b = a;  //Uncaught ReferenceError: a is not defined
let a = 1;

//3、函数参数有默认值时
function test(x = y, y = 1) {}
test(); //Uncaught ReferenceError: y is not defined

说明函数参数作用域中,声明变量的方式类似let。

1.1.11、函数参数默认值的使用要点

一般来说,建议将带默认值的函数参数放最后,不需要带默认值的函数参数放前面,这样在使用起来比较顺手。

例如:

function test(x, y = 1) {
    console.log(x, y);
}
test(1);    //1 1

function anotherTest(y = 1, x) {
    console.log(x, y);
}
anotherTest(undefined, 1); //1 1

毫无疑问,第二种写法要多写一个undefined以使用参数的默认值

1.1.12、立即执行函数

函数的默认值可以是一个立即执行的函数,或者调用其他的函数,例如代码:

function test(x = (function () {
                  alert("缺少参数x")
              })()) {
    console.log(x);
}
test();    //undefined,会发起alert
test(1);    //1,不会发起alert

在以上代码中,参数x的默认值是一个立即执行的函数。

当没有给x赋值时,该立即执行函数在赋值时,会执行,弹出alert窗口;
当给x赋值时,该立即执行函数不会执行,x的值是1;

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值