理解闭包

反射苔原
• 阅读 1056

知识小储备

ECMAScript 的数据有两种类型:基本类型值和引用类型值,基本类型指的是简单的数据段,引用类型指的是可能由多个值构成的对象。

Undefined、Null、Boolean、Number 和 String 是值类型,其他都是引用类型。

垃圾回收

我们创建的原始类型、对象、函数等等,都会占用内存。为了防止溢出,我们就需要对不用的数据进行删除。这就是垃圾回收。

可触及(Reachability)

JavaScript 内存管理的关键概念是可触及(Reachability)。
我的理解就是还处于被引用状态。
将全局(无论是window还是global)比作树根,我们创建的原始类型、对象、函数等等比作一个个枝杈。如果可以从window不断层的知道某个变量,那这个变量就是可触及的,不可回收的。

举个例子:

// user has a reference to the object
let user = {
  name: "John"
};

理解闭包

箭头代表的是对象引用。全局变量 "user" 引用了对象{name: "John"}(简称此对象为 John)。John 的 "name" 属性储存的是一个原始值,所以无其他引用。

如果覆盖 user,对 John 的引用就丢失了:

user = null;

理解闭包

现在 John 变得不可触及,垃圾回收机制会将其删除并释放内存。

内存机制

js的内存空间分为栈 (stack)、堆 (heap);其中栈存放变量,堆存放复杂对象。
借用一张图直观感受一下
理解闭包

栈内存

只能存放基本数据类型的数据和对象类型的引用地址也叫哈希码。里面的数据后进先出。
对栈内数据进行复制修改时:
理解闭包

堆内存

是用来存储 “数组类型” 和“对象类”的数据。特点是存储空间大。
对堆内数据进行复制修改时:
理解闭包

理解闭包

有了前面的铺垫,我们再来看看闭包是怎么回事。还是举个例子:

//决策层开会决定生产新一代phone手机,就弄了个叫PhoneFactory的企划案
let Proposal = function(){
    //新一代手机信息被封装在企划案中
    let  version = "XX", money = 10000
    return function (){
        //生产新的手机
        return {
            version: version,
            money: money
        }
    }
}
//执行者根据策划案建成了一个工厂, 工厂方法每执行一次就产出一个手机
let Factory = new Proposal();
let phone1 = Factory();
console.log(phone1.version)

对于手机的version, money而言, 它是策划书Proposal的内部变量,而Proposal的同级phone1应该是访问不到。但实际上我们还是拿到了手机的版本和价格数据。这种反常的现象我们就叫它Closure,中文名闭包。

原因

我们不管它为什么叫这个名字,先看看具体是什么原因产生的。
根据上面说的垃圾回收机制。函数Proposal在执行过之后(第16行)就没有引用。那么Proposal久应该被回收。里面的所有内部变量也应该被回收了。
但实际上 Proposal返回了一个新的函数Factory。而这个Factory是要能够访问到它生成时的同级以及父祖辈变量。而Proposal内部变量version, money就有了新的引用。因而阻止了被回收。就像Factory生成了一个新的泡泡把它能访问到的作用域包裹了起来。这就是闭包形成的原因了。

基于上面的js内存机制的知识,我们可以画出下面这张图:
理解闭包

图简陋了点。。。。
但也可以看出,对于变量version,money而言。虽然他们本身在proposal的黄色作用域中。但也在fatory生成的时候也被包含在了fatory打的可访问的作用域气泡内。不仅他们,甚至更外层的也都被包含在内。
在proposal这个泡泡破碎之后,只有当打的红色泡泡也破了,这些变量才会真的被回收。这也是为什么闭包用多了会影响性能的原因。

引用:

前端基础进阶:详细图解 JavaScript 内存空间
垃圾回收

