《ES6标准入门(第3版)》学习笔记19:chapter_7 函数的扩展之函数参数的默认值

这是该系列的第19篇笔记!
让学习“上瘾”,成为更好的自己!!!

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>函数参数的默认值</title>
</head>

<body>

    <script>
        // 1, 基本用法
        // (1)
        // ES6之前:不能直接为函数的参数指定默认值
        // function log(x, y) {
        //     // y = y || 'world';  // 缺点:参数y被赋值了,但是对应的布尔值时false,则该赋值不起作用
        //     if (typeof y === 'undefined') {
        //         y = 'world';
        //     }
        //     console.log(x, y);
        // }
        // log('hello');
        // log('hello', 'china');
        // log('hello', '');

        // ES6: 能直接为函数的参数指定默认值
        // function log(x, y = 'world') {
        //     console.log(x, y);
        // }
        // log('hello');
        // log('hello', 'china');
        // log('hello', '');

        // function Point(x = 0, y = 0) {
        //     this.x = x;
        //     this.y = y;
        // }
        // var p = new Point();
        // console.log(p);

        // ES6写法的好处:a, 简洁明了
        //               b, 阅读代码的人可以立即意识到哪些参数是可以省略的,不用查看函数体或文档
        //               c, 有利于将来代码优化,即使未来的版本彻底拿掉了这个参数,也不会导致以前的代码无法运行


        // (2) 参数变量时默认声明的,不用let or const再次声明
        // function foo(x = 5){
        // let x = 23;  // error
        // const x = 232;  //  error
        //     var x = 12;  // normal
        //     console.log(x);
        // }

        // (3) 使用参数默认值时,不能有同名参数
        // function foo(x, x, y = 1){  // error
        //     // ...
        // }

        // (4) 参数默认值不是传值的,而是每次都重新计算默认值表达式的值,即参数默认值是“惰性求值”的
        // let x = 12;
        // function foo(p = x + 1){
        //     console.log(p);
        // }
        // foo();  // 13

        // x = 100;
        // foo();
        // 【解释】参数p的默认值是"x + 1",每次调用函数foo都会重新计算"x + 1",而不是默认p为13



        // 2, 与解构赋值默认值结合使用
        // (1) 参数默认值可以与解构赋值的默认值结合起来使用
        // function foo({
        //     x,
        //     y = 5
        // }) { // 使用了对象的解构赋值默认值,而不是函数参数的默认值
        //     console.log(x, y);

        // }

        // foo({});
        // foo({x: 1});  // 1 5
        // foo({x:12, y: 23});  // 12 23
        // foo();  // error  --> 如果函数foo调用时参数不是对象,则变量x and y不会生成,从而报错

        // (2) 
        // function fetch(url, {body = '', method = 'GET', headers = {}}){  // 这种写法不能省略第二个参数,在传值时
        //     console.log(method, headers);
        // }

        // fetch('http://example.com' ,{});  // GET
        // fetch('http://example.com');  // 报错

        // 上面的这种写法不能省略第二个参数,如果结合函数参数的默认值,则可以省略第二个参数,这时,会出现“双重默认值”
        function fetch(url, {
            method = 'GET'
        } = {}) { // 这种写法可以省略第二个参数,在传值时
            console.log(method);
        }
        fetch('http://example.com'); // 不报错 
        // --> 函数fetch没有第二个参数时,函数参数的默认值就会生效,然后才是解构赋值的默认值生效,变量method取得默认值GET

        // (3) 比较下面的写法

        // way 1: 函数参数的默认值是空对象,但是设置了对象解构赋值的默认值
        function m1({
            x = 0,
            y = 0
        } = {}) {
            return [x, y];
        }


        // way 2: 函数参数的默认值是一个有具体属性的对象,但是没有设置对象解构赋值的默认值
        function m2({
            x,
            y
        } = {
            x: 0,
            y: 0
        }) {
            return [x, y];
        }

        // 函数没有参数的情况
        // console.log(m1());  // [0, 0]
        // console.log(m2());  // [0, 0]
        // // x and y have the value
        // console.log(m1({x: 3, y: 8}));  // [3, 8]
        // console.log(m2({x: 3, y: 8}));  // [3, 8]
        

        // x has the value but y doesn't have
        // console.log(m1({x: 3}));  // [3, 0]
        // console.log(m2({x: 3}));  // [3, undefined]

        // // x and y都无值的情况
        // console.log(m1({}));  // [0, 0]
        // console.log(m2({}));  // [undefined,  undefined]



        // 3, 参数默认值的位置 
        // 通常情况下,定义了默认值的参数应该是函数的尾参数
        // --> 因为这样比较容易看出到底省略哪些参数
        // 如果非尾部参数设置了默认值,实际上这个参数是无法省略的
        // (1)
        // example 1
        // function f(x = 1, y){
        //     return [x, y];
        // }
        // console.log(f());  // [1, undefined]
        // console.log(f(2));  // [2, undefined]
        // console.log(f(undefined, 1));  //[1, 1]


        // example 2
        function f(x, y = 5, z){  
            // 非尾部参数设置了默认值,这时,无法只省略该参数而不省略其后的参数,除非显式输入undefined
            return [x, y, z];
        }
        // console.log(f());  // [undefined, 5, undefined]
        // console.log(f(1));  // [1, 5, undefined]
        // console.log(f(1,undefined,2));  // [1, 5, 2]
        
        // (2) 如果传入undefined,将触发该参数等于默认值,null则没有这个效果
        // function foo(x = 5, y = 12){
        //     console.log(x, y);
        // }
        // foo(undefined, null);  // 5 null





        // 4, 函数的length属性
        // 指定了默认值后,函数length属性将返回“没有指定默认值”的参数的个数
        // (1) 指定了默认值后,length属性将失真
        // console.log((function(a){}).length); // 1
        // console.log((function(a = 5){}).length);  // 0
        // console.log((function(a, b, c = 23){}).length);  // 2 


        // 指定了默认值后,length属性将失真?
        // --> 因为length属性的函数是该函数预期传入的参数的个数,某个参数指定默认值后,预期传入的参数个数就不包括这个参数了
        //     同理,rest参数也不计入length属性
        // (2)
        // console.log((function(a, b, ...args){}).length);  // 2

        // (3) 如果设置了默认值的参数不是“尾参数”,那么length属性也不再计入后面的参数
        // console.log((function(a = 0, b, c){}).length);  // 0
        // console.log((function(a, b = 1, c){}).length);  // 1

        // 5, 作用域
        // 一旦设置了参数默认值,函数进行声明初始化时,参数会形成一个单独的作用域,等到初始化结束,这个作用域消失
        // 这种语法行为在不设置参数默认值时是可不会出现的
        // (1)
        // example 1
        // var x = 1;
        // function f(x, y = x){
        //     console.log(y);
        //     // console.log(y);

        // }
        // f();  // undefined --> 这里,参数y的默认值等于变量x,调用函数f时,参数形成了一个单独的作用域
              //               在这个作用域中,默认值变量x指向第一个参数x,而不是全局变量x,所以输出2
        // f(2);

        // example 2: 
        // let x = 1;
        // function f(y = x){
        //     let x = 2;
        //     console.log(y);

        // }

        // f(); // 1 --> 函数f调用时,参数"y = x"形成一个单独的作用域,在这个作用域中,变量x本身没有定义,所以指向外层的全局变量x
             // 函数调用时,函数体内部的局部变量x影响不到默认变量x

        // 如果此时全局变量不存在,就会报错
        // function f(y = x){
        //     let x = 2;
        //     console.log(y);

        // } 
        // f();  // 报错

        // 下边写法也会报错
        // var x = 1;
        // function foo(x = x){
        //     // ...

        // }
        // foo();  // 报错 --> "x = x"形成了一个单独的作用域,实际执行的是"let x = x",由于“暂时性死区”,执行这行代码会产生“定义”错误


        // (2) 如果参数的默认值是一个函数,该函数的作用域也遵守这个规则, like "example 2"
        // let foo = 'outer';
        // function bar(func = x => foo){
        //     // let foo = 'inner';
        //     console.log(func());

        // }
        // bar();  // outer 
       
       // (3) a more complicated example
       var x = 1;
       function foo(x, y = function(){x = 2;}){
        var x = 3;
        y();
        console.log(x);
       }
       foo();  // 3
       console.log(x);  // 1
       //【解释】
       // 函数foo的参数形成了一个单独的作用域,这个作用域中首先声明了变量x,然后声明变量y 
       // y的默认值是一个匿名函数,这个匿名函数内部的变量x指向同一个作用域的第一个参数x
       // 函数foo内部又声明了一个内部变量x,该变量与第一个参数x由于不是同一个作用域,所以,在执行完y后,内部变量x和外部全局变量x的值“不会变”


       // 【改变一下】
       var x = 1;
       function foo(x, y = function(){x = 2;}){
       x = 3;  // var去掉,此时,函数foo内部变量x就指向第一个参数x,与匿名函数内部的x是一致的
        y();
        console.log(x);
       }
       foo();  // 2
       console.log(x);  // 1



        // 6, 应用
        // (1)利用参数默认值可以指定一个参数不得省略,如果省略就抛出一个错误
        // function throwIfMissing(){
        //     throw new Error('Missing parameter');
        // }

        // function foo(mustBeProvided = throwIfMissing()){  // 参数的默认值不是在定义时执行,而是在运行是执行
        //     return mustBeProvided;
        // }

        // console.log(foo(12));  //  Missing parameter

        // var err = new Error('i was wrong!');
        // console.log(err);

        // (2)将参数的默认值设为undefined,表明这个参数是可以省略的
        // function foo(optional = undefined){
        //     // ......
        // }
    </script>
</body>

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值