广州蓝景分享 — 前端闭包原理以及使用场景

2022-06-23 15:15:02   阅读:221 次  点赞:116 次  鄙视:75 次  收藏:0 次  由 campingchina.cc 收集整理
分享到:
关闭
听新闻 - 广州蓝景分享 — 前端闭包原理以及使用场景
00:00 / 00:00

-

+

语速: 慢速 默认 快速
- 6 +
自动播放×

御姐音

大叔音

萝莉音

型男音

温馨提示:
是否自动播放到下一条内容?
立即播放当前内容?
确定
确定
取消

原标题:广州蓝景分享 — 前端闭包原理以及使用场景

Hello~~各位编程爱好者,今天广州蓝景继续与大家分享一些前端技术干货:闭包原理的使用场景。

前端面试,必问闭包

闭包有多重要?如果你是初入前端的朋友,我没有办法直观的告诉你闭包在实际开发中的无处不在,但是我可以告诉你,前端面试,必问闭包。面试官们常常用对闭包的了解程度来判定面试者的基础水平,保守估计,10个前端面试者,至少5个都死在闭包上。

讲解闭包时,我们先来看个例子

var n = 999;

function f1() {

console.log(n);

}

f1(); // 999

上面代码中,函数f1可以读取全局变量n。但是下面例子,函数外部无法读取函数内部声明的变量。

function f1() {

var n = 999;

}

console.log(n);

// Uncaught ReferenceError: n is not defined

展开全文

上面代码中,函数f1内部声明的变量n,函数外是无法读取的.

如果有时需要得到函数内的局部变量。正常情况下,这是办不到的,只有通过变通方法才能实现。那就是在函数的内部,再定义一个函数。

function f1() {

var n = 999;

function f2() {

console.log(n); // 999

}

}

上面代码中,函数f2就在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。既然f2可以读取f1的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!

闭包是什么

闭包是一种特殊的对象。

它由两部分组成。执行上下文(代号A),以及在该执行上下文中创建的函数(代号B)。

当B执行时,如果访问了A中变量对象中的值,那么闭包就会产生。

在大多数理解中,包括许多著名的书籍,文章里都以函数B的名字代指这里生成的闭包。而在chrome中,则以执行上下文A的函数名代指闭包。

我们可以对上面代码进行如下修改:

function f1(){

var a = 999;

function f2(){

console.log(a);

}

return f2; // f1返回了f2的引用

}

var result = f1(); // result就是f2函数了

result(); // 执行result,全局作用域下没有a的定义,

//但是函数闭包,能够把定义函数的时候的作用域一起记住,输出999

上面的例子,首先有执行上下文f1,在f1中定义了函数f2,而通过对外返回f2的方式让f2得以执行。当f2执行时,访问了f1内部的变量a。因此这个时候闭包产生

闭包就是函数f1,

在上面的图中,红色箭头所指的正是闭包。其中Call Stack为当前的函数调用栈,Scope为当前正在被执行的函数的作用域链,Local为当前的局部变量。

JavaScript拥有自动的垃圾回收机制,关于垃圾回收机制,有一个重要的行为,那就是,当一个值,在内存中失去引用时,垃圾回收机制会根据特殊的算法找到它,并将其回收,释放内存。

而我们知道,函数的执行上下文,在执行完毕之后,生命周期结束,那么该函数的执行上下文就会失去引用。其占用的内存空间很快就会被垃圾回收器释放。可是闭包的存在,会阻止这一过程。

我们再来看个例子:

var fn = null;

function foo() {

var a = 2;

function innnerFoo() {

console.log(a);

}

fn = innnerFoo; // 将 innnerFoo的引用,赋值给全局变量中的fn

}

function bar() {

fn(); // 此处的保留的innerFoo的引用

}

foo();

bar(); // 2

在上面的例子中,foo()执行完毕之后,按照常理,其执行环境生命周期会结束,所占内存被垃圾收集器释放。但是通过fn = innerFoo,函数innerFoo的引用被保留了下来,复制给了全局变量fn。这个行为,导致了foo的变量对象,也被保留了下来。于是,函数fn在函数bar内部执行时,依然可以访问这个被保留下来的变量对象。所以此刻仍然能够访问到变量a的值。