点赞
收藏
评论区
推荐文章
仔细看看,会有收获。js深浅拷贝
好好理解深浅拷贝和赋值(针对引用类型)赋值:两个对象指向同一内存地址。结果,无论是修改基本类型还是引用类型,两个对象的值都会改变。浅拷贝:两个对象指向不同的内存地址,但是他们中的引用类型数据指向同一内存地址。结果,修改引用类型,两个对象的值都会改变;修改基本类型,互不影响。深拷贝:两个对象指向不同的内存地址,他们中的引用类型也指向不同的内存地址。结果,均互不
菜园前端 菜园前端
2年前
你了解JavaScript中的数据类型区分吗
原文链接:常见的ES5数据类型分为基本数据类型、引用数据类型两种。包含字符串、数字、对象、数组、函数、布尔值、空值、未知。基本数据类型String类型(字符串)javascriptvarname'xiaoming'Number类型(数字)javascrip
LinMeng LinMeng
4年前
js之传值与传址/undefined和null/严格模式
传值与传址基本数据类型有五种Undefined、Null、Boolean、Number和String引用数据类型有两种object,array,fn两种数据类型的区别:1.存储位置不同原始数据类型直接存储在栈(stack)中简单数据段,占据空间小,大小固定,属于被频繁使用的数据,所以存储在栈中;引用数据类型直接存
待兔 待兔
4年前
[Dart]Dart语言之旅<二>:变量
变量以下是创建变量并为其分配值的示例:varname'Bob';变量是引用。名为name的变量包含对值为“Bob”的String类型的对象的引用。默认值未初始化的变量的初始值为null。即使是数字类型的变量,初始值也为null,因为数字也是对象。intlineCount;assert(lineCountnull)
劳伦斯 劳伦斯
4年前
前端面试题自检 JS CSS 部分
JS类型JavaScript的简单数据类型Number,String,Boolean,Undefined,Null,Symboltypeof操作符的返回值numberstringbooleanundefinedobjectfunction
Wesley13 Wesley13
3年前
Java对象的浅拷贝和深拷贝&&String类型的赋值
Java中的数据类型分为基本数据类型和引用数据类型。对于这两种数据类型,在进行赋值操作、方法传参或返回值时,会有值传递和引用(地址)传递的差别。浅拷贝(ShallowCopy):①对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象。因为是两份不同的数据,所以对其中一个对象的该成员变量值进行修改,
Stella981 Stella981
3年前
JavaScript的深拷贝和浅拷贝
一、数据类型数据分为基本数据类型(String,Number,Boolean,Null,Undefined,Symbol)和对象数据类型。、1.基本数据类型的特点:直接存储在栈(stack)中的数据2.引用数据类型的特点:存储的是该对象在栈中引用,真实的数据放在堆内存里。引用数据类型在栈中存储了指针,该指针指向堆中该实
Wesley13 Wesley13
3年前
GO值类型与引用类型
值类型值类型包括基本数据类型,int,float,bool,string,以及数组和结构体(struct)。值类型变量声明后,不管是否已经赋值,编译器为其分配内存,此时该值存储于栈上。值类型的默认值:varaint//int类型默认值为0varbstring//string类型默认值为n
Stella981 Stella981
3年前
JavaScript学习小结
JavaScirpt变量可用来保存两种类型值:基本类型值,引用类型值基本类型值:Undefined,Null,Boolean,Number,String基本类型及引用类型值特点:1.基本类型值在内存中占据固定大小的空间,被保存在栈内存中;2.从一个变量向另一个变量复制基本类型值,会创建这个值的一个副本;
Stella981 Stella981
3年前
JVM调优总结一
数据类型   Java虚拟机中,数据类型可以分为两类:基本类型和引用类型。基本类型的变量保存原始值,即:他代表的值就是数值本身;而引用类型的变量保存引用值。“引用值”代表了某个对象的引用,而不是对象本身,对象本身存放在这个引用值所表示的地址的位置。基本类型包括:byte,short,int,long,cha
Stella981 Stella981
3年前
JVM调优总结(一)基本概念
数据类型Java虚拟机中,数据类型可以分为两类:基本类型和引用类型。    基本类型:保存原始值,即:他代表的值就是数值本身;    引用类型:保存引用值。“引用值”代表了某个对象的引用,而不是对象本身,对象本身存放在这个引
反射苔原
反射苔原
Lv1
道理只会告诉你对错但未必能给你幸福.
文章
4
粉丝
0
获赞
0