给函数参数赋默认值
    function log(x,y="hello") {
        console.log(x,y);
    }
    log("hai~","大妹纸");
    log('hi');
    log('hi',"");
    log();

    function Point(x,y) {
        this.x = x;
        this.y = y;
    }
    const p = new Point(10,10);
    console.log(p);
参数变量是默认声明的,因此不能用let const再次声明
    function foo(x = 5) {
        console.log(x);
        var x = 1;
        console.log(x);
       // let x = 2;//Identifier 'x' has already been declared
    }
    foo();
使用参数默认值时,函数不能有同名参数
         // 不报错
        function foo1(x, x, y) {
            // ...
        }

        // 报错
       function foo2(x, x, y = 1) {
            ...
       }
        // SyntaxError: Duplicate parameter name not allowed in this context
参数默认值是惰性求值的
         let x = 99;
        function foo(p = x + 1) {
            console.log(p);
        }

        foo()

        x = 100;
        foo()
参数默认值与解构赋值默认值联合使用
        function foo({x,y=5}) {
            console.log(x,y)
        }
        foo({});
        foo({x:2});
        // foo();
函数参数为空时 设置默认值为空对象 对象属性为空时 默认值为设定值
        function foo1({x,y=5}={}) {
            console.log(x,y)
        }
        foo1({});
        foo1({x:2});
        foo1();
下面两个方法的不同之处
        function m1({x = 0,y=0}={}) {
            console.log(x,y)
        }
        function m2({x,y}={x:0,y:0}) {
            console.log(x,y)
        }
        m1();
        m2();
        m1({x:1});
        m2({x:1})

    //    m1方法设置参数默认值为空对象 且利用了对象的解构赋值
    //    m2方法仅设置了参数默认值为一个固定对象 未设置对象的解构赋值
定义默认值的参数应该是函数的尾参数, 如果非尾部的参数设置默认值,实际必须传入undefined 否则会报错
        function f1(x=1,y) {
            console.log(x,y);
        }
        // f1(undefined,2);
        function f2(x,y,z=2) {
            console.log(x,y,z);
        }
        f2(2,3);
函数的length属性在设置默认参数的情况下 返回为指定默认值的参数个数  length属性的含义:该函数预期传入的参数个数
        console.log((function (a) {}).length )
        console.log((function (a = 5) {}).length )
rest参数不会计入length属性,如果指定默认值的参数不是尾参数,那么length属性不再计入其后的参数个数
        console.log((function (a = 0, b, c) {}).length);
        console.log((function (a, b = 1, c) {}).length);
  设置了函数默认值,进行函数初始化时会形成一个单独的作用域,初始化结束后,该作用域会消失  不设置函数参数默认值时不存在该行为
        let x = 1;
        function f(x,y=x) {
            console.log(y)
        }
        f(2);
        function f3(y=x) {
            let x = 3;
            console.log(y);
        }
        f3();

        function f4(x,y=function (){x = 2}) {
            var x = 3;
            y();
            console.log(x);
        }
        f4();
        console.log(x)
        function f5(x,y=function (){x = 2}) {
            x = 3;
            console.log(x);
            y();
            console.log(x);
        }
        f5();
rest参数用于获取函数多余参数 代替arguments对象 rest参数是真正数组 可直接使用数组方法 arguments对象是类数组 需使用Array.prototype.slice.call()方法转换为真正的数组
        function add(...values) {
            var sum = 0;
            values.forEach((item)=>{
                sum += item;
            })
            return sum;
        }
        console.log(add(1,2,3));
上面的方法用arguments变量写的话
        function add() {
            let args = Array.prototype.slice.call(arguments);
            let sum = 0
            args.forEach((item)=>{
                sum += item;
            })
            return sum;
        }
        console.log(add(1,2,3))
注:rest参数之后不能再有其他函数参数,否则会报错   且函数的length属性不包括rest参数
        // 报错
        function f(a, ...b, c) {
            ...
        }