这样,我们就可以称foo为闭包。

所以,通过闭包,我们可以在其他的执行上下文中,访问到函数的内部变量。比如在上面的例子中,我们在函数bar的执行环境中访问到了函数foo的a变量。个人认为,从应用层面,这是闭包最重要的特性。利用这个特性,我们可以实现很多有意思的东西。

不过读者朋友们需要注意的是,虽然例子中的闭包被保存在了全局变量中,但是闭包的作用域链并不会发生任何改变。在闭包中,能访问到的变量,仍然是作用域链上能够查询到的变量。

对上面的例子稍作修改,如果我们在函数bar中声明一个变量c,并在闭包fn中试图访问该变量,运行结果会抛出错误。

var fn = null;

function foo() {

var a = 2;

function innnerFoo() {

console.log(c); // 在这里,试图访问函数bar中的c变量,会抛出错误

console.log(a);

}

fn = innnerFoo; // 将 innnerFoo的引用,赋值给全局变量中的fn

}

function bar() {

var c = 100;

fn(); // 此处的保留的innerFoo的引用

}

foo();

bar();

闭包形成的条件

  • 函数嵌套内部函数引用外部函数的局部变量

闭包的应用场景

初级前端了解以上闭包面试即可,以下内容属于中高级前端理解范围。大家可以暂时跳过,等学有一定基础积累,再回头看看。

除了面试,在实践中,闭包有两个非常重要的应用场景。分别是模块化与柯里化。

柯里化

模块化

在我看来,模块是闭包最强大的一个应用场景。如果你是初学者,对于模块的了解可以暂时不用放在心上,因为理解模块需要更多的基础知识。但是如果你已经有了很多JavaScript的使用经验,在彻底了解了闭包之后,不妨借助本文介绍的作用域链与闭包的思路,重新理一理关于模块的知识。这对于我们理解各种各样的设计模式具有莫大的帮助。

(function () {

var a = 10;

var b = 20;

function add(num1, num2) {

var num1 = !!num1 ? num1 : a;

var num2 = !!num2 ? num2 : b;

return num1 + num2;

}

window.add = add;

})();

add(10, 20);

在上面的例子中,我使用函数自执行的方式,创建了一个模块。add是模块对外暴露的一个公共方法。而变量a,b被作为私有变量。

为了验证自己有没有搞懂作用域链与闭包,这里留下一个经典的思考题,常常也会在面试中被问到。

利用闭包,修改下面的代码,让循环输出的结果依次为1, 2, 3, 4, 5

for (var i=1; i<=5; i++) {

setTimeout( function timer() {

console.log(i);

}, i*1000 );

}

广州蓝景“好的经验得到传播”,想要了解更多前端技术学习,可以关注我们。

Tags标签
加入收藏夹(0 点赞一下(116 鄙视一下(75
发表评论
内容:
剩余字数:360/360


     :: 正在为您加载评论……


每页10条,共0

相关文章

play
next
close
X

Copyright © 2021-2022 精致露营网Jingzhiluying.com -中国精致露营资源整合分享平台 商务合作联系153-5599-0481(同微信)
露营,精致露营,露营网,露营中国,露营中国网,精致露营网,去露营,露营之家,露营旅游,glamping,户外露营,露营网站,户外网站,露营资讯,房车,露营论坛,户外露营展,luying,jingzhiluying,campingchina,户外,户外装备,露营装备,精致露营装备,精致露营攻略,精致露营地,露营地,精致露营信息,露营必备,露营物品,精致露营火了,露营图片,露营的精美图片,露营背景图片,露营壁纸,夜晚的帐篷图片,露营野餐图片,精致露营图片,户外露营的照片,露营基地效果图,精致露营文案,精致露营季,露营的朋友圈文案

京ICP备09102084号 XML地图 Tags标签 APP客户端下载

×

分享到微信朋友圈

扫描二维码在微信中分享
关闭
手机客户端
APP下载