js闭包原理及用途

闭包的原理

官方解释:闭包就是能够读取其他函数内部变量的函数。

要解释这段话,就要引入变量作用域的概念。

变量作用域

首先看下面这段代码的输出值

var num = 0
function add(){
  num = num + 1
  console.log(num)
}

add()  // 1
add()  // 2
add()  // 3
console.log(num) //3

此时,num是全局的作用域,在add()函数内外都可以访问到的;且在内存中是只有一个的,所有的运算都指向这唯一的num。

然后修改一下num声明的位置,查看下面这段代码的输出

function add(){
  var num = 0
  num = num + 1
  console.log(num)
}

add()  // 1
add()  // 1
add()  // 1
console.log(num) // num is not defined

此时,num的作用域只在函数add()中,在add()函数外是访问不到的;且每次执行add()在内存中都会创建一个num,这里相当于创建了3个num,所以加减运算并不会互相影响。

引入闭包

那么针对上面的情况,想在add()函数体外控制num该怎么办呢?

想想开篇的官方解释:“闭包就是能够读取其他函数内部变量的函数”。

这不就是js提供给我们的解决方法么,那么我们修改一下上面的代码:

const add=(function(){
  var num = 0
  return function(){
    num = num + 1
    console.log(num)
  }
})()

add()  // 1
add()  // 2
add()  // 3
console.log(num) // num is not defined

此时,num的作用域在函数内部,在外部也是访问不到的,但是可以通过函数内部return的方法访问;且现在函数里面的num只在内存里保留了一份,每次加减运算都是针对这唯一的num操作的。

这样即达到了函数外部无法直接访问函数内部变量的目的,又提供了方法去操作内部变量。

有没有觉得这种设计似曾相识,学过java的同学应该能想到,这个跟java里类的private和public属性很相似。

不错,摊牌了,闭包的用法之一,就是模拟私有方法。

用途实例

用途一,模拟私有方法

创建一个闭包 Conter,内部变量num和方法changeByNum(),保证在外部无法访问;
对外提供add()、subtract()、value()方法,使得外部可以调用内部方法或变量

const Conter = (function(){
  var num = 0
  
  // 私有方法
  function changeByNum(val) {
    num += val
  }

  // 公有方法
  return {
    add:function(){
      changeByNum(1)
    },
    subtract:function(){
      changeByNum(-1)
    },
    value:function(){
      return num
    }
  }
})();

Conter.add()
console.log(Conter.value())  // 1
Conter.add()
console.log(Conter.value())  // 2
Conter.subtract()
console.log(Conter.value())  // 1

console.log(num) // num is not defined

用途二,单例

假设通过上面的方法,创造两个闭包Conter1和Conter2,我们对Conter1做加减处理,在打印Conter2的值

const Conter = function(){
      var num = 0
      
      // 私有方法
      function changeByNum(val) {
        num += val
      }

      // 公有方法
      return {
        add:function(){
          changeByNum(1)
        },
        subtract:function(){
          changeByNum(-1)
        },
        value:function(){
          return num
        }
      }
    }
    
    var Conter1 = Conter();
    var Conter2 = Conter();
    Conter1.add()
    console.log(Conter1.value())  // 1
    Conter1.add()
    console.log(Conter1.value())  // 2
    Conter1.subtract()
    console.log(Conter1.value())  // 1

    console.log(Conter2.value())  // 0

可以看到无论Conter1如何改变,都不会影响Conter2内部的值,因为在创建闭包时已经分别存在内存中,相当于有两个num了。

那如果想要Conter1和Conter2共享一个num,也就是无论创建多少个闭包,指向的都是一块内存的代码呢?这个就是单例的概念。

我们分析下面这段代码:


    function Conter() {
      this.num = 0
    }

    Conter.prototype = {
      add: function () {
        this.num++
      },
      subtract: function () {
        this.num--
      }
    }

    const singleton = (function () {
      var instance = null

      return function () {
        if (!instance) {
          instance = new Conter();
        }

        return instance
      }
    })()

    var Conter1 = new singleton();
    var Conter2 = new singleton();
    Conter1.add()
    console.log(Conter1.num) // 1
    console.log(Conter2.num) // 1
    Conter1.add()
    console.log(Conter1.num) // 2
    console.log(Conter2.num) // 2
    Conter1.subtract()
    console.log(Conter1.num) // 1
    console.log(Conter2.num) // 1

首先创建构造函数 Conter,并原型中添加add()和subtract()两个方法。

然后创建闭包singleton,构造了一个Conter单例。

最后new了两个变量Conter1 ,Conter2,通过打印结果可以看到,无论Conter1 怎么变化,Conter2里面的num都是一起变化的,可以说明两个变量指向了同一个单例Conter。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值