作用域和作用域链的相关知识

这篇具有很好参考价值的文章主要介绍了作用域和作用域链的相关知识。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

作用域

作用域(scope)规定了变量能够被访问的“范围”,离开了这个“范围”变量便不能被访问。
作用域分为:

  • 局部作用域
  • 全局作用域

局部作用域

局部作用域分为函数作用域和块作用域。

函数作用域

在函数内部声明的变量只能在函数内被访问,外部无法直接访问。

function foo(){
    const bar = 1;
}

console.log(bar); // ReferenceError: bar is not defined

总结

  • 函数内部声明的变量,在函数外部无法被访问;
  • 函数的参数也是函数内部的局部变量;
  • 不同函数内部声明的变量无法互相访问;
  • 函数执行完毕后,函数内部的变量实际被清空了。

块作用域

在JavaScript中使用{}包裹的代码称为代码块,代码块内部声明的变量外部将有可能无法被访问。

有可能:取决于使用let还是var

for (let i=1; i<=5; i++){
    // i 只能在该代码块中被访问
    console.log(i); // 正常
}

// 超出了 i 的作用域
console.log(i); // 报错
for (var i=1; i<=5; i++){
    // i 能在该代码块中被访问
    console.log(i); // 正常
}

// 超出了 i 的作用域
console.log(i); // 不会报错,因为 i 是使用var声明的

总结

  • let声明的变量会产生块作用域,var不会产生块作用域;
  • const声明的常量也会产生块作用域;
  • 不同代码块之间的变量无法互相访问;
  • 推荐使用let或const。

全局作用域

<script>标签和.js文件的最外层就是所谓的全局作用域,在此声明的变量在函数内部也可以被访问。
全局作用域中声明的变量,任何其它作用域都可以被访问。

注意文章来源地址https://www.toymoban.com/news/detail-709923.html

  • 为window对象动态添加的属性默认也是全局的,不推荐!
  • 函数中未使用任何关键字声明的变量为全局变量,不推荐!!
  • 尽可能少的声明全局变量,防止全局变量被污染。

作用域链

作用域链本质上是底层的变量查找机制

  • 在函数被执行时,会优先查找当前函数作用域中查找变量;
  • 如果当前作用域查找不到则会依次逐级查找父级作用域直到全局作用域。

总结

  • 嵌套关系的作用域串联起来形成了作用域链
  • 相同作用域链中按着从小到大的规则查找变量
  • 子作用域能够访问父作用域,父级作用域无法访问子级作用域

垃圾回收机制

内存的生命周期

JS环境中分配的内存,一般有如下生命周期

  1. 内存分配:当我们声明变量、函数、对象的时候,系统会自动为他们分配内存
  2. 内存使用:即读写内存,也就是使用变量、函数等
  3. 内存回收:使用完毕,由垃圾回收器自动回收不再使用的内存
// 为变量分配内存
const num = 10;

// 为对象分配内存
const obj = {
    num: 10
}

// 为函数分配内存
function fn(){
    const num = 10;
    console.log(num);
}

说明

  • 全局变量一般不会回收(关闭页面回收)
  • 一般情况下局部变量的值,不用了,会被自动回收

内存泄漏:程序中分配的内存由于某种原因程序未释放无法释放叫做内存泄漏

算法说明

这一部分介绍JS引擎是如何回收内存的。

复习

堆栈空间分配区别:

  1. 栈(操作系统):由操作系统自动分配释放函数的参数值、局部变量等,基本数据类型放到栈里面。
  2. 堆(操作系统):一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。复杂数据类型放到堆里面。

下面介绍两种常见的浏览器垃圾回收算法引用计数法标记清除法

引用计数法

IE采用的引用计数算法,定义“内存不再使用”,就是看一个对象是否有指向它的引用,没有引用了就回收对象
算法

  1. 跟踪记录被引用的次数
  2. 如果被引用了一次,那么就记录次数1,多次引用会累加
  3. 如果减少一个引用就减1
  4. 如果引用次数是0,则释放内存。

引用计数算法是个简单有效的算法,但是现在已经很少使用,因为它存在一个致命的问题:嵌套引用(循环引用)

如果两个对象相互引用,尽管他们已不再使用,垃圾回收器不会进行回收,导致内存泄漏。

function fn(){
    let o1 = {}
    let o2 = {}
    o1.a = o2
    o2.a = o1
    return '引用计数无法回收'
}
fn()

如上图,函数执行结束后,局部变量都被取消,但是由于对象相互引用,内存无法被回收。

并且,每执行一次函数,就会导致一次内存泄漏。

标记清除法

现代的浏览器已经不再使用引用计数算法了。
现代浏览器通用的大多是基于标记清除算法的某些改进算法,总体思想都是一致的。
核心

  1. 标记清除算法将“不再使用的对象”定义为“无法达到的对象”。
  2. 就是从根部(在S中就是全局对象)出发定时扫描内存中的对象。凡是能从根部到达的对象,都是还需要使用的。
  3. 那些无法由根部出发触及到的对象被标记为不再使用,稍后进行回收

如图,标记清除法可以解决引用计数法无法解决的相互引用的问题。

闭包

概念:一个函数对周围状态的引用捆绑在一起,内层函数中访问到其外层函数的作用域
简单理解:闭包 = 内层函数 + 外层函数的变量

function outer(){
    const num = 10;
    function fn(){
        console.log(num);
    }
    fn();
}
outer(); // 10

内函数fn使用了外函数的变量num,形成闭包。

另一个例子:统计函数调用次数,函数调用一次,就加1。

