美文网首页
JS专题: 函数

JS专题: 函数

作者: squall1744 | 来源:发表于2018-04-20 15:54 被阅读0次

    函数声明


    匿名函数

    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 为什么必须是对象

    因为 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

    柯里化和高阶函数


    • 柯里化:将 f(x,y) 变成 f(x=1)(y) 或 f(y=1)x
    //柯里化之前
      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
    • 高阶函数
      在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数:

      接受一个或多个函数作为输入:forEach sort map filter reduce
      输出一个函数:lodash.curry
      不过它也可以同时满足两个条件:Function.prototype.bind

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

    // 筛选出数组中的偶数并求和
    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. 在函数中调用这个函数参数
      回调跟异步没有任何关系

    相关文章

      网友评论

          本文标题:JS专题: 函数

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