JS基础知识(作用域/垃圾管理)
1.js没有块级作用域
if (true) { var color = “blue”; } alert(color); //”blue” for (var i=0; i < 10; i++){ doSomething(i); } alert(i); //10
变量定义:var和没有var
function add(num1, num2) { var sum = num1 + num2; return sum; } var result = add(10, 20); //30 alert(sum); //causes an error since sum is not a valid variable
function add(num1, num2) { sum = num1 + num2; return sum; } var result = add(10, 20); //30 alert(sum); //30 var scope = "global"; function f() { alert(scope); //显示undefined var scope = "local"; alert(scope); } f();
3,垃圾回收
垃圾回收机制最困难的工作在于找出什么时候“不再需要某个已分配的内存了”。这常常需要开发者去决定在程序的什么位置不再需要某个内存然后释放它。
高阶语言的解释器包含一个称为“垃圾收集器”的软件,它的工作是追踪内存分配和使用,以便于在不再需要某个已分配的内存时发现,并自动释放它。这个过程值是近似的,因为通常问题在于某段内存是否还会被使用是无法确定的。
两种垃圾回收机制:
1.引用计数(reference counting)
跟踪记录每个值的被引用次数。声明一个变量并将一个引用类型值赋给该变量时,这个值得引用次数就是 1。如果同一个值又被赋给另外一个变量,则该值的引用次数加 1。相反,如果包含对这个值的引用的变量又取得另外一个值,这个值得引用次数减 1。当这个值得引用次数为 0 时,则说明没有办法再访问到此值,因此就可以将其占用的内存空间回收。当垃圾收集器在下一个周期运行时,会释放引用次数为零的值所占用的内存空间。
var o = { a: { b:2 } }; //2个对象被创建。其中一个作为另一个的属性而被引用。 //后者通过赋值给变量‘o’而被引用。 //很明显,它们都不能被垃圾收集。 var o2 = o; //变量‘o2’是第二个引用这个对象的 o = 1; //现在,起始于'o'的对象通过变量'o2'产生了一个独立的引用 var oa = o2.a; //这个对象现在有2个引用:一个作为属性,另一个作为变量'oa'。 o2 = "yo"; //现在,没有任何引用指向起始于'o'的对象 //然而,最初作为属性'a'的对象仍然被变量'oa'引用着,因此它不能被释放 oa = null; //当'oa'重新赋值后,最初属性'a'指向的对象不再被引用
循环引用导致内存泄漏
Netscape Navigator 3.0是最早使用引用计数策略的浏览器,但很快它就遇到了一个严重的问题:循环引用。循环引用指的是对象A中包含一个指向对象B的引用,而对象B中也包含一个指向对象A的引用。请看下面例子:
function () { var objectA = new Object(); var objectB = new Object(); objectA.someOtherObject = objectB; objectB.anotherObject = objectA; }
在这个例子中,objectA和objectB通过各自的属性相互引用,也就是说,这两个对象的引用次数都是2。在采用引标记清除略的实现中,由于函数执 行之后,这两个对象都离开了作用域。因此这两种相互引用不是个问题。但在采用引用计数策略的实现中,但函数执行完毕后,objectA和objectB还 将继续存在,因此他们的引用次数永远不会是0。假如这个函数被重复调用,就会导致大量的内存得不到回收。因此,Netscape在Navigator 4.0中放弃了引用计数器方式,转而采用标记清除来实现对其垃圾回收机制。可是,引用计数导致的麻烦并未就此终结。
2.标记清除(make-and-sweep)
每个变量都有其运行环境,变量创建后会在某种环境中运行,比如创建一个局部变量,局部变量会运行在函数体内。当函数运行时,会标记局部变 量为“进入环境”,当函数体运行结束后,意味着变量脱离了其运行环境,此时则将变量标记为“离开环境”。对于“离开环境”的变量,垃圾收集机制会进行相应 记录,并且在下一个回收周期时将其释放。
也就是说,一个变量是否符合被回收的资格,取决于这个变量所在的环境,只要依然在这个环境中执行,变量就也不会被回收,哪怕这个变量已经是垃圾变量。
这个算法将定义”一个对象不再被需要“缩小为”一个对象不能被到达“。
这个算法假设一组称为roots的对象(在Javascript中,root是全局对象)。垃圾收集器会定期地从roots开始查找所有被roots引用的对象,然后是所有被这些对象引用的对象,以此类推。由于是从roots开始,因此垃圾收集器将找到所有可以到达的对象,并收集所有不可到达的对象。
这个算法优于前一个,因为当”一个对象没有任何引用指向它“时必然使得这个对象不能被到达。但是相反则不成立,正如我们从循环引用中看到的那样。
截 止到2012,所有的现代浏览器都使用了标记-扫描式的垃圾收集器。在过去几年中,Javascript垃圾回收领域的所有改进(世代/增量/并发/并行 的垃圾收集)都是对这一算法的改进实现,而不是对垃圾回收算法本身的改进,也不是对它的定义”当一个对象不再被需要“的缩减。
-------------------------
最后是不是要说作为开发者 如今基本不用关心垃圾回收问题呢?
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· [AI/GPT/综述] AI Agent的设计模式综述