从闭包函数的变量自增的角度 - 解析js垃圾回收机制
前言
感觉每一道都可以深入研究下去,单独写一篇文章,包括不限于闭包,原型链,从url输入到页面展示过程,页面优化,react和vue的价值等等。
代码实现
const times = (()=>{ var times = 0; return () => times++; })() console.log( times(), times(), times(), times() ) // 0,1,2,3,复制代码
原理
因为times变量一直被引用,没有被回收,所以,每次自增1。
更简单的实现方式,一行代码实现闭包
const times = ((times = 0)=> () => times++)() console.log( times(), times(), times(), times() ) // 0,1,2,3复制代码
这并非闭包地专利, 变量放在闭包外部同样可以实现阻止变量地垃圾回收机制
let time = 0 const times = ()=>{ let time = 10 return function(){ return time++ } }// 根据JavaScript作用域链地规则,闭包内部没有,就从外面拿变量 const a = times(); // times函数只被执行了1次,产生了一个变量 time console.log( a(), // 而times返回的匿名函数却被执行了5次 a(), // 而times返回的匿名函数却被执行了5次 a(), // 而times返回的匿名函数却被执行了5次 其中的差别相差非常远 a(), // 而times返回的匿名函数却被执行了5次 a() // 而times返回的匿名函数却被执行了5次 ) // 0,1,2,3复制代码
深入写下去之前,先放出类似的代码
同样的执行,我把函数执行时间放到了前面,自增失败
const times = ((times = 0)=> () => times++)()(); 匿名函数只被执行了一次,同时返回函数再次执行一次 console.log( times, // 得到匿名函数返回值, 函数只有配合()才会被执行一次么,此处 times, // 此处没有函数被执行 times, // 因此打印值为四个零 times ); // 0,0,0,0复制代码
同样的执行,我把闭包函数执行时间放到了后面,同样自增失败
const times = ((times = 0)=> () => times++); time相当于声明式函数 console.log( times()(), // 此处外部函数执行一次,产生times变量,返回的函数再执行一次times引用次数为0 times()(), // 此处外部函数执行一次,产生times变量,返回的函数再执行一次 times()(), // 此处外部函数执行一次,产生times变量,返回的函数再执行一次 times()() ); // 0,0,0,0复制代码
函数[1,2,3,4,4].entires()会返回一个迭代器,一下代码同样实现了类似自增1的效果
const arr = [1,2,3,3,5,6,4,78].entries() console.log( arr2.next().value, arr2.next().value, arr2.next().value, arr2.next().value, arr2.next().value ); // [0, 1], [1, 2], [2, 3], [3, 3], [4, 5] 迭代器返回值, 【index,value】复制代码
JavaScript辣鸡回收机制
按照JavaScript里垃圾回收的机制,是从root(全局对象)开始寻找这个对象的引用是否可达,如果引用链断裂,那么这个对象就会回收。换句话说,所有对象都是point关系。引用链就是所谓的指针关系。
当const的过程中,声明的那个函数会被压入调用栈,执行完毕,又没有其他地方引用它,那就会被释放。这个浏览器端,挺难的,但是在nodejs端,就可以用process.memoryUsage()
调用查看内存使用情况。
{ rss: 23560192, // 所有内存占用,包括指令区和堆栈。 heapTotal: 10829824, // "堆"占用的内存,包括用到的和没用到的。 heapUsed: 4977904, // 用到的堆的部分。同时也是判断内存是否泄露的标准。 external: 8608 // V8 引擎内部的 C++ 对象占用的内存。 }复制代码
如果你想要引用,又不想影响垃圾回收机制,那就用WeakMap,WeakSet这种弱引用吧,es6的新属性。
从引用次数来解释为什么变量times没有被回收
const timeFunc = ((time = 0)=> () => time++) var b = timeFunc(); // time 变量引用次数+1,不能被回收 console.log(b()); console.log(b()); console.log(b());复制代码
// 真的非常神奇,需要满足2个条件 // 1.变量命名于返回函数外部,函数函数内部。 // 2.返回函数引用外部变量,导致外部变量无法触发垃圾回收机制。因为引用次数>1 let timeFunc = (time = 0)=>{ return (() => time++)() } var b = timeFunc(); // b变量接受的是timeFunc返回的函数,由于返回函数内部有引用外部变量,故 console.log(b) console.log(b)复制代码
JavaScript中的内存简介(如果缺少必须的基础知识,想要深入了解下去,也是比较难的吧)
像C语言这样的高级语言一般都有低级的内存管理接口,比如 malloc()和free()。另一方面,JavaScript创建变量(对象,字符串等)时分配内存,并且在不再使用它们时“自动”释放。 后一个过程称为垃圾回收。这个“自动”是混乱的根源,并让JavaScript(和其他高级语言)开发者感觉他们可以不关心内存管理。 这是错误的。
闭包的本质
JavaScript闭包的形成原理是基于函数变量作用域链的规则 和 垃圾回收机制的引用计数规则。
JavaScript闭包的本质是内存泄漏,指定内存不释放。
(不过根据内存泄漏的定义是无法使用,无法回收来说,这不是内存泄漏,由于只是无法回收,但是可以使用,为了使用,不让系统回收)
JavaScript闭包的用处,私有变量,获取对应值等,。。
内存生命周期
不管什么程序语言,内存生命周期基本是一致的:
- 分配你所需要的内存
- 使用分配到的内存(读、写)
- 不需要时将其释放\归还
在所有语言中第一和第二部分都很清晰。最后一步在底层语言中很清晰,但是在像JavaScript 等上层语言中,这一步是隐藏的、透明的。
为了不让程序员操心(真的是操碎了心),JavaScript自动完成了内存分配工作。
var n = 123; // 给数值变量分配内存 var s = "azerty"; // 给字符串变量分配内存 var obj = { a: 1, b: null }; // 给对象以及其包含的值分配内存 var arr = [1,null,"abra"]; // 给函数(可调用的对象)分配内存 function f(a){ return a+2 } // 给函数(可调用对象)分配内存 // 为函数表达式也分配一段内存 document.body.addEventListener('scroll', function (){ console.log('123') },false)复制代码
有些函数调用之后会返回一个对象
var data = new Date(); var a = document.createElement('div');复制代码
有些方法是分配新变量或者新对象
var s1 = 'azerty'; // 由于字符串属于引用,所以JavaScript不会为他分配新的内存 var s2 = 's.substr(0,3)'; // s2是一个新的字符串 var a = ["ouais ouais", "nan nan"]; var a2 = ["generation", "nan nan"]; var a3 = a.concat(a2); // 新数组有四个元素,是 a 连接 a2 的结果复制代码
命名变量的过程其实是对内存的写入和释放
辣鸡回收
如上文所述,内存是否仍然被需要是无法判断的,下面将介绍垃圾回收算法以及垃圾回收的局限性
引用
辣鸡回收算法主要依赖于引用的概念。在内存管理的环境中,如果一个对象有访问另一个对象的权限,那么对于属性属于显示引用,对于原型链属于隐式引用。
引用计数垃圾收集
下面是最简单的垃圾回收算法。此算法把“对象是否被需要”简单定义为“该对象没有被其他对象引用到”。
var o = { a: { b: 2 } }; // 两个对象被创建,一个作为另一个的属性被引用,另一个被分配给变量o // 很显然,没有一个可以被作为辣鸡收集 var o2 = o; // o2变量是第二个对“这个对象” o = 1; // 现在这个对象的原始引用o被o2替换了 var oa = o2.a; // 引用“这个对象”的a属性 // 现在,“这个对象”有两个引用了,一个是o2,一个是oa o2 = 'yo'; // 最初的对象现在已经是零引用了 // 它可以被垃圾回收了 // 然而他的属性a还在被调用,所以不能回收 oa = null; // a属性的那个对象现在也是零引用了 // 它可以被垃圾回收了复制代码
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Python 4 种不同的存取文件骚操作
最近开始学习tensorflow框架,选修课让任选一种框架实现mnist手写数字的识别分类。小詹也就随着大流选择了 tf 框架,跟着教程边学边做,小詹用了不同的神经网络实现了识别分类,其中有一个步骤是将训练过程得到的模型进行保存,在之后的测试中加载并使用该模型。想到这种先保存再加载调用的过程,之前很多地方都遇到过呀,最简单常用的就是python中文件的存取哇!于是乎,小詹夜观星象,就着手整理记录各种文件存取的骚操作,具体如下。 (PS:虽然我知道技术文章太长,耐心看完的人很少,曝光率和点赞率会下降,更不会有什么收益,但是还是想记录下自己学习过程中的一些笔记,以后自己或者别人查起来方便些!) ●Python内置方法 ●numpy模块方法 ●os模块方法 ●csv模块方法Pythn内置方法 在不需要借助任何外界库的前提下,python内置方法其实也可以完成我们需要的文件存取任务,这里主要介绍几种python内置方法的使用方式,最后再给出一个实际案例展示: open()方法 file object = open(file_name [, access_mode][, buffering]) ...
- 下一篇
华为快应用引擎技术架构详解
2018 年 3 月华为与小米,Oppo,Vivo 等 9 家手机厂商,联合发布快应用联盟标准。快应用是一种基于手机硬件平台的新型应用形态,无需安装,即点即用,又兼具原生应用体验(性能、系统整合、交互等)。同时,快应用在诞生之初就在开发规范、能力接入、开发者服务等层面实现了手机厂商间的标准化统一,极大地降低开发者的适配成本。 与传统应用相比,快应用具备如下特点: Instant:即点即用,用户无需等待 Everywhere:与手机的使用场景深度整合,入口无处不在(搜索,智能助手,智能推荐,应用市场,浏览器 ……) Efficient:准前端的开发方式,效率高 华为快应用引擎架构简介 上图是快应用的总体框架示意图。最上面是应用形态以及场景入口,中间是快应用引擎,底下是 OS(操作系统) 的基础设施及其硬件。从执行路径层面,有标准的 HTML5 方式支撑通用的 Web 场景(一般通过系统的 Webview 组件或定制的 Webview), 以及 JS(JavaScript)+Native 的方式,支撑更轻量、更快速的体验。 下面将按 3 个层面方面简要介绍快应用引擎的架构。 应用开发(前...
相关文章
文章评论
共有0条评论来说两句吧...