function counter(){
    let num = 0;
    return function(){
        console.log(num++);
    }
}

const add = counter();
add(); // 0
add(); // 1
add(); // 2

闭包作用:封闭数据,提供操作,外部也可以访问函数内部的变量。

闭包应用:实现数据的私有。

变量提升

变量提升是JavaScript中比较“奇怪”的现象,它允许在变量声明之前即被访问(仅存在于var声明变量)

console.log(num); // undefined
var num = 10;

在这个案例中,使用var声明的num会存在变量提升的现象。提前输出不会报错,是因为这个变量已经声明了。虽然声明会被提升,但是赋值操作不会被提升,所以num还是undefined

注意

  1. 变量在未声明即被访问时会报语法错误;
  2. 变量在var声明之前即被访问,变量的值为undefined;
  3. let/const声明的变量不存在变量提升;
  4. 变量提升出现在相同作用域当中;
  5. 实际开发中推荐先声明再访问变量

到了这里,关于作用域和作用域链的相关知识的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 深入理解作用域、作用域链和闭包

     ​ 🎬 岸边的 风 :个人主页  🔥  个人专栏  :《 VUE 》 《 javaScript 》 ⛺️  生活的理想,就是为了理想的生活 ! ​ 目录  📚 前言  📘 1. 词法作用域 📖 1.2 示例 📖 1.3 词法作用域的应用场景  📘 2. 作用域链 📖 2.1 概念 📖 2.2 示例 📖 2.3 作用域链的应用场景  📘

    2024年02月10日
    浏览(28)
  • JS面试题:说一下什么是作用域、作用域链?

    说一下执行上下文的理解?     在 代码执行前 产生     产生变量提升、函数提升的原因     定义:         全局执行上下文对象:在执行全局代码前,创建对应的全局执行上下文对象,即window对象,进行预处理         函数执行上下文对象:在调用函数后、准备执行函数

    2024年01月25日
    浏览(39)
  • C语言变量的作用域,生命周期和链接相关

    本文介绍C语言的三个很重要的概念: 变量的作用域 变量的生命周期 变量或者函数的链接 先介绍一个概念: 翻译单元 C语言中有两种文件, 头文件.h , 代码文件.c 翻译单元指的是 包含头文件,并且将头文件展开以后的代码文件.c ,而每个翻译单元都有一个 文件作用域 ,实

    2024年02月04日
    浏览(36)
  • 【前端|Javascript第3篇】探秘JavaScript的作用域与作用域链:小白也能轻松搞懂!

    大家好!欢迎来到本篇博客,今天我们将解开JavaScript编程世界中的一道神秘面纱:作用域与作用域链。很多Javascript开发者并不真正理解它们,但这些概念对掌握Javascript至关重要。如果你对这些概念感到困惑,不要担心!本文将以通俗易懂的方式,用趣味横生的例子,为你详

    2024年02月13日
    浏览(31)
  • 一、Go基础知识2、iota、匿名变量与变量作用域的小细节

    1、iota是特殊常量,可以理解为是一个可被编译器修改的常量。 2、iota中有一个计数器,会自动加1,自增类型默认是int类型。 3、如果中断了iota则必须显示恢复。参考示例二。 4、iota简化了const类型的定义。 5、每次出现const的时候,iota归零。 示例一: 运行结果: 两个const打

    2024年02月05日
    浏览(27)
  • Javascript作用域 (局部作用域和全局作用域) 详细介绍

    作用域是当前的执行上下文,值和表达式在其中“可见”或可被访问。 常见的作用域为: 全局作用域:脚本模式运行所有代码的默认作用域 函数作用域:由函数创建的作用域 局部作用域:用对象{}包着(一个代码块)创建出来的作用域 在 了解作用域之前先看一下全局变量

    2024年02月08日
    浏览(51)
  • Bean作用域和生命周期

    hi,今天为大家带啦Bean的作用域和生命周期的相关知识 Bean的作用域和我们之前学过的不一样,我们之前学的作用域是一个范围,而现在指的是 Bean在Spring框架中的某种行为模式,也就是一个动作. 这样干巴巴的说看我可能无法理解,我们来举个例子 创建一个公共类的一个公共对象

    2024年02月15日
    浏览(42)
  • Bean 作用域和生命周期

    Spring 容器是用来存储和读取 Bean 的 , 因此 Bean 是 Spring 中最核心的操作资源. 编写代码过程中 , bean 对象如果有多个属性 , 创建 Getter , Setter, 构造方法 等方法 , 会产生大量冗长的代码. 那么为了使代码更加简洁 , 我们可以使用 Lombok 框架 , 只需要一行注释 , 就可以避免大量冗长

    2024年02月05日
    浏览(69)
  • JavaScript(函数,作用域和闭包)

    类似于Java中的方法,是完成特定任务的代码语句块 特点 使用更简单 不用定义属于某个类,直接调用执行 分类 系统函数 自定义函数 1.将字符串转换为整型数字 js示例1 从下标为0起,依次判断每个字符是否可以转换为一个有效数字 如果不是有效数字,则返回NaN,不再继续执

    2024年02月10日
    浏览(29)
  • 关于OC中变量相关知识点

    众所周知,变量是用来存储数据的 围绕着变量,有很多知识点,总结归纳一下 变量的类型 变量的作用区域 局部变量 全局变量 静态变量 变量的访问范围 属性 成员变量 实例变量 synthesize dynamic … 变量大致分为两大类型: 基本数据类型 非基本数据类型(指针/对象/Class类型)

    2024年01月21日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包