JavaScript - 函数 - 闭包作用域和浏览器回收机制

什么闭包

在全局上下文中,在浏览器加载页面会把代码放到栈内存(ECStack)中执行,函数进栈执行会产生一个私有的上下文(EC),此上下文能 保护 里面的私有变量(AO)不受外界干扰,并且如果当前上下文中的某些内容(一般是一个堆)被上下文以外的内容所占用,当前上下文是不会出站释放,这样可以 保存 里面的变量和变量值,我们把函数执行形成私有上下文,来保存和保存私有变量的机制称之为“闭包”,闭包是一种机制。

  1. 创建函数
    • 开辟一个堆内存。
    • 把函数体中的代码当做字符串存储进去。
    • 把堆内存的地址赋值给函数名。
    • 函数在哪儿创建的,那么它执行的时候做需要查找的上级作用域就是谁。
  2. 函数执行
    • 形成一个全新的私有作用域、执行上下文,私有栈内存(执行一次形成一个,多个之间也不会产生影响)。
    • 形参赋值&变量提升。
    • 代码执行(把所属堆内存中的代码字符串拿出来一行一行执行)。
    • 遇到一个变量,首先看它是否为私有变量(形参和在私有作用域中声明的变量),是私有的就操作自己的变量即可,不是私有的则向上级作用域中查找…一直找到全局作用域为止 => 作用域链查找机制
    • 私有变量和外界变量变量没有必然关系,可以理解为被私有栈内存保护起来了,这种机制其实是闭包的保护机制

闭包的两大作用

保护

  • 私有变量和外界没有关系。
  • 划分一个独立的代码执行区域,在这个区域中有自己私有变量存储的空间,而用到的私有变量和其他区域中的变量不会有任何的冲突(防止变量污染)。

保存

  • 形成不销毁的上下文,里面的私有变量等信息保存下来了。
  • 如果上下文不被销毁,那么存储的私有变量的值也不会被销毁,可以被其下级上下文调取使用 。
  • 我们把函数执行形成私有上下文,来保存和保存私有变量的机制称之为“闭包”,闭包是一种机制。

练习题

let a = 0,
	b = 0;
function A(a){
	A = function(b){
		alert(a+b++)
	};
	alert(a++)
};
A(1);
A(2);

图形解析
在这里插入图片描述

关于堆栈内存释放问题

以谷歌webkit内核为例子,函数执行就会形成栈内存【执行上下文/作用域】(从内存中分配出的一块空间),如果内存都不销毁释放,很容易就会导致栈内存溢出(内存爆满,电脑就卡死了),堆栈内存的释放问题是学习JS的核心知识之一。

堆内存释放问题

  • 堆内存:只要有一个引用类型值,就有一个堆内存。
  • 如果当前创建的堆内存不被其他东西占用了(浏览器会在空闲的时候,查找每一个内存的引用状况,不被占用的都会给回收掉释放掉),所以说JS中的内存释放问题是浏览器自己完成的,我们写代码,浏览器帮我们创建一个堆内存,同样的,浏览器也会帮我们把不被占用的内存回收。
let  obj = {name:"zhangsan"};
let oop = obj; 
//此时obj和oop都占用着对象的堆内存,想要释放对内存,需要手动解除变量和值的关系(null:空对象指针,不占内存)
obj = null;
oop = null;

栈内存释放问题

  • 全局上下文:
    • 打开浏览器页面形成的全局作用域是栈内存。
    • 关掉页面的时候才会销毁。
    • F5刷新是把上一次的释放,再形成一个新的。
  • 私有占内存:
    • 手动执行函数形成的私有作用域是栈内存。
    • ES6中的LET/CONST会产生的块级作用域也是栈内存。
    • 默认函数执行完毕,形成的私有栈内存会被销毁释放掉。
    • 一旦栈内存中的某个东西 (一般都是堆内存) 被私有作用域外的事物给占用了,不仅被占内容不能被释放,当前私有栈也不能被释放。
    • 特点:私有上下文(作用域)中的私有变量等信息也保留下来了。
    • 闭包是一种机制。

练习题1:

// 默认函数执行形成栈内存,执行完成栈内存销毁
function fn(){};
 fn(); 
 

function x(){ return function(){} };
// f占用了x执行形成的栈内存中的一个东西(返回小函数对应的堆),则x执行形成的内存不能被释放了。
let f = x(); 
//如果想要X可以被释放,则让f不占用X中的东西(返回小函数对应的堆),断开连接,即可释放X执行形成的栈内存
//这是最佳办法,也可以关联别的类型值,但是都是要占用内存的
f = null;

练习题2:

var i = 5;
function fn(i){// AF0[i]
	return function(){//BF0
		console.log(n+(++i));
	}
};
var fn = fn(1);//return AF0(1)
fn(2);//4
fn(3)(4);//8
fn(5)(6);//12
fn(7);//10
console.log(i);//5

图形解析
在这里插入图片描述

浏览器垃圾回收机制

  • 浏览器垃圾回收机制(GC):浏览器自己会在空闲的时候,把所有未被占用的内容释放掉,以此来优化空间;对于前端开发来讲,应该尽可能减少内存的开辟,并把一些无用的内存取消对其的占用。
  • 常用方法:
    • 引用计数
    • 标记清除

引用计数

  • [谷歌等高版本浏览器是“基于引用查找”来进行垃圾回收的]。
  • 开辟的堆内存,浏览器会在空闲的时候查找所有内存引用,把那些不被引用的释放掉。
  • 开辟的栈内存(上下文)一般在代码执行完都会出站释放,如果遇到上下文中的东西被外部占用,则不会释放。

标记清除

  • [IE浏览器是“基于计数器”机制来进行内存管理的]。
  • 创建的内存被引用一次,则计数1,再被引用一次,计数2… 移除引用减去1…当减为0的时候,浏览器会把内存释放掉。
  • 真实项目中,某些情况导致技术规则会出现一些问题,造成很多内存不能被释放,产生"内存泄漏";
  • 查找引用的方式如果行程相互引用,也会性导致"内存泄漏"。

练习题1

let x = 5;
const fn = function fn(x){
	return function(y){
		console.log(y+(++x));
	};
};
let f = fn(6);
f(7);
fn(8)(9);
f(10);
console.log(x);

答案解析:
在这里插入图片描述
练习题2:

let a = 0,
	b = 0;
let A = function(a){
	A = function(b){
		alert(a+b++);
	};
};
A(1);
A(2);

答案解析:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值