likes
comments
collection
share

javascript 递归、闭包、this对象

作者站长头像
站长
· 阅读数 1

javascript 递归、闭包、this对象

递归

什么是递归函数?

  • 编程语言中,函数直接或间接调用函数本身,则该函数称为递归函数。

递归特性:

  • 必须要有一个明确的结束条件
  • 每次进入更深一层次递归时,问题规模比上一次递归都应有所减少
  • 递归效率不高,递归层次过多会导致栈溢出

arguments.callee 指向正在执行的函数的指针。因此可以用它实现对函数的递归调用。

但是在严格模式 use strict 下,不能访问arguments.callee ,访问时会导致致命错误。可以使用命名函数表达式来替代。

function sum(num){
  if(num-1){
    return 1;
  }else {
    return num * arguments.callee(num-1);
  }
}

'use strict'
var sum = (function f(num){
  if(num-1){
    return 1;
  }else {
    return num * f(num-1);
  }
})

闭包

什么是闭包?
  • 是指有权访问另一个函数作用域中的变量的函数。
  • 能够读取其他函数内部变量的函数。
闭包的三个特性
  1. 函数嵌套函数
  2. 内部函数可以引用外部的参数和变量
  3. 参数和变量不会被垃圾回收机制回收
常见闭包的实现?

在一个函数内部,创建另一个函数,并return这个函数;

闭包为什么可以实现在函数外读取到函数内的变量?
  • 因为内部函数的作用域链中包含外包函数的作用域。

  • f1是f2的父函数,f2被赋给了一个全局变量,f2始终存在内存中,f2的存在依赖f1,因此f1也始终存在内存中,不会在调用结束后,被垃圾回收机制回收。
function f1(){
        var n = 123;
        function f2(){    //f2是一个闭包
            alert(n)
        }    
        return f2;
    }
闭包产生的原因?

首先要明白作用域链的概念,其实很简单,在ES5中只存在两种作用域全局作用域函数作用域当访问一个变量时,解释器会首先在当前作用域查找标示符,如果没有找到,就去父作用域找,直到找到该变量的标示符或者不在父作用域中,这就是作用域链,值得注意的是,每一个子函数都会拷贝上级的作用域,形成一个作用域的链条

闭包产生的本质就是,当前环境中存在指向父级作用域的引用


闭包的作用(用途场景)
  • 匿名自执行函数:创建一个匿名的函数,并立即执行它,由于外部无法引用它内部的变量,因此在函数执行完后会立刻释放资源,关键是不污染全局对象

  • 对结果进行缓存:闭包可以做到这一点,因为它不会释放外部的引用,从而函数内部的值可以得以保留。

    • var fn=(function(){
          var cache={}//将结果缓存到该对象中
          return function(){
              var str=JSON.stringify(arguments);
              if(cache[str]){//判断缓存中是否存在传递过来的参数,存在直接返回结果,无需计算
                  return cache[str];
              }else{//进行计算并返回结果
                  var sum=0;
                  for(var i=0;i<arguments.length;i++){
                      sum+=arguments[i];
                  }
                  return cache[str]=sum;
              }
          }
      })()
      
  • 模拟私有方法:私有方法有利于限制对代码的访问,而且可以避免非核心的方法干扰代码的公共接口,减少全局污染。

    • # 模块模式
      var calculator = (function(){
          var a = 1;
          function addCalculator(val){
              a += val
          }
          return {
              add1:function() {
                  addCalculator(1);
              },
              add2:function() {
                  addCalculator(2);
              },
              result:function() {
                  return a
              }
          }
      })();
      console.log(calculator.result());  // 1
      calculator.add1();
      console.log(calculator.result());  // 2
      calculator.add2();
      console.log(calculator.result());  // 4
      
闭包的优缺点
  1. 优点
    1. 可以读取函数内部的变量.
    2. 可以让局部变量保存在内存中,实现变量数据共享,希望一个变量长期存储在内存中。
    3. 避免全局变量的污染
    4. 私有成员的存在。
  2. 缺点
    1. 闭包会使得函数中的变量保存在内存中,一直没释放,会导致内存占用很大,造成内存泄漏
    2. 闭包会在父函数外部,改变父函数内部变量的值。当父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

this对象

this 对象是在运行时基于函数的执行环境绑定的:在全局函数中this 等于 windows,而当函数被作为某个对象的方法调用时this 等于那个对象

如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this且不能通过 call()apply()bind() 方法来改变 this 的值

  1. 普通函数调用this 是指向 window 的.

  2. 作为对象的方法调用this 这时就指向调用它的对象。

  3. 构造函数的调用:如果不使用new 操作符,则this指向window,如果使用new 操作符创建了一个实例,则this指向该实例。

    那么new 构造函数做了什么?

    1. 创建新对象
    2. 把构造函数的作用域赋给新创建的对象。因此this指向了这个新对象
    3. 执行构造函数中的代码,为新对象添加属性 ;
    4. 返回新对象。

    👉new 运算符

    自己实现 new 操作符功能

    # 1、创建一个对象并返回;2、修改该对象的this指向;3、保证该对象拥有构造函数的所有属性;
    function _new(){
      var obj = {}
      var constuctorFun = [].shift.call(arguments)
      obj.__proto__ = constuctorFun.prototype
      var res = constuctorFun.apply(obj,arguments)
      return res instanceof Object ? res: obj
    }
    
    function People(name,age){
      this.name = name;
      this.age = age;
    }
    let people2 = _new(People,'Rose',18);//调用自定义create实现new
    console.log(people2.name) //Rose
    console.log(people2.age) //18
    
  4. apply / call 调用:使用 apply 方法可以改变 this 的指向。如果这个函数处于非严格模式下,则指定为 null 或 undefined 时会自动指向全局对象(浏览器中就是window对象)。

  5. 箭头函数:箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值。如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this;否则,this 的值会被设置为 undefined


资料参考

👉公司新来的女实习生问我什么是闭包?