JavaScript(单线程,调用栈,任务队列)







<!--

浏览器垃圾回收制:
    1----引用计数算法:

    2----标记清除算法:

    将不再使用的对象   ----   定义为无法到达的对象!!!

    即从根部(在JS中就是全局对象)出发定时扫描内存中的对象,凡是能从根部到达的对象,保留。那些从根部出发无法触及到的对象被标记为不再使用,稍后进行回收。

    无法触及的对象包含了没有引用的对象这个概念,但反之未必成立。

    所以上面的例子就可以正确被垃圾回收处理了。

    所以现在对于主流浏览器来说,只需要切断需要回收的对象与根部的联系。最常见的内存泄露一般都与DOM元素绑定有关:

    a = null 其实仅仅只是做了一个释放引用的操作,让 a 原本对应的值失去引用,脱离执行环境,这个值会在下一次垃圾收集器执行操作时被找到并释放。
    
    而在适当的时候解除引用,是为页面获得更好性能的一个重要方式。

    实际上是将变量脱离执行环境,断开引用的一种操作!!!



首先JavaScript在被浏览器执行的时候,会分为三个阶段:

    1、定义阶段,在内存当中定义一个,执行环境栈(Global main),将window对象(全局执行上下文 或者 全局作用域)放入执行栈当中。
    2、生成活动对象AO(active Object)scope执行栈,函数执行的时候,会将函数执行上下文(局部作用域)push到活动对象当中,活动对象遵循先进后出的原则,
    当函数执行完成以后会进行出栈操作(失去引用关系,会被下次垃圾回收机制回收。),每次执行函数的时候创建的函数执行上下文都是最新的!!!

    Javascript被浏览器执行为什么是单线程?

        JavaScript 是浏览器脚本语言,它可以操纵 DOM ,可以渲染动画,可以与用户进行互动,

        如果是多线程,执行顺序无法预知,操作以哪个线程很难预知。

        单线程是JavaScript脚本语言的核心特征。

        补充:
            在 HTML5 时代,浏览器为了充分发挥 CPU 性能优势,允许 JavaScript 创建多个线程,
            但是即使能额外创建线程,这些子线程仍然是受到主线程控制,而且不得操作 DOM,------类似于开辟一个线程来运算复杂性任务,
            运算好了通知主线程运算完毕,结果给你(主线程),这类似于异步的处理方式,所以本质上并没有改变 JavaScript 单线程的本质。


            函数调用栈    ---------------------------------------------    任务队列

            JavaScript 只有一个主线程和一个调用栈(call stack)。      所有任务可以分成两种,一种是 同步任务(synchronous),

            代码执行,进栈,执行完毕,出栈。                           另一种是 异步任务(asynchronous) 。

----------------------------------------------------

                    什么是同步任务和异步任务?

                    同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。

                    异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有 "任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

                    所以,当在执行过程中遇到一些类似于 setTimeout 等异步操作的时候,会交给浏览器的其他模块进行处理,

                    当到达 setTimeout 指定的延时执行的时间之后,回调函数会放入到任务队列之中。

                    当然,一般不同的异步任务的回调函数会放入不同的任务队列之中。等到调用栈中所有任务执行完毕之后,接着去执行任务队列之中的回调函数。

            什么是事件循环?:

                调用栈顺序调用任务----------------------(往调用栈里面push任务,顺序执行同步任务)

                当调用栈发现异步任务时,将异步任务交给其他模块处理,自己继续进行下面的调用

                异步执行完毕,异步模块将任务推入任务队列,并通知调用栈

                调用栈在执行完当前任务后,将执行任务队列里的任务

                调用栈执行完任务队列里的任务之后,继续执行其他任务

                -----------------这一整个流程就叫做 事件循环(Event Loop)。


                价值非常高解释调用栈---任务队列
                 for (var i = 0; i < 10; i++) {
                    setTimeout(() => {
                    console.log(i)
                    }, 1000)
                }
                console.log(i)
                
                复制代码解析:

                首先由于 var 的变量提升,i 在全局作用域都有效
                再次,代码遇到 setTimeout 之后,将该函数交给其他模块处理,自己继续执行 console.log(i) ,由于变量提升,i 已经循环10次,此时 i 的值为 10 ,即,输出 10
                之后,异步模块处理好函数之后,将回调推入任务队列,并通知调用栈
                1秒之后,调用栈顺序执行回调函数,由于此时 i 已经变成 10 ,即输出10次 10


-----------------------------------------------------

引发问题:

    全局作用域不是包含局部作用域吗?
    不矛盾。

什么叫内存泄漏?:

    (对于持续运行的服务进程(daemon),必须及时释放不再用到的内存。否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃。)

    对于不再用到的内存,没有及时释放,就叫做内存泄漏(memory leak)




总结:
    总之,变量之间的引用关系,变量是否存在于执行环境中是重点!!!(闭包原理亦如此!!!)




    eg:
        var div = document.createElement("div");
        div.onclick = function() {
            console.log("click");
        };
    注释:
        变量div有事件处理函数的引用,同时事件处理函数也有div的引用!(div变量可在函数内被访问)。-----(细细的品)
        -----IE采用的是引用计数算法,因此这里存在内存泄漏。



-->

猜你喜欢

转载自www.cnblogs.com/swt-axios/p/12699047.html