美文网首页
js学习笔记4(函数)

js学习笔记4(函数)

作者: 木小伍 | 来源:发表于2021-04-28 15:06 被阅读0次
    1.箭头函数

    ES6新增属性。箭头函数特别适合嵌入函数的场景。

        //只有一个参数,括号可以省略
        let double = x => {return 2 * x};
        let tripble = (x) => {return 2 * x};
    
        //没有参数需要括号
        let getRandom = () => {return Math.random();};
    
        //有多个参数需要括号
        let sum = (a, b) => {return a + b}
        /**
         * 箭头函数也可以不使用大括号,但这会改变函数的行为。如果不使用大括号
         * 那么箭头后面就只能有一行代码(赋值操作或者表达式),而且省略大括号
         * 会隐式返回这行代码的值。
         */
        let fun1 = (x) => {return 2 * x;}
        let fun2 = (x) => 2 * x; //等于上面的写法
        let fun3 =  (x) => return  2 * x ; //无效写法
    

    箭头函数虽然语法简介,但是很多场合不适用。箭头函数不能使用arguments、super、new.target,也不能用作构造函数。箭头函数也没有protptype属性。

    2.函数名

    因为函数名就是指向函数的指针,所以它们跟其他包含对象指针的变量具有相同的行为。
    在ES6中,所有的函数对象都会暴露一个只读的name属性,其中包含关于函数的信息。

        function sum(num1, num2) { return num1 + num2; }
        console.log(sum(1,2)); //3
        let anotherSum = sum;
        sum = null;
        console.log(anotherSum(1,2)); //3
        console.log(anotherSum.name) //sum 返回的是方法名
        anotherSum.name = '小马哥';
        console.log(anotherSum.name) //sum  name属性只读,不能修改
    
    3.理解参数

    ECMAScript函数即不关心传入的参数个数,也不关心这些参数的数据类型。定义函数时要接收2个参数,并不意味着调用的时候就要传入2个参数,可以传1个,3个甚至一个都不传。事实上,在使用function关键字定义(非箭头)函数时,可以在函数的内部访问arguments对象,从中获取传进来的每个参数值。arguments是一个类数组对象(不是Array的实例)

        function sayHi(name, message) {
            console.log(arguments.length);  //2
            return '小马哥对' + name + '说' + message;
        }
    
        function sayHello() {
            console.log(arguments.length);  //2
            return '小马哥对' + arguments[0] + '说' + arguments[1];
        }
    
        let sayHello2 = () => {
            return '小马哥对' + arguments[0] + '说' + arguments[1];
        }
        
        console.log(sayHi('韩梅梅', '今天加班'))       //小马哥对韩梅梅说今天加班
        console.log(sayHello('韩梅梅', '今天加班'))    //小马哥对韩梅梅说今天加班
        console.log(sayHello2('韩梅梅', '今天加班'))   //报错
    

    箭头函数中的参数:如果函数是使用箭头函数语法定义的,那么传给函数的参数不能使用arguments关键字访问,而只能通过定义的命名参数访问。

        function foo(){
            let sayHello = () => {
                console.log( '小马哥对' + arguments[0] + '说' + arguments[1]);   //小马哥对韩梅梅说今天加班
            }
            sayHello();
        }
        foo('李雷','你也加班');
    
    4.没有重载

    ECMAScript函数如果定义了同名函数,则后面定义的会覆盖先定义的。

    5.默认参数值

    在ECMAScript5.1及以前,实现默认参数的一种常用方式就是检测某个参数是否等于undefined。在ES6开始可以支持显示定义默认参数。

       function sayName(name = '小马哥') {
            console.log('myName==' + name);
        }
        sayName('孙红雷');  //myName==孙红雷
        sayName();  //myName==小马哥
    
    6.参数扩展与收集

    ES6新增了扩展操作符,使用它可以非常简洁地操作和组合数据。扩展操作符最有用的场景就是函数定义中的参数列表。

    • 6.1扩展参数:对于可迭代对象应用扩展操作符,并将其作为一个参数传入,可以将可迭代对象拆分,并将返回的每个值单独传入。
        let values = [1, 2, 3, 4]
        function getSum() {
            let sum = 0;
            for (let i = 0; i < arguments.length; i++) {
                sum += arguments[i];
            }
            return sum;
        }
        console.log(getSum.apply(null, values)); //10---es5写法
        console.log(getSum(-1,...values,5)); //14
        console.log(getSum(...values,5,6,7,8,9,10));//55
        console.log(getSum(...values,...[5,6,7,8,9,10]));//55
        console.log(getSum(5,...values,...[5,6,7,8,9,10]));//60
    
    • 6.2收集参数: 在构思函数定义时,可以使用扩展操作符把不同长度的独立参数组合为一个数组。类似于arguments对象的构造机制,只不过收集参数的结果会得到一个Array实例。
        let ignorFirst = (firstValue, ...values) => {
            console.log(values);
        }
        ignorFirst()   // []
        ignorFirst(1,2,3,4,5)   //[2, 3, 4, 5]
    
    7.函数内部(arguments,this,new.target)
    • arguments对象:这个对象只有以function关键字定义函数时候才会有,主要用于包含函数参数。arguments还有一个callee属性,是指向arguments对象所在函数的指针。
        function factorial(num) {
            if (num <= 1) {
                return 1;
            } else {
                return num * arguments.callee(num - 1);  //指向当前对象所在的函数
            }
        }
    
        let trueFactorial = factorial;
        factorial = function () {
            return 0;
        }
        console.log(trueFactorial(5))   //120
        console.log(factorial(5))   //0
    
    • this对象:在标注函数(指向调用该函数的上下文)和箭头函数中(指向定义该函数的上下文)有不同的行为。
      在标准函数中:this引用的是把函数当成方法调用的上下文对象,通常这个时候,称其为this值(在网页的全局上下文中调用函数时,this指向windows)。
     window.color = 'red';
        let o = {
            color: 'blue',
        }
        function sayColor() {
            console.log(this.color);
        }
        sayColor()  //red
        o.sayColor = sayColor;
        //调用的时候,函数调用者变更为o,this指向o
        o.sayColor();   //blue
    

    在箭头函数中:this引用的是定义箭头函数的上下文,在事件回调或者定时回调中调用某个函数,this指向的并非想要的对象。此时将回调函数写成箭头函数就可以解决这个问题。因为箭头函数中的this会保留定义该函数时的上下文。

        window.color = 'red';
        let o = {
            color: 'blue',
        }
        let sayColor = () => console.log(this.color);
        sayColor()  //red
        o.sayColor = sayColor;
        o.sayColor();   //red
    
    • caller属性:这个属性引用的是调用当前函数的函数,或者如果的在全局作用域中调用的则为null。
      function outer(){
           let  arr =[];
           for (let i=0;i<20;i++){
               arr[i] = i;
           }
           inner();
       }
       function inner(){
           let  arr =[];
           for (let i=0;i<20;i++){
               arr[i] = i;
           }
           console.log(inner.caller)  //outer的源代码
           console.log(arguments.callee.caller) //等价于上面
       }
       outer();
    
    • new.target属性(ES6新增): 检测函数是否使用new关键字调用的new.target属性。如果函数是正常调用的则new.target的值是undefined,反之将引用被调用的构造函数。
    8.函数属性与方法(length,prototype,apply(),call())
    • length:表示该函数方法的入参个数。
    • prototype:保存引用类型所有实例方法的地方,就意味着toString()、valueOf()等方法都保存在prototype上,进而由所有实例共享。在ES5中,prototype属性是不可枚举的,因此使用for-in循环不会返回这个属性。
    • apply():这个方法都会以指定的this值来调用函数,即会设置调用函数时函数体内的this对象的值。apply()接收两个参数:函数体内this的值和一个参数数组。第二个参数可以是Array的实例,但也可以是arguments对象。
    • call():方法作用和apply()一样,只是传参的形式不同,第一个参数是this,后面的参数必须一个一个列出来。
       function sum(num1,num2){
            return num1 + num2;
        }
        function sum2(num1,num2){
            return sum.apply(this,arguments)
        }
        function sum3(num1,num2){
            return sum.call(this,num1,num2);
        }
        console.log(sum2(20,30)); //50
        console.log(sum3(20,30)); //50
    

    apply()和call(),更重要的作用是控制函数调用上下文,即函数体内this值的能力。

    window.color = 'red';
        let o = {
            color: 'blue'
        };
        function sayColor() {
            console.log(this.color);
        }
        sayColor();    //red
        sayColor.call(this);    //red
        sayColor.apply(window);    //red    
        sayColor.apply(o);    //blue
        sayColor.call(o);    //blue
    
    9.闭包

    匿名函数经常被人误以为是闭包。闭包指的是那些引用了另一个函数作用域变量的函数,通常是在嵌套函数中实现的。

        function compare(propertyName) {
            return function (object1, object2) {
                //内部函数(匿名函数)引用了外部函数的变量propertyName
                let value1 = object1[propertyName];
                let value2 = object2[propertyName];
                return value1 === value2 ? 0 : value1 > value2 ? 1 : -1;
            }
        }
    

    在这个内部函数被返回并在其他地方被使用后,它仍然引用着哪个变量。因为这是因为内部函数的作用域链包含compare()函数的作用域。

    10.this 对象

    在闭包中使用this会让代码变复杂。如果内部函数没有使用箭头函数定义,则this对象会在运行时绑定到执行函数的上下文。如果在全局函数中调用,非严格模式下,this等于window。

      window.identity = 'The Window';
        let object = {
            identity: 'My Object',
            getIdentityFunc() {
                return function () {
                    return this.identity;
                }
            }
        }
        console.log(object.getIdentityFunc()())     //The Window
    

    object.getIdentityFunc()返回函数,所以object.getIdentityFunc()()立即调用返回的函数,从而得到一个字符串。每个函数在被调用的时候都会创建两个特殊的变量:this和arguments。内部函数用于不可能直接(可以间接,箭头函数或者使用变量)访问外部函数的这两个变量。

    11.立即调用的函数表达式

    立即调用的函数表达式又称为立即嗲用的函数表达式(IIFE)。类似于函数声明,但是由于被包含在括号中,所以会被解释为函数表达式。紧跟在第一组括号后面的第二组括号会立即调用前面的函数表达式。在ES5.1及以前,为了防止变量定义外泄,IIFE是个非常有效的方式。也不会导致闭包相关的内存问题,因为不存在对这个函数的引用,为此,只要函数执行完毕,其作作用域链就可以被销毁。

        (function () {
            //块级作用域
        })();
    

    相关文章

      网友评论

          本文标题:js学习笔记4(函数)

          本文链接:https://www.haomeiwen.com/subject/zhosrltx.html