当前位置:网站首页>js闭包:函数和其词法环境的绑定
js闭包:函数和其词法环境的绑定
2022-07-26 08:50:00 【weixin_39805244】
闭包是JavaScript的基础概念,在开发中经常出现。那么,闭包是什么?为什么会有闭包?
词法作用域
示例一:
function outerFunc(){
let name='kobe';
function innerFunc(){
console.log(name);
}
innerFunc();
}
outerFunc();
上面这段代码中,在outerFunc()中声明了一个局部变量name和内部函数innerFunc(),innerFunc()内引用了name。执行代码发现,可以正常打印出name的值。
看到这里,我们会有一个疑问:innerFunc()是怎么访问到name的?
我们自然地会想到:首先在innerFunc()内查找name的声明,发现没找到则继续往其外部作用域查找,最终在outerFunc()找到了结果。
这就是词法作用域:根据源代码中声明变量的位置,来确定变量在何处可用;并且内部函数可以访问其外部作用域的变量。
闭包
示例二:
function counterFactory(){
let count=0;
return function counter(){
console.log(count+1);
return ++count;
}
}
let counterInstance=counterFactory();
counterInstance();
这段代码跟示例一是类似的,区别在于:内部函数counter()作为counterFactory()的返回值,是在counterFactory()执行结束后才被调用的。执行代码发现,可以正常实现count值累加。
看到这里,我们会有一个疑问:局部变量count应该是在counterFactory()执行结束就被释放了,但counterInstance()为什么还可以访问到count呢?
这是因为JavaScript函数和它的词法环境是绑定在一起的,也就是闭包。函数被创建时会持有一个其词法环境的引用,词法环境保存了该闭包创建时作用域内的所有局部变量。所以,无论函数在哪里被调,都可以访问到其作用域内的变量。
OOP之封装
在OOP中,用对象封装数据和方法,不让外界直接访问内部数据,而是提供公共的访问方法。虽然JavaScript没有原生支持OOP,但我们可以用闭包来实现封装,从而限制外界对数据的访问。
示例三:
function counterFactory(){
let count = 0;
return {
value: function(){
return count;
},
increment: function(){
return ++count;
},
decrement: function(){
return --count;
}
}
}
let counter = counterFactory();
let otherCounter = counterFactory();
console.log(`counter.value()=${
counter.value()},otherCounter.value()=${
otherCounter.value()}`);
console.log(`counter.increment()=${
counter.increment()},otherCounter.decrement()=${
otherCounter.decrement()}`);
console.log(`counter.value()=${
counter.value()},otherCounter.value()=${
otherCounter.value()}`);
上面这段代码中,counterFactory()定义了一个局部变量count,并且返回了一个计数器对象,该对象包含了三个访问count的公共方法value()、increment()、decrement()。执行代码发现,计数器对象的三个方法可以正常访问count,并且不同的计数器对象之间互不干扰。
看到这里,我们会有一个疑问:为什么同一个计数器对象的三个方法访问的是同一个count?而多次执行counterFactory()创建的计数器对象为什么是相互独立的?
首先,根据上文可知:函数创建时会产生闭包,并且对于同一个计数器对象来说,其三个方法是在同一次counterFactory()调用中创建的,所以它们引用的是同一个词法环境,即同一个count。
然而,多次调用counterFactory()创建的计数器对象,其引用的是不同的词法环境,即不是同一个count。
for循环闭包陷阱
示例四:
function closureTrap(){
var items = [0,1,2];
for(var i=0; i<items.length; ++i){
var item = items[i];
console.log(`current=${
item}`);
setTimeout(function timeoutCallback(){
console.log(`timeout=${
item}`);
},0);
}
}
closureTrap();
上面这段代码中,在一个for循环中设置超时回调timeoutCallback(),打印数组items中的元素item。执行这段代码发现,同步代码current处符合预期结果,但超时回调中timeout处item的值全是2。
看到这里,我们会有一个疑问:为什么回调中打印的item值全是2?
首先,item是用var声明的,由于变量提升,item其实是属于closureTrap()函数作用域的;然后,循环中的timeoutCallback()形成了闭包,并且引用的是同一个词法作用域,即所有timeoutCallback()中引用了同一个变量item;当timeoutCallback()被执行时,for循环已经结束了,此时item值为2。
解决这个问题的一种方法是类似上面工厂函数的例子,给每个回调函数创建独立的闭包:
function closureTrap(){
var items = [0,1,2];
for(var i=0; i<items.length; ++i){
var item = items[i];
console.log(`current=${
item}`);
setTimeout((function factory(){
var localItem = item;
return function timeoutCallback(){
console.log(`timeout=${
localItem}`);
}
})(),0);
}
}
closureTrap();
总结
根据源代码中声明变量的位置来确定变量在何处可用,并且内部函数可以访问其外部作用域,这就是JavaScript的词法作用域。
JavaScript函数跟它的词法作用域是绑定一起的,这就是闭包。每个函数实例被创建时,都持有一个其词法环境的引用。所以,无论函数何时被调用,都可以访问到其作用域。
有了闭包,可以利用其形成的封闭作用域来实现OOP封装,从而限制外界对数据的访问。
边栏推荐
- [abstract base class inheritance, DOM, event - learning summary]
- Media at home and abroad publicize that we should strictly grasp the content
- Kotlin属性与字段
- 12306 ticket system crawling - 1. Saving and reading of city code data
- day06 作业--增删改查
- Dynamic SQL and exceptions of pl/sql
- Okaleido上线聚变Mining模式,OKA通证当下产出的唯一方式
- Learning notes of automatic control principle - Performance Analysis of continuous time system
- Cve-2021-26295 Apache OFBiz deserialization Remote Code Execution Vulnerability recurrence
- Recurrence of SQL injection vulnerability in the foreground of a 60 terminal security management system
猜你喜欢

正则表达式:判断是否符合USD格式

Hegong sky team vision training Day6 - traditional vision, image processing

Deploy prometheus+grafana monitoring platform

One click deployment of lamp and LNMP scripts is worth having

Day06 homework - skill question 6

Day06 operation -- addition, deletion, modification and query
![[encryption weekly] has the encryption market recovered? The cold winter still hasn't thawed out. Take stock of the major events that occurred in the encryption market last week](/img/d8/a367c26b51d9dbaf53bf4fe2a13917.png)
[encryption weekly] has the encryption market recovered? The cold winter still hasn't thawed out. Take stock of the major events that occurred in the encryption market last week

合工大苍穹战队视觉组培训Day6——传统视觉,图像处理

Learning notes of automatic control principle --- linear discrete system

pl/sql之集合
随机推荐
day06 作业--技能题1
Vision Group Training Day5 - machine learning, image recognition project
idea快捷键 alt实现整列操作
Oracle 19C OCP 1z0-082 certification examination question bank (24-29)
pl/sql之集合
C # use npoi to operate Excel
Oracle 19C OCP 1z0-082 certification examination question bank (36-41)
Foundry tutorial: writing scalable smart contracts in various ways (Part 1)
Cve-2021-3156 duplicate of sudo heap overflow privilege raising vulnerability
P1825 [USACO11OPEN]Corn Maze S
Deploy prometheus+grafana monitoring platform
Kept dual machine hot standby
CIS 2020 - alternative skills against cloud WAF (pyn3rd)
Day06 homework - skill question 7
Typescript encryption tool passwordencoder
Cadence (x) wiring skills and precautions
My meeting of OA project (query)
One click deployment of lamp and LNMP scripts is worth having
IC's first global hacking bonus is up to US $6million, helping developers venture into web 3!
Web overview and b/s architecture