函数的五种声明方式

  1. 具名函数

    function f(x,y){
     return x+y
    }
    f.name // 'f'
    
  2. 匿名函数

    var f
    f = function(x,y){
     return x+y
    }
    f.name // 'f'
    
  3. 具名函数赋值

    var f
    f = function f2(x,y){ return x+y }
    f.name // 'f2'
    console.log(f2) // Uncaught ReferenceError: f2 is not defined
    
  4. window.Function (不推荐)

    var f = new Function('x','y','return x+y')
    f.name // "anonymous"
    
  5. 箭头函数

    var f = (x,y) => {
     return x+y
    }
     
    var sum = (x,y) => x+y
    //等同于
    var sum = (x,y) => {return x+y}
      
    var n2 = n => n*n
    //等同于
    var n2 = (n) => {return n*n}
    

注意函数的 name 属性在不同的声明方法中各种不同的表现。

函数的本质

函数就是一段可以反复调用的代码块。

  1. 回顾一下

回顾原型
回顾原型

  1. 函数的调用

f.call(asThis, input1,input2) 其中 asThis 会被当做 this,[input1,input2] 会被当做 arguments 禁止使用 f(input1, input2),因为学会 .call 才能理解 this

函数的本质
函数的本质

this 和 arguments

this-arguments
this-arguments

来看一个例子:

var f = function () {
  console.log(this)
  console.log(this === window)
}

f.call(undefined,2,3)
// Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
// true

第一个参数明明是 undefined,但是浏览器自动把 this 变成了 window。

我们需要用严格模式来看一下:

var f = function () {
  'use strict'
  console.log(this)
  console.log(this === window)
}

f.call(undefined,2,3)
// undefined
// false

f.call('666',3,4)
// 666
// false

这样才符合我们的预期,即:this 就是 call 中的第一个参数;而后面的参数则组成 arguments 。

call stack

  • call stack(调用堆栈)先入后出
  • 每进入一个函数中,会把函数的位置记录在 stack 里面,等 return 的时候就回到 stack 最顶部的位置。

递归,每次进一个函数就要压一次 stack,压入过多会导致 stack overflow

function sum(n) {
  if (n === 1) {
    return 1
  }
  else {
    return n + sum.call(undefined, n-1)
  }
}
sum.call(undefined, 5)//试着把这个值改成很大的值,会出现 stack overflow
//15
sum.call(undefined, 27988)  // 2018年6月1日 Chrome
//391678066
sum.call(undefined, 27989)
//Uncaught RangeError: Maximum call stack size exceeded

来看几个可视化的例子:

作用域

  • 变量声明提升

    var a = 1
    function f1(){
    alert(a) // 是多少
    var a = 2
    }
    f1.call()
    
  • 就近原则

    var a = 1
    function f1(){
    var a = 2
    f2.call()
    }
    function f2(){
    console.log(a) // 是多少
    }
    f1.call()
    
  • 我们只能确定变量是哪个变量,但是不能确定变量的值

    // 先来写 6 个 li 标签
    var liTags = document.querySelectorAll('li')
    for(var i = 0; i<liTags.length; i++){
    liTags[i].onclick = function(){
        console.log(i) // 点击第3个 li 时,打印 2 还是打印 6?
    }
    }
    

闭包

如果一个函数,使用了它范围外的变量,那么(这个函数 + 这个变量)就叫做闭包。

请参看:

JS 中的闭包是什么?

(待续)