深入了解函数

函数声明

匿名函数
var fn = function(){
    return 1
}

var fn2 = fn
fn.name //fn
fn2.name //fn

注意,这里的变量fn是保存匿名函数的内存地址,而不是函数本身.

具名函数
function fn3(){
    return 3
}

var fn5 = function fn4(){
    return 4
}

console.log(fn3) //3
console.log(fn4) //undefined
console.log(fn5) //4

注意,具名函数假如不赋值给变量,那么作用域是全局,如果赋值给变量,那么作用域在函数本身.

箭头函数
var fn6 = i => i+1

fn6(1) //2

var fn7 = i =>{
    i+1;
    ...doSometing
}

fn7(1) //undefiend

假如箭头函数只有一个变量,那么括号可不写.
箭头函数的函数体只有一句话,那么return的值就是求的值.
箭头函数的this是上下文的this.

词法作用域(静态作用域)

var globall1 = 1
function fn1(param1){
    var local1 = 'local1'
    var local2 = 'local2'
    function fn2(param2){
        var local2 = 'inner local2'
        console.log(local1)
        console.log(local2)
    }
    function fn3(){
        var local2 = 'fn3 local2'
        fn2(local2)
    }
}

JS引擎会先进行词法解析,并形成词法树

下面是上面函数的词法树


    window
      |
  ----------
 |         |
 global1  fn1
 ---
  |
 ------------------------------------
  |       |          |       |       |
  param1 local1    local2   fn2     fn3
                             |       |
                            ---     ---
                             |       |
                           local2  local2 
  1. 当函数寻找变量的时候,会先从函数内部寻找变量
  2. 假如没有该变量,则会从词法树往上找.

  3. fn2里的console.log(local1)是上层fn1的local2,所以输出’local1’,

  4. fn2里的console.log(local2)是自己的local2,所以输出’inner local2’,
  5. fn3里的local2是自己的local2,为’fn3 local2’.
  6. fn3里的fn2(local2),local2是他本身local2,即fn2(‘fn3 local2’),最终结果同2.

Call stack

Stack(栈),先进后出.

  1. 普通调用

     function a(){
         console.log('a')
         return 'a'
     }
    
     function a(){
         console.log('b')
         return 'b'
     }
    
     function a(){
         console.log('c')
         return 'c'
     }
    
     a()
     b()
     c()
    

    运行函数

    进行了以下事情

     先进行词法解析,函数提升
    
     ->a() //进入a,记下a的位置
         ->console.log('a') //进入,记下该位置
         <-console.log('a') //退出,回到原位置
         ->return 'a' //进入
         <-return 'a' //退出
     <-a() //退出a,回到原来a的位置
    
     ->b()//进入b,记下a的位置
    
         ...
    
     <-c() `
    
  2. 嵌套调用

function a(){
    console.log('a1')
    b()
    console.log('a2')
    return 'a'
}

function a(){
    console.log('b1')
    c()
    console.log('b2')
    return 'b'
}

function a(){
    console.log('c')
    return 'c'
}

运行

->a()
    ->console.log('a1')
    <-console.log('a1')
    ->b()
        ->console.log('b1')
        <-console.log('b1')
        ->c()
            ->console.log('c')
            <-console.log('c')
            ->return 'c'
            <-return 'c'
        <-c()
        ->console.log('b2')
        <-console.log('b2')
        ->return 'b'
        <-return 'b'
    <-b()
    ->console.log('a2')
    <-console.log('a2')
    ->return 'a'
    <-return 'a'
<-a()  

  1. 递归
function fab(n){
    console.log('strat calc fab' +n)
    if(n>=3){
        return fab(n-1) + fab(n-2)
    }else{
        return 1
    }
}

fab(5)

运行

strat calc fab 5
    5>3
    求fab(4)
        strat calc fab 4
        fab(3)+fab(2)
        4>3
        求fab(3)
            strat calc fab 3
            fab(2)+fab(1)
            3=3
            求fab(2)
                strat calc fab 2
                fab(2)+fab(1)
                3-1<3
                fab(2)=1
            求fab(1)
                strat calc fab 1
                1<3
                fab(1)=1
        得fab(3) = 1 + 1 = 2
        求fab(2)
            strat calc fab 2
            2<3
            fab(2)=1
        得fab(2) = 1
    得fab(4) = 2 + 1 = 3
    求fab(3)
        strat calc fab3
        fab(2)+fb(1)
            求fab(2)
            strat calc fab 2
            2<3
            fab(2)=1
            求fab(1)
             strat calc fab1
             fab(1)=1
    得fab(3) = 1 + 1 = 2
得fab(5) = 3 + 2 = 5   

this&arguments

this是隐藏的第一参数,且必须是对象
function f(){
    console.log(this)
    console.log(arguments)
}

f.call() //window (chrome是Window) ,[]
f.call({name:'a'}) //{name:'a'} ,[]
f.call({name:'a'},1) //{name:'a'},[1]
f.call({name:'a'},1,2) //{name:'a',[1,2]}
this是函数与对象的羁绊

函数调用的时候,this只看调用时前面的对象是什么,不看定义时的对象,是独立存在的!
this是调用时第一个隐藏参数.

var name = window
var person = {
    name:'roxas',
    sayHi:function(){
        console.log('I am' + this.name)
    }
}

person.sayHi() //I am roxas
//相当于perosn.sayHi.call(peroson)

var fn = person.sayHi
fn() //I am window
//相当于peroson.sayHi.call()

call/apply

fn.call(_this,p1,p2)是函数的正常调用方式.
当你不确定参数的个数时,就使用apply.
fn.apply(_this,params)

假设数组的长度不确定
var arr = [1,2,3,4,5,....]
function sum(){
    var n = 0;
    for(var i = 0;i< arguments.length;i++){
        n+= arguments[i]
    }
}

sum.call(undefined,arr[0],arr[1],....)
这时候无法用call,所以要用apply
sum.apply(undefined,arr)

bind

call和appy都是直接调用函数,而bind则是返回一个新函数.
但是并没有调用,这个函数调用时会call原来的函数,call的参数是bind传入的参数.
也就是说,bind的函数的this是bind参入的第一个参数.

柯里化

高阶函数

回调

构造函数

箭头函数

与普通函数区别

  1. this是上下文的this
  2. 不能指定this,call()无效
function fn(){
    this
    function fn2(){
        this //通过调用确定
    }
    var fn3 = ()=>{
        this //一定是函数内的this
    }
}