深入理解js闭包

本文深入探讨了闭包的概念,它是有权访问另一个函数作用域中变量的函数。通过MDN的定义,了解到闭包由函数及其创建时的环境组成。文章详细阐述了执行上下文、作用域链以及不同作用域类型。举例说明了闭包如何保存外部变量的执行上下文,并展示了闭包在单例模式和模拟私有属性中的应用。闭包在JavaScript中扮演着重要角色,如防止内存泄漏和实现数据隐藏。
摘要由CSDN通过智能技术生成

闭包的定义

  • 简单讲:闭包就是指有权访问另一个函数作用域中的变量的函数。

  • MDN这样定义:闭包是一种特殊的对象。它由两部分构成:函数,以及创建该函数的环境。环境由闭包创建时在作用域中的任何局部变量组成。

执行上下文和作用域

作用域就是一套规则,用于确定在何处以及如何查找变量(标识符)的规则
三种作用域类型:

  • 全局作用域:全局作用域为程序的最外层作用域,一直存在。
  • 函数作用域:函数作用域只有函数被定义时才会创建,包含在父级函数作用域 / 全局作用域内。
  • 块级作用域块级作用域由最近的一对包含花括号{}界定,ES6新增,用let命令新增了块级作用域,

笔者所理解的执行上下文和作用域的区别:

  • 作用域在函数定义时已经确定,因为js使用的作用域类型是词法作用域
  • 执行上下文会在函数被调用时创建,它是一个内部对象,它定义了一个函数执行时的环境。函数执行完毕,上下文被销毁。

所以可以利用同一个作用域可以创建多个执行上下文

作用域链

作用域链当中储存了执行其上下文的集合,这个集合呈链式链接,称作作用域链。
在这里插入图片描述

我理解的闭包

如果一个函数执行时引用了外部的变量,则被引用的那个变量所在的执行上下文会被保存下来,不会被出栈释放。
在这里插入图片描述

  • 第13行intter是一个闭包,包含函数以及一个私有的执行上下文,被它调用的a和b都被保存下来了
    在这里插入图片描述

值得一提的是,全局下的变量也可以用作闭包,因为变量存在于全局下,所以全局上下文被保存下来,由于全局上下文始终存在且只有一个,所以这个操作忽略不计。

下面一个例子,判断它的输出:

let bar="hello"
function outter() {

  return function intter() {
    console.log(bar)
  }
  
}
bar="world"
let intter=outter()
intter();

输出结果为“world”

  • 此例我们可以理解到,闭包并不是拷贝了一份变量保存下来,而是将它所调用的变量当时的执行上下文给保存下来了。
  • bar变量定义在全局下,全局上下文只有一个,所以bar能被改变再输出。

再来个例子:

function outter() {
  let bar="hello"
  function intter() {
    return function intter2(){
      console.log(bar);
    }
  }
  bar="world"
  let intter2=intter()
  intter2();
}

outter()

很显然,intter2会把outter的一个执行上下文保存下来,所以如果你还在此执行上下文中改变变量,会产生效果。

闭包的应用场景

1.单例模式

  • 单例模式保证了一个类只有一个实例,是一种常见的设计模式。
  • 实现方法:先判断实例是否存在,如果存在就直接返回,否则就创建了再返回。
  • 好处:避免了重复实例化带来的内存开销

//单例模式
function fun() {
  this.data = 'hello,world'
}

//立即执行函数保证只会创建一次函数上下文
//所以instance只会存在一个
fun.getInstance = (function () {
  let instance=null;

  return function () {
    if(instance){
      return instance
    }
    else {
      instance=new fun()
      return instance
    }
  }
})()

let sa=fun.getInstance()
let sb=fun.getInstance()
console.log(sa===sb)
sa.data="111"
console.log(sb.data)

2.模拟私有属性

javascript 没有 java 中那种 public private 的访问权限控制,对象中的所用方法和属性均可以访问,这就造成了安全隐患,内部的属性任何开发者都可以随意修改。虽然语言层面不支持私有属性的创建,但是我们可以用闭包的手段来模拟出私有属性:


function fun() {
  let _name="why"
  let _age=20

  return function () {
    return {
      getName:function() {return _name;},
      getAge:function() {return _age;}
    }
  }
}

let obj = fun()()
console.log(obj.getName())//why
console.log(obj.getAge())//20
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值