Js高级程序设计第三版学习(七章)

                              Js高级程序设计第三版学习(七章)

 

第七章 函数表达式

定义函数有两种方式 函数声明, 和函数表达式

函数声明: function 后面跟标识符name, 函数声明提升: 代码流在执行时 会先读取函数声明,所以函数声明代码可以放在函数调用后面.

      func();//我是函数声明
      function func() {
        console.log('我是函数声明');
      }

函数表达式: 把匿名函数赋值给变量,通过变量调用,  匿名函数: function后面不跟标识符, 由于是赋值操作,所以不存在提升问题

      //函数表达式
      var func = function () {
        console.log('函数表达式');
      };
      func();

命名函数表达式 :  如果我们仅仅想在函数内部调用自身,那么可以使用命名函数表达式, 命名函数表达式在函数外部是无法调用的

      //命名函数表达式 只能在函数内部调用
      var func = function fn() {
        console.log('命名函数表达式');
        fn();//会无限调用
      };
      func();
      fn();//fn is not defined

立即执行函数(IIFE) : 将函数声明包在一个括号中'()',作为函数表达式,然后跟一个(参数)表示立即调用, 立即执行函数不会污染全局,因为函数执行后里面的作用域链就被销毁了, IIFE 有两种写法 (函数声明)(), (函数声明()),两种写法使用方法一样

      //IIFE 立即执行函数
      (function(name) {
        var tomorrow = 'tomorrow';
        console.log(name); //lmx
      })('lmx');

      console.log(tomorrow); //tomorrow is not defineds

立即执行函数前面可以放运算符(+ - * / %  = ......), 代码会在函数执行完成后再执行运算操作

      //IIFE 立即执行函数
      var result = (function() {
        var num = 5;
        return num;
      })() % (function(){
        var num = 10
        return num
      })();
      console.log(result); // 5

如果函数不是立即执行函数,也想调用自身的情况下,也可以用如下的形式, 因为赋值操作已经完成,所以不会报错,但如果是立即执行函数则会报错 func()     not defined

      //与上面相似
      var func = function () {
        console.log('函数表达式');
        func();//会无限调用
      };
      func();

立即执行函数调用自身, 就需要使用命名表达式了

      var func = (function fn() {
        console.log('函数表达式');
        fn();//会无限调用
      })();

  1.递归函数:  : 就是函数调用自身,递归中使用命名函数表达式,使用函数声明的时候有可能会出现错误,下面对书中递归例子给行解释

      //递归
      function fn(num) {
        console.log(num)
        if (num < 1) {
          return 1;
        } else {
          //通过函数名调用自身
          return fn(num - 1); //改成nn() 
        }
      }
      //让nn等于这个函数指针
     var nn = fn;
     //让fn指向null
     fn = null;
     /* 
        此时会报错  fn is not a function
        但是 此时函数会执行一次 console 然后才报错
        一开始我理解为 应该直接报错 因为这时候fn = null 
        出现这种情况是因为 fn仅仅是一个标识符 用来存放指向这个函数体的指针
        再赋值时fn把这个指针赋值给了nn 此时nn也指向了函数体,
        改变fn的指向并没有改变nn, nn还是指针,指向函数体, 此时nn还是function,对于引用类型都适用
        递归中使用函数名调用自己是存在风险的,所以建议使用命名函数表达式的形式
      */
     console.log(nn(5)); //5
     console.log(typeof nn)//function

 

  2.闭包:  

闭包实质就是一个函数可以访问另一个函数作用域中的变量,闭包最常见的方式就是在a函数内部在创建一b个函数,b函数的作用域有权访问a函数内部的变量, 当我们调用a方法并返回b方法, 然后再调用b函数时 我们依旧可以使用a函数内部的变量,因为此时b函数内部的变量对象包含着a函数的变量对象

      //闭包
      function wrapper(){
        var xiaoMing = 'xiaoMing';
        return function (){
          console.log(xiaoMing)
        }
      }
      var fn = wrapper();
      fn();//xiaoMing

