当前位置:网站首页>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封装,从而限制外界对数据的访问。
边栏推荐
- Use index to optimize SQL query "suggestions collection"
- Media at home and abroad publicize that we should strictly grasp the content
- Form form
- Node-v download and application, ES6 module import and export
- TypeScript版Snowflake主键生成器
- Learning notes of automatic control principle - Performance Analysis of continuous time system
- tcp 解决short write问题
- Analysis on the query method and efficiency of Oracle about date type
- Cve-2021-21975 VMware SSRF vulnerability recurrence
- Pan micro e-cology8 foreground SQL injection POC
猜你喜欢
随机推荐
Foundry教程:使用多种方式编写可升级的智能合约(上)
Day06 homework -- skill question 2
Uni app simple mall production
Ueditot_ JSP SSRF vulnerability recurrence
Implementation of Prometheus web authentication and alarm
Excel delete blank lines
Pytoch learning - from tensor to LR
Kotlin properties and fields
Oracle 19C OCP 1z0-082 certification examination question bank (24-29)
Oracle 19C OCP 1z0-082 certification examination question bank (42-50)
《Datawhale熊猫书》出版了!
The effective condition of MySQL joint index and the invalid condition of index
TypeScript版加密工具PasswordEncoder
day06 作业--技能题6
03异常处理,状态保持,请求钩子---04大型项目结构与蓝图
Day 6 summary & database operation
P3743 kotori的设备
sklearn 机器学习基础(线性回归、欠拟合、过拟合、岭回归、模型加载保存)
海内外媒体宣发自媒体发稿要严格把握内容关
Neo eco technology monthly | help developers play smart contracts









