js逆向基础5闭包

闭包

 

在理解闭包之前,有个重要的概念需要先了解一下,就是 js 执行上下文

 

JS代码在执行前,JS引擎总要做一番准备工作,这份工作其实就是创建对应的执行上下文;

function f1() {console.log('anlan');};f1();function f1() {console.log('noob');};f1();

b727993c65c146bc99c2b8bf25f2c7ef.png 

这说明代码在执行前一定发生了某些微妙的变化,我们都知道它叫变量提升。那JS引擎究竟做了什么呢

执行上下文有且只有三类,全局执行上下文,函数上下文,与eval上下文

全局执行上下文只有一个,在客户端中一般由浏览器创建,也就是我们熟知的window对象,我们能通过this直接访问到它。

全局对象window上预定义了大量的方法和属性,我们在全局环境的任意处都能直接访问这些属性方法,同时window对象还是var声明的全局变量的载体。我们通过var创建的全局对象,都可以通过window直接访问。

函数执行上下文可存在无数个,每当一个函数被调用时都会创建一个函数上下文;需要注意的是,同一个函数被多次调用,都会创建一个新的上下文。

eval函数执行上下文:执行在eval函数内部的函数也有自己的函数上下文

 

执行上下文栈也叫调用栈,当js解释器运行的时候,执行栈就会用于存储代码执行期间创建的所有上下文,具有LIFO(Last In First Out后进先出,也就是先进后出)的特性。U型

JS代码首次运行,都会先创建一个全局执行上下文并压入到执行栈中,之后每当有函数被调用,都会创建一个新的函数执行上下文并压入栈内;由于执行栈LIFO的特性,所以可以理解为,JS代码执行完毕前在执行栈底部永远有个全局执行上下文。

 

function f1() {f2();console.log(1);};function f2() {f3();console.log(2);};function f3() {console.log(3);};f1()

f1入栈 ---> f2入栈 ----> f3入栈  -----> f3 出栈 -----> f2 出栈 -----> f1 出栈

03dd7af09be149a3a23a902e18725976.png

 

那么我们现在已经在一定程度上了解了上下文,那当一个函数创建的时候究竟发生了什么呢?

1. JavaScript创建一个新的函数执行上下文

2. 这个执行上下文将有它自己的一组变量,这些变量将是这个执行上下文的本地变量。

3. 新的执行上下文被推到到执行堆栈中

4. 直到遇到return语句或一个结束括号},函数执行结束

当函数结束时:

1. 弹出执行栈

2.函数将返回值返回调用上下文

3.这个本地函数执行上下文被销毁

 

闭包中最简单的例子

 

例1 :

let a = 3;let b = addTwo(a);console.log(b);function addTwo(x) {let ret = x + 2;return ret;};

我们在函数执行上下文中有变量,在全局执行上下文中有变量。JavaScript的一个复杂之处在于它如何查找变量,沿着作用域链查找而不是严格按照上下文的方式查找。

 

真正的闭包

function createCounter() {

let counter = 0

const myFunction = function() {

counter = counter + 1

return counter

}

return myFunction

}

const increment = createCounter()

const c1 = increment();const c2 = increment();const c3 = increment();

console.log('result', c1, c2, c3)

 

const myFunction = function()在这个函数创建的时候会把所有变量放进myFunction这个函数中闭包背包中去

const c2 = increment();再去执行这个函数的时候,值变成了2,原先应该被销毁,但是因为闭包,把值存进去啦

怎么看背包,打断点,然后作用,闭包作用域,如上图所述

 

无论何时声明新函数并将其赋值给变量,都要存储函数定义和闭包。闭包包含在函数创建时作用域中的所有变量,它类似于背包。函数定义附带一个小背包,它的包中存储了函数定义创建时作用域中的所有变量。

 

在全局作用域中创建的函数创建闭包,但是由于这些函数是在全局作用域中创建的,所以它们可以访问全局作用域中的所有变量,闭包的概念并不重要。

当函数返回函数时,闭包的概念就变得更加重要了。返回的函数可以访问不属于全局作用域的变量,但它们仅存在于其闭包中。

闭包的方法是通过背包的类比。当一个函数被创建并传递或从另一个函数返回时,它会携带一个背包。背包中是函数声明时作用域内的所有变量。

 

注意事项:容易导致内存泄漏。闭包会携带包含其它的函数作用域,因此会比其他函数占用更多的内存。过度使用闭包会导致内存占用过多,所以要谨慎使用闭包。

 

MDN对闭包的官方解释:

A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function’s scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.

闭包是绑定在一起(封闭)的函数及其周围状态(词汇环境)的引用的组合。换句话说,闭包允许您从内部函数访问外部函数的scope(作用域)。在JavaScript中,每次创建函数时,都会在函数创建时创建闭包。

 

闭包绝不可以理解为在函数里返回函数的函数。这是引发闭包问题的原因,而不是闭包概念的本身

 

什么情况下会产生闭包现象?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值