当第一次执行一个函数时,会创建一个执行环境,和相应的作用域链,把作用域链赋值给一个特殊的属性[[scope]],然后初始化这个函数的变量对象,这个函数的上下文的[[scope]]中 第一项 是这个函数本身的变量对象的指针 第二项是 外部函数的变量对象的指针, .... 最后一项 是全局的变量对象的指针, 所以某些函数可能公用一个函数的变量对象

      //闭包 公用一个变量对象的例子
      // 可以看到 public 被改变了两次
      var public = 'xiaoHei';

      function change1(){
        public = 'chang1';
      }
      function change2(){
        return function(){
          public = 'return fn';
        }
      }
      change1();
      console.log(public)//chang1
      var fn = change2();
      fn();
      console.log(public)//return fn

闭包会携带包含他的作用域,占用比普通函数更多的内存,请慎重使用

  • 闭包与变量: 闭包只能取得包含函数中的最后一个值

result数组中的匿名函数的作用域链,指向的都是同一个包含函数handle的活动变量对象,在最后一次循环结束的时候index的值为5 所以此时result的所有匿名函数取得的index都为5

      function handle() {
        var result = [];
        for (var index = 0; index < 5; index++) {
          result[index] = function(){
            console.log(index)
          }
        }
        console.log(index);//5
        return result;
      }
      var result = handle();
      result[0]();//5
      result[1]();//5

我们可以使用IIFE强制修改闭包的行为,来符合我们的预期, 此时result数组的匿名函数,都是通过IIFE返回的匿名函数,这些匿名函数都有一个各自的一个匿名的包含函数,无论是以传参的形式,保存index, 还是以声明变量的形式保存index,包含函数中都是符合我们预期的index值.

      function handle() {
        var result = [];
        for (var index = 0; index < 5; index++) {
          result[index] = (function(num) {
            var hehe = index;
            return function() {
              console.log(hehe);
              console.log(num);
            };
          })(index);
        }
        return result;
      }
      var result = handle();
      result[0](); //00
      result[1](); //11
  • 关于this对象 : js的this对象,都是根据执行时的上下文来决定的, 如果我们在闭包中使用this 那么可能这个上下文对象并不一定是我们预期的上下文对象,而有可能是全局对象
          var name = 'window';
          var obj = {
            name:'lmx',
            handle:function(){
              return function(){
                console.log(this.name);
              }
            }
          }
          //实质上闭包的this.name 是window.name
          //它跟下面函数表达式的写法相似 调用者都是全局对象
          obj.handle()()//window;
          var fn = obj.handle();
          fn();//window
    
          //此时调用的才是obj的 name
          obj.newHandle = obj.handle();
          obj.newHandle(); // lmx

     

我们可以在闭包的包含函数中定义 一个变量来存放上下文对象,来使闭包中的this符合我们的预期

var name = 'window';
      var obj = {
        name:'lmx',
        handle:function(){
          var that = this;
          return function(){
            console.log(that.name);
          }
        }
      }

      obj.handle()();//lmx
     
  • 内存泄漏 : 在低版本ie中, 对于dom和bom对象可能使用的是引用计数的收回机制来处理内存,如果我们在闭包中使用了dom或者bom对象,虽然闭包已经执行完成,但是引用dom的变量并未被释放, 因为他的引用次数不为0,这时我们可能需要手动将变量设置为null,来释放内存

3.私有变量:  在任何函数定义的变量或函数,都可以被认为是私有变量,因为不能再函数外面使用私用变量,我们把有权访问私有变量或者私有函数的方法称之为特权方法

我们可以用对象的构造函数来创建特权方法, 此时实例拥有各自私有变量,而且每次创建一个实例,特权方法都会被新建(构造函数的弊端)

      function Human(){
        var tomorrow = ' tomorrow';
        this.handle = function(){
          console.log(tomorrow);
        }
      }
      var li = new Human();
      li.handle();//study.html:14  tomorrow
  • 静态私有变量: 通过私有作用域创建的私有变量或方法, 其实就是原型模式, 通过原型的特权方法来访问私有变量,注意此时所有实例共享一个方法,而且每个实例并没有其独自的私有变量
          function Person() {}
          (function() {
            var haha = 'haha';
            Person.prototype.heihei = function() {
              console.log(haha);
            };
          })();
    
          var obj = new Person();
          obj.heihei(); //haha

     

  • 模块模式 :   其实就是为字面量方式创建的对象, 进行增强, 为其增加私有变量和特权方法
      var single= (function(){
            var name = 'lmx'
            return {
              name:'nihao',
              //特权方法
              handle:function(){
                console.log(name);
              }
            }
          })()
    
          console.log(single.name);//nihao
          single.handle();//lmx

     

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值