GitHub https://github.com/pengliheng/pengliheng.github.io/issues/80
前言
感觉每一道都可以深入研究下去, 单独写一篇文章, 包括不限于闭包, 原型链, 从 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
深入写下去之前, 先放出类似的代码
同样的执行, 我把函数执行时间放到了前面, 自增失败
- const times = ((times = 0)=> () => times++)()();
- console.log(
- times,
- times,
- times,
- times
- ); // 0,0,0,0
同样的执行, 我把闭包函数执行时间放到了后面, 同样自增失败
- const times = ((times = 0)=> () => times++);
- console.log(
- 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 自动完成了内存分配工作.
- 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 属性的那个对象现在也是零引用了
- // 它可以被垃圾回收了
来源: https://juejin.im/post/5b392f5551882574cd531c8d