闭包是JavaScript的重点也是难点之一,由于涉及多重知识点,对初学者来说比较难理解。本文将闭包相关的知识点进行梳理,帮助大家更好的理解闭包。下面从4个部分进行说明:即作用域、执行上下文、闭包和内存管理。
文章重点内容如图所示:
image.png1、作用域
作用域其实就是定义了一套规则用来查找变量,控制着变量与函数的可见性和生命周期。在JavaScript中,有全局作用域和函数作用域,随着es6的发展,引入了块级作用域。
- 全局作用域
在代码中任何地方都能访问到的对象拥有全局作用域,一般来说以下几种情形拥有全局作用域:
(1)最外层函数和在最外层函数外面定义的变量拥有全局作用域
(2)所有末定义直接赋值的变量自动声明为拥有全局作用域
(3)所有window对象的属性拥有全局作用域
- 函数作用域
在函数内声明的变量,在当前函数可访问。
- 块级作用域
Es6增加了let和const声明变量,也使得作用域更加丰富,增加了块级作用域。
总结:在JavaScript执行函数时,遇到变量,先按照“就近”原则在函数内部找该变量的声明或者赋值。若没有找到,则继续向上个函数作用域查找,直到最顶层作用域。整个查找过程层层递进,形成一个链就是作用域链。
2、执行上下文和调用栈
- 执行上下文
执行上下文是当前代码的执行环境/作用域。每个执行环境都有一个与之关联的变量对象(VO),执行环境中定义的所有变量和函数都会保存在这个对象中,解析器在处理数据时就会访问这个内部对象。
JavaScript执行主要分为两个阶段:代码预编译和代码执行阶段。
代码预编译是编译器将JavaScript代码编译成可执行的代码,同时对变量的内存空间进行分配(变量提升过程再此阶段完成),作用域也在该阶段确定。
执行阶段主要任务是执行代码,执行上下文在这个阶段全部创建完成。包括:变量对象、作用域链及this的指向
- 调用栈
在执行一个函数时,如果这个函数又调用了另外一个函数,而这个“另外一个函数”也调用了“另外一个函数”,便形成了一系列的调用栈。
3、闭包
- 什么是闭包?
比较容易理解的版本定义如下:
函数嵌套函数时,内层函数引用了外层函数作用域下的变量,并且内层函数在全局环境下可访问,就形成了闭包
- 为什么会有闭包
由于JavaScript的作用域只能向上层函数访问,内部函数的变量外部函数无法访问(执行完上下文被销毁),为了解决这个问题,出现了闭包。这样外界可以通过这个返回的函数获取原函数内部的变量值。
- 闭包有哪些优点
简单来说闭包为访问函数内部变量提供了途径和便利。这样可以实现“模块化”
- 闭包的缺点
由于闭包使得变量常驻内存,滥用闭包会导致内存的大量消耗,导致内存泄漏问题。
4、内存管理
- 基本概念
内存管理都是指对内存生命周期的管理,包括分配内存空间、读写内存和释放内存空间。
- 内存空间分为:
栈空间:由操作系统自动分配释放,存放函数的参数值,局部变量的值等
堆空间:一般由开发者分配释放,要考虑垃圾回收的问题
一般情况下基本数据类型存放在栈内存中,引用类型保存在堆内存当中
- 内存泄漏
内存泄漏是指内存空间明明已经不再被使用,但由于某种原因并没有被释放的现象
- 浏览器垃圾回收
两种算法实现主动垃圾回收:标记清楚法和引用计数法
垃圾回收优质文章参考:
本文讲解了闭包相关的基本知识,有了这些知识再通过实例练习,相信聪明的你一定可以掌握闭包了。
网友评论