js学习笔记4(函数)

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(函数))