• JavaScript函数表达式


    函数表达式的基本语法形式

    var functionName = function(arguments){
      //函数体
    }
    

    递归建议

    我们通过例子来一步步说明,递归的最佳实现方式。下面是普通递归调用的例子:

    // 阶乘的递归函数
    var factorial = function(num){
      if(num <= 1){
        return 1;
      }else {
        return num * factorial(num-1);
      }
    }
    
    console.log(factorial(3)); //6
    

    我们来看一下这种情况:

    // 赋给一个变量
    var anotherFactorial = factorial;
    factorial = null;
    
    // 调用递归函数
    console.log(anotherFactorial(3));
    

    运行结果:

    TypeError: factorial is not a function
    

    这里提示错误,说factorial不是一个函数,因为我们已经把factorial设置为null,而在执行 anotherFactorial(3) 时,是通过factorial(num-1) 来递归调用的,所以就报错,因为已经把factorial设置为null。

    解决的策略就是使递归调用的函数内部不要出现外部定义的函数名。

    我们可以通过命名表达式来实现(注意括号的使用):

    var factorial = (function f(num){
      if(num <= 1){
        return num;
      }else {
        return num * f(num-1);
      }
    });
    
    console.log(factorial(3)); //6
    
    var anotherFactorial = factorial;
    factorial = null;
    
    console.log(anotherFactorial(3)); //6
    

    理解匿名函数

    匿名函数:function后面没有跟着函数名的函数

    我们常见的匿名函数有函数表达式:

    var functionName = function(arguments){
      //函数体
    }
    

    当然匿名函数也可以不要赋值给变量:

    (function(num){
      console.log(num+1);
    })(3);
    

    运行结果:

    4
    

    匿名函数也可以作为函数返回:

    function person(){
      return function(){
        console.log("TabWeng");
      }
    }
    person()();
    

    运行结果:

    TabWeng
    

    对于 person()(),有些人不太理解,其实不难理解,这里解释一下:

    首先来看一下 person(),我们是不是得到一个返回的匿名函数 function(){console.log("TabWeng");},既然我们得到了一个函数,对于函数的调用,是不是给它的尾部加一个括号就可以了,所以就写成person()()

    理解闭包

    闭包:有权访问另一个函数作用域的函数

    匿名函数是function后面没有跟着函数名的函数,和闭包的定义不同,尽管这两个称呼经常说的是同一个函数,但是根据功能的不同,应该加以区分。

    如果你理解作用域链,那么闭包就非常好理解。通过闭包的这种特性,我们可以来实现JavaScript的很多模式,更加灵活的运用JavaScript。

    如果要访问一个函数的作用域,我们可以在函数里面创建一个闭包,对于闭包而言,被访问的函数处在闭包作用域链的第二层(从前端到终端的顺序),而作用域链的指针指向的是整个活动对象。

    有一个典型的例子不得不讲:

    function printNum(){
    
      var nums = [];
      for(var i = 0; i < 3; i++){
        nums[i] = function(){
          return i;
        }
      }
    
      for(var j = 0; j < nums.length; j++){
        console.log(nums[j]());
      }
    }
    
    printNum();
    

    运行结果:

    3
    3
    3
    

    为什么会得到这样的结果,存在两个疑问:

    1. 为什么结果是3?不应该是2吗?
    2. 为什么结果都是3?

    我们先来分析一下出现的原因:

    首先针对问题2,在闭包里面有return i;,而在闭包中,i是没有定义的,根据作用域链,会向上一层作用域链寻找i,在上一层中我们发现了i,我们是通过寻找上一层的活动对象来找到i的,既然是活动对象,里面的数值就是最终的值,所有此时的i已经是最终的值3了,我获得的i就是最终的值3。因此打印出来每个结果都是3。

    知道了问题2出现的原因,那么问题1也就是自然明了,在for循环中,i加到等于3,通过i<3,使i没有进入for循环的里面,尽管 i==3 没有进入for循环,但是闭包在获取i的时候,获取的就是i的最后一个值3。

    通过立即执行函数来解决这个问题:

    function printNum(){
    
      var nums = [];
      for(var i = 0; i < 3; i++){
        nums[i] = function(num){
          return num;
        }(i);
      }
    
      for(var j = 0; j < nums.length; j++){
        console.log(nums[j]);
      }
    }
    
    printNum();
    

    运行结果:

    0
    1
    2
    

    性能优化

    使用闭包时,因为闭包的作用域链会引用活动对象,使这个活动对象无法被回收(内存),而如果这个引用一直存在,那么内存将一直无法得到释放。通常的解决方法是解除这个引用。(解除引用的方法很多,主要是要有这个性能优化的思想,知道存在的问题)。

    闭包的运用

    (function(){
      //私有作用域
    })();
    

    看这块代码,之所以称为私有,通过作用域链我们可以知道,外部不能访问里面。

    而通过闭包可以实现提供公有方法而使外部能对私有变量进行访问。

    function Person(name){
      var name = name;
      var sayName = function(){
        console.log(name);
      };
    
      this.publicSayName = function(){
        console.log("我访问了name这个属性了:"+name);
        sayName();
      };
    }
    
    var p1 = new Person("TabWeng");
    p1.publicSayName();
    

    运行结果:

    我访问了name这个属性了:TabWeng
    TabWeng
    

    这块代码也可以这样写:

    function Person(name){
      var name = name;
      var sayName = function(){
        console.log(name);
      };
    
      return function(){
        console.log("我访问了name这个属性了:"+name);
        sayName();
      };
    }
    
    var p1 = new Person("TabWeng");
    p1();
    

    运行结果:

    我访问了name这个属性了:TabWeng
    TabWeng
    

    通过返回的匿名函数获得私有变量。

    当然,也可以这样写:

    function Person(name){
      var name = name;
      var sayName = function(){
        console.log(name);
      };
    
      return {
        printString:"我访问了name这个属性了:"+name,
        sayName:sayName,
        publicMethod:function(){
          sayName();
        }
      };
    }
    
    var p1 = new Person("TabWeng");
    console.log(p1.printString);
    p1.sayName();
    p1.publicMethod();
    

    运行结果:

    我访问了name这个属性了:TabWeng
    TabWeng
    TabWeng
    

    这就是模块模式,返回的是一个对象,尽管是对象字面量的形式,也可以得到私有变量。

    参考

    • 《JavaScript高级程序设计》
  • 相关阅读:
    .htaccess 文件不起作用?| (Apache Rewrite)
    Putty 如何解决中文乱码(笔记)
    如何删除鼠标右键“发送到”的多余项
    开发环境搭建笔记
    Javascript String 包
    第十五章:使用canvas绘图
    慕课css3 2章边框和3章颜色相关
    第5章引用类型
    第一章 JavaScript简介
    第五章引用类型(Object类型、Array类型)
  • 原文地址:https://www.cnblogs.com/hlwyfeng/p/6082210.html
Copyright © 2020-2023  润新知