JS 核心之函数

作者: 养乐多__ | 来源:发表于2019-04-14 23:32 被阅读31次

    函数是一段可以反复调用的代码块。
    函数还能接受输入的参数,不同的参数会返回不同的值。

    一、Function 对象

    1. 构造函数
      Function 构造函数可以创建一个新的 Function 对象,它的语法:
    new Function (arg1, arg2, ...argN, functionBody)
    

    用 Function 创建函数时加不加 new 效果是一样的:

    var f = new Function('a', 'b', 'return a+b')
    var f = Function('a', 'b', 'return a+b')
    // f(1,2)    3
    
    1. 字符串之间加变量
    var n = 1
    var f = new Function('x','y','return x+' + n + '+y')
    // f(1,2)   4
    
    1. functionFunction 的区别
      function 是关键字,与 var 用法相似,function 用来声明一个函数。
      Function 是全局对象,window.Function 用法与 window.Object 相似,也可以用来声明函数。

    二、函数的声明

    函数有五种声明方式。

    1. 具名函数
    function f(x, y){
      return x+y
    }
    

    函数可以没有参数,但必须有返回值,不写的话则自动 return undefined.

    1. 匿名函数
     var f
     f = function(x, y){
         return x+y
     }
    

    单独声明一个匿名函数会报错,需把它赋给一个变量才可以。

    1. 具名函数赋值
     var f
     f = function f2(x, y){ return x+y }
     console.log(f2) // Uncaught ReferenceError: f2 is not defined
    

    注意:打印 f2 时报错。采用表达式声明函数时,function 命令后面若加上函数名,该函数名只在函数体内部有效,在函数体外部无效。

    1. window.Function
     var f = new Function('x','y','return x+y')
    
    1. 箭头函数
     var f = (x, y) => { return x+y }
     var sum = (x, y) => x+y
     var n2 = n => n*n
    

    箭头函数没有函数名。
    如果只有一个参数,可以省略()
    如果函数体只有一句话,可以同时省略{}return,不能只省略一个。

    三、函数的属性和方法

    1. name 属性
    • 函数的 name 属性返回函数的名字。
    function f1() {}
    f1.name // "f1"
    
    • 如果是通过变量赋值定义的匿名函数,那么 name 属性返回变量名。
    var f2 = function () {};
    f2.name // "f2"
    
    • 若变量的值是一个具名函数,name 属性返回 function 关键字之后的那个函数名。
    var f3 = function f4() {};
    f3.name // 'f4'
    f4.name // 报错
    
    • 若用 Function 构造一个函数,name 属性返回 anonymous,即“匿名的”。
     var f5 = new Function('x','y','return x+y')
     f5.name // "anonymous"
    
    2. length 属性

    函数的 length 属性返回函数定义时的参数个数。无论调用时输入了多少个参数,length 属性始终不变。

    function f(x, y) {}
    f.length // 2
    
    3. call() 方法

    f.call() 方法用来调用函数,即执行这个函数的函数体。f只是代表这个函数,并不能执行它。

    f.call(asThis, input1,input2)
    

    其中 asThis 会被当做 this[input1,input2] 会被当做 arguments.

    function f(x,y){ return x+y }
    f.call(undefined, 1,2) // 3
    

    四、函数其他知识点

    1. 参数 thisarguments

    call() 的第一个参数可以用 this 得到;
    call() 的后面的参数可以用 arguments 得到。

    1. this
      普通模式下,如果 thisundefined,浏览器会自动把 this 变成window.
      严格模式下,this 传的参数的值不会改变。
      this
    • 易错题 易错题 (1)this 就是 call 的第一个参数。
      (2)每个函数都有自己的 this,第一个 this 对应的 callf1.call(obj),第二个 this 对应的 call 是 f2.call()
      (3)thisarguments 都是参数,参数都要在函数执行(call)的时候才能确定。
    1. arguments
    • arguments 对象包含了函数运行时的所有参数,arguments[0] 是第一个参数,arguments[1] 是第二个参数,以此类推。
    • arguments 对象是伪数组
    • arguments 对象只有在函数体内部才可以使用。
      arguments
    2. call stack 调用栈
    • 代码段执行时,每进入一个函数之前,都会把函数的位置记入到 stack 里面,依次进行,return 时再回到 stack 里最上面的位置。例:
    function a() { console.log('a1'); b.call(); console.log('a2'); return 'a' }
    function b() { console.log('b1'); c.call(); console.log('b2'); return 'b' }
    function c() { console.log('c'); return 'c' }
    a.call()
    console.log('end')
    
    上面代码段调用栈的过程图解如下: call stack 调用栈
    • 递归
    function sum(n){
      if(n === 1) { return 1 }
      else { return n + sum.call(undefined, n-1) }
    }
    sum.call(undefined, 3) // 6
    // sum(3) = 3 + sum(2),sum(2) = 2 + sum(1),sum(1) = 1
    

    n 不可以无限大,超过栈内存就会发生溢出 stack overflow.

    3. 作用域
    1. 定义:作用域(scope)指变量存在的范围。
      全局作用域:变量在整个程序中一直存在,所有地方都可以读取;
      函数作用域:变量只在函数内部存在。
      函数外部声明的变量是“全局变量”(global variable),它可以在函数内部读取。
      在函数内部定义的变量,为“局部变量”(local variable),外部无法读取。
    • 对于var命令,局部变量只能在函数内部声明,在其他区块中声明,一律都是全局变量。
    • 对于a = 3 语句,会优先认为它是一个赋值语句,并按作用域从小到大找变量a,找到后给变量a赋值;若整个作用域里都找不到变量a,则认为它是声明并赋值,此时会声明一个全局变量a并赋值为3.
    1. 变量提升
      函数作用域内部也会产生“变量提升”现象。var命令声明的变量,不管在什么位置,变量声明都会被提升到函数体的头部。
    • 易错题 1:变量提升
    var a = 1
    function f1(){
        f2.call()
        console.log(a)  // 断点 1
        var a = 2
        function f2(){
            var a = 3
            console.log(a)  // 断点 2
        }
    }
    f1.call()
    console.log(a)
    

    问:运行到断点1时,打印出 a 的值为?
    答:由于变量提升,正确答案是 undefined.
    技巧:看到题目后,第一件事是先做变量提升。

    // 变量提升后
    var a = 1
    function f1(){
        var a  // f1 的变量提升
        function f2(){
            var a  // f2 的变量提升
            a = 3
            console.log(a)  // 断点 2
        }
        f2.call()
        console.log(a)  // 断点 1
        a = 2
    }
    f1.call()
    console.log(a)
    
    • 易错题 2:作用域
    var a = 1
    function f1(){
        console.log(a)
        var a = 2
        f4.call()
    }
    function f4(){
        console.log(a)  // 断点 3
    }
    f1.call()
    console.log(a)
    

    问:断点 3 处 函数 f4 打印出 a 的结果是?
    答:a = 1。函数 f4 的变量只在它自己的作用域或它的父作用域里有效。

    • 易错题 3
    var a = 1
    function f1(){
        console.log(a)
        var a = 2
        f4.call()
    }
    function f4(){
        console.log(a)  // 断点 4
    }
    a = 2
    f1.call()
    console.log(a)
    

    问:断点 3 处 函数 f4 打印出 a 的结果是?
    答:a = 2。因为函数的执行在语句a = 2之后发生。

    • 易错题 4

      问:点击“选项3”,打印出的两个 i 分别是多少? 易错题 4 答:打印出的两个 i 都是 6。因为 “用户移动鼠标点击的时间” 远远小于 “for 循环的时间”,所以点击 “选项 2” 时 for 循环已执行完毕,第一个 i=6;第二个 i 打印是在跳出 for 循环之后,所以结果也为 6。
    4. 闭包

    定义:如果一个函数,使用了它范围外的变量,那么(这个函数+这个变量)就叫做闭包。下面的一段代码就是闭包:

    var a = 1
    function f(){
      console.log(a)
    }
    

    五、相关知识

      • console.log() 默认打印出字符串,即使在 Chrome 中输出的结果不一定带双引号;
      • console.log() 返回 undefined,它的返回类型和打印出的结果没有 任何关系。源代码:
    console.log = function(a){
      alert(a)
      return undefined
    }
    
    1. window.eval():将字符串当作语句执行
    eval('1+1') // 2
    eval('1+"1"') // "11"
    

    函数的调用过程即 “eval 函数体(字符串)”的过程。

    相关文章

      网友评论

        本文标题:JS 核心之函数

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