温故而知新:JavaScript作用域和闭包

74 阅读4分钟

前言

🙋‍♂️ 知其然,更知其所以然,举一反三,融会贯通

闭包是js早期谈论的比较多的一个知识点,很多人可能都理解,但是想说清楚却又很难,今天再来温故一下这个知识点,闭包本质是和作用域有着千丝万缕的关联,所以作用域也有必要了解一下。

作用域

概念

定义:作用域是在程序运行时代码中某些特定部分中的变量,函数和对象的可访问性,这是比较专业的定义,换个意思理解,作用域决定了代码区域中变量和其他资源的可见性(是否有权限能被范围到),其实从字面上面理解都比较狭隘,具体可以通过代码层面去深入理解。

分类

目前作用域一共可以分为3类

全局作用域

简单理解是代码中任何地方都能访问到的对象拥有全局作用域, ,例如网页中window对象是就一个全局对象,或者在script脚本中定义的变量,其后续代码都可以访问到这个变量,那么在script脚本内那个变量就具有全局作用域。

一般情况下我们尽量避免去声明全局变量,一方面是容易造成变量污染,另一方面声明太多可能会造成内存泄漏。

<script>
    const title = 'hello world'
    function getTitle() {
        console.log(title) // hello world
    }
    console.log('title')
    getTitle() // hello world
</script>

函数作用域

一般指在函数内定义的变量对象,跟全局作用域相比,影响的范围更小,只能在函数内部被访问到,在函数外部访问不到,函数执行完毕之后,内部的变量对象就会被释放掉

<script>
    function getTitle() {
        var title = 'hello world'
        console.log(title) 
    }
    function getValue() {
        console.log(title) 
    }
    getTitle()  // hello world
    getValue() // ReferenceError: title is not defined
    console.log('title') // ReferenceError: title is not defined
</script>

块级作用域

ES6中新增的作用域,用中括号{...}包裹的代码块,并且使用let或const声明的变量对象具有块级作用域,只能在中括号{}内部区域被访问到

<script>
    {
     var title = 'hello world'
    }
    console.log(title) // hello world
</script>

如果在中括号中使用var声明变量,不具有块级作用域

<script>
    {
     let title = 'hello world'
    }
    console.log(title) // ReferenceError: title is not defined
</script>

作用域链

作用域链与原型链的概念有点类似,js程序执行读取变量时,会先在自身所处的作用域去查找变量,如果找不到,就会去上层的作用域查找,这样一步一步的找到顶层的全局作用域,如果还是访问不到,就会返回undefined

闭包

闭包是一个比较专业的术语(计算机用语),单从字面语义或结合JS而言很难去理解,这对初学js的开发者来说是最迷糊的。

闭包定义:能够读取(有权限访问)其他函数内部变量的函数,这是大多数闭包相关资料中的定义,其实也是很抽象的, 另一种说法是,一个函数嵌套另一个函数,执行函数时返回嵌套的函数,嵌套的函数中有对外部函数变量的引用。

function myFun() {
    var total = 1
    return function() {
        return total++
    }
}
const fun = myFun()
console.log(fun()) // 1
console.log(fun()) // 2

从上面例子可以看出,total变量为myFun的局部变量,在函数外部是访问不到的,但是通过返回一个函数的形式,就可以对它进行访问和操作了,那么闭包形成的条件是什么呢?

  • 在函数内部返回一个引用类型,可能是对象,数组或函数,大多数情况下是函数
  • 返回的引用类型(对象,数组或函数),对外部函数中定义的局部变量进行了引用(使用)
  • 在函数外部,有变量来引用函数返回的引用类型,通过变量就可以读取和操作内部的变量了

闭包的作用

  • 让函数内的局部变量能长期保存在内存中,不被销毁
  • 形成私有的作用域,避免全局变量的污染,闭包中的变量无法在外部为访问到,只能在闭包内部调用访问

闭包的缺点

说的最多的是内存泄漏吧,因为形成闭包后,局部变量不会被垃圾回收,导致一直存在内存中,要注意手动释放内存,我觉得能有多少变量,造成泄漏的可能性不大😹