es5开始函数内部可以设定为严格模式 函数体第一行 ‘use strict’
        function f6() {
            "use strict";
        }
es6 规定 使用了默认参数、解构赋值、扩展运算符,函数内部就不能设置为严格模式,否则会报错 Illegal 'use strict' directive in function with non-simple parameter list
        function f7(x,y=0) {
            // "use strict";
        }
因为函数的严格模式适用于函数参数和函数体 执行顺序是先执行函数参数再执行函数体,但是否使用严格模式是在函数体中决定的,因此产生矛盾  规避方法如下
设置全局性的严格模式
        "use strict"
        function f8() {

        }
把函数放在无参数的立即执行函数中
        (function () {
            "use strict";
            function f9() {

            }
        })()
函数的name属性,返回函数的名字 对某些特殊情况es5和es6返回值不一样   
匿名函数的name值 es5返回空字符串“”  es6返回匿名函数赋值的变量名
         let a = function () {

        }
        console.log(a.name);
具名函数赋值给变量后 函数的name属性返回函数本来的名字
        let b = function c() {

        }
        console.log(b.name)
Function构造函数实例 name值为anonymous
console.log(new Function().name)
bind返回的函数 name值为加入bound前缀
        function a() {

        }
        console.log(a.bind(this).name)
箭头函数 参数大于1个或者没有参数时箭头前用圆括号  函数体语句大于1条时用{}包裹 并使用return语句
        let a = (num1,num2) => num1+num2;
        console.log(a(11,22))
如果箭头函数返回的是一个对象 必须在对象外加() 否则会被解释为代码块标识
        let a = (a,b) => ({a:a,b:b});
        console.log(a(10,20));
箭头函数与结构赋值联合使用
        const full = ({first,last})=> first+","+last
        console.log(full({first:"再见",last:"你好"}));
常用方法示例
        const isEven = num => num%2===0 ;
        const squre = num => num*num;
        [1,2,3].map(item=>item*item);
        [3,5,2,4,1].sort((a,b)=> a-b);
        const arr = (...nums) => nums;
        const headAndTail = (head,...tail) => [head,tail];
箭头函数注意点:
1.函数内部的this指向函数定义生效时所在的对象箭头函数内部没有this 不能用call()bind()apply()改变this的指向                2.不可以做构造函数 不能使用new命令
3.不能使用arguments对象 可用rest参数代替
4.不能使用yield 命令 不能做Generator函数
        function abc() {
            setTimeout(()=>{console.log(this.id)},100)
        }
        var id = 21;
        abc();
        abc.apply({id:42});

        function Timer() {
            this.s1 = 0;
            this.s2 = 0;
            setInterval(()=>this.s1 ++,1000);
            setInterval(function () {
                this.s2 ++;
            },1000)
        }
        let timer = new Timer();
        setTimeout(() => console.log(timer.s1),3001);
        setTimeout(() => console.log(timer.s2),3001);
双冒号运算符  将运算符右边的方法绑定到运算符左边的对象上 如果::运算结果是一个对象,可采用链式写法
        foo::bar1;
        foo::bar2(...argument);

        ::obj.foo
尾调用是指 函数的最后一步是调用某个方法
        function f(x) {
            if (x > 0) {
                return m(x)
            }
            return n(x);
        }
尾调用优化:用内层函数的调用帧取代外层函数的调用帧 ,只保留内层函数的调用帧,节省内存空间   只有不在使用外层函数的内部变量,才能进行尾调用优化
递归是指函数调用自身  尾递归是指函数的最后一步调用自身方法
        //    阶乘函数
        function factorial(n) {
            if(n ===1)return 1;
            return n*factorial(n-1);
        }
        console.log(factorial(3));
        //改写成尾递归 只保留一个调用记录
        function factorial1(n,total) {
            if(n ===1) return total;
            return factorial1(n-1,n*total)
        }
改写递归函数 将函数内部用到的变量改写成函数参数 尾调用优化和尾递归优化都只有在严格模式下才生效
01-16 13:27