JS专题: 函数

2018-04-20  本文已影响0人  squall1744

函数声明


匿名函数

var fn = function() {
  return 1
}

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

具名函数

// fn3在全局作用域下
function fn3() {
  return 3
}

//fn4作用域在函数体内
var fn5 = function fn4() {
  return 4
}
console.log(fn4) //not defined

箭头函数

var fn6 = () => {
  return 6
}

词法作用域


我们有如下代码

var global1 = 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)
     }
 }

对于上面的代码, 浏览器先把代码变成抽象语法树


词法树

假设我们要在fn2中调用local1变量

  1. 解析器会先在fn2节点中找, 上图中我们发现, fn2节点内没有local1
  2. 在fn2节点的上层节点中继续找, 也就是在fn1节点中找, 发现了local1
  3. 调用fn1中的local1
  4. 假设fn1中还没有, 那就继续往上找, 知道咋window下还找不到, 就报错
  5. 词法书只是分析调用哪个变量, 并不是分析变量的值

对于上面的第五点, 我们具体说明一下什么叫分析变量, 并不分析变量的值

假如我们有如下代码, 调用b()之后函数的值为1

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

我们稍在函数中给a赋值, 这时候打印出来的就是2

var a = 1
function b() {
  a = 2
  console.log(a)
}
b() //2

这就说明了词法树只会分析函数调用时候调用的是哪个变量, 并不关心变量的值, 变量的值决定于你给它赋值是多少

Call Stack


这里还是提一下loupe, 这个很清晰的描述了调用栈

this & aguments


  function f(){
      console.log(this)
      console.log(arguments)
  }
  f.call() // window
  f.call({name:'frank'}) // {name: 'frank'}, []
  f.call({name:'frank'},1) // {name: 'frank'}, [1]
  f.call({name:'frank'},1,2) // {name: 'frank'}, [1,2]

从上面的代码就能看出, f()这种方式的调用, 就是隐藏了this的简化版的call()

因为 this 就是函数与对象之间的羁绊

var person = {
          name: 'frank',
          sayHi: function(person){
              console.log('Hi, I am' + person.name)
          },
          sayBye: function(person){
              console.log('Bye, I am' + person.name)
          },
          say: function(person, word){
              console.log(word + ', I am' + person.name)
          }
      }
      person.sayHi(person)
      person.sayBye(person)
      person.say(person, 'How are you')

      // 能不能变成 
      person.sayHi()
      person.sayBye()
      person.say('How are you')

      // 那么源代码就要改了
      var person = {
          name: 'frank',
          sayHi: function(){
              console.log('Hi, I am' + this.name)
          },
          sayBye: function(){
              console.log('Bye, I am' + this.name)
          },
          say: function(word){
              console.log(word + ', I am' + this.name)
          }
      }
      // 如果你不想吃语法糖
      person.sayHi.call(person)
      person.sayBye.call(person)
      person.say.call(person, 'How are you')

      // 还是回到那句话:this 是 call 的第一个参数
      // this 是参数,所以,只有在调用的时候才能确定
      person.sayHi.call({name:'haha'})  // 这时 sayHi 里面的 this 就不是 person 了
      // this 真的很不靠谱

      // 新手疑惑的两种写法
      var fn = person.sayHi
      person.sayHi() // this === person
      fn()  // this === window

call / apply
fn.call(asThis, p1,p2) 是函数的正常调用方式
当你不确定参数的个数时,就使用 apply
fn.apply(asThis, params)
bind
call 和 apply 是直接调用函数,而 bind 则是返回一个新函数(并没有调用原来的函数),这个新函数会 call 原来的函数,call 的参数由你指定。
return
每个函数都有 return
如果你不写 return,就相当于写了 return undefined

柯里化和高阶函数


//柯里化之前
  function sum(x,y){
      return x+y
  }
  //柯里化之后
  function addOne(y){
      return sum(1, y)
  }
  //柯里化之前
  function Handlebar(template, data){
      return template.replace('{{name}}', data.name)
  }
  //柯里化之后
  function Handlebar(template){
      return function(data){
          return template.replace('{{name}}', data.name)
      }
  }

柯里化可以将真实计算拖延到最后再做
关于柯里化的高级文章:

  1. http://www.yinwang.org/blog-cn/2013/04/02/currying
  2. https://zhuanlan.zhihu.com/p/31271179

下面两个例子就是高阶函数在前端的应用例子

// 筛选出数组中的偶数并求和
reduce(filter(array, function(n){ n % 2 === 0}), function(prev, next){return preve+next},0)

//筛选出数组中的基数并排序
sort(filter(array, function(n){n % 2 === 1}),function(a,b){return a-b})

在前端, 高阶函数和函数式编程在react中有大量应用

回调


回调
分为两部分

  1. 函数被当做另一个函数参数
  2. 在函数中调用这个函数参数
    回调跟异步没有任何关系
上一篇下一篇

猜你喜欢

热点阅读