闭包——非常重要但又难以掌握的概念,理解闭包可以看作是某种意义上的重生——《你不知道的Js》
虽然关于闭包,虽然大家可能已经看腻了,但我仍要试着去总结下它!!!
大家在阅读这篇文章之前,不妨先阅读一下我的前面几篇文章作为前置知识:
顾名思义,遇见问题先问为什么是我们一贯的思维方式,我们尝试回答一下:
等于没说
靠谱
靠谱
很靠谱
我们试着用代码来描述一下上面的回答,看看你最中意哪一个~
先看这段代码:
function foo(params) {
var a = '余光';
function bar() {
console.log(a);
}
bar()
}
foo(); // 余光
基于词法作用域的查找规则,bar函数
可以成功的打印a
变量,并且它也是foo
的子函数,但严格来说它并没有清晰的表达出闭包这一概念,说它表达的是嵌套函数可以访问声明于大外部作用域的变量更准确一些。
再来看下面的例子:
function foo(params) {
var a = '余光';
function bar() {
console.log(a);
}
return bar;
}
var res = foo();
res(); // 余光
结果一致,这是因为此时res
是执行foo
函数时返回的bar
引用,bar函数得以保存了它饿词法环境。
我们来看下面代码:
var name = '余光';
function foo() {
console.log(name); // 余光
}
foo(); //余光
foo的上下文被静态的保存了下来,而且是在该函数创建的时候就保存了。下面我们来验证一下:
var name = '余光';
function foo() {
console.log(name); // 余光
}
(function (func) {
var name = '老王';
func()
})(foo); // 余光
这里我们就可以理解——函数被创建后就形成了闭包,他们保存了上层上下文的作用域链,并且保存在[[scope]]
中,如果你对[[scope]]
的概念已经模糊了,不妨花几分钟看看《JavaScript中的执行上下文》这篇文章。
注意:闭包是函数内部的返回的子函数这句话本身没错,但要看从什么角度出发:
ECMAScript中,闭包指的是:
总结:
注意:这些并不是闭包的全部,就好像当你被问到——闭包是什么的时候,你的上述回答并不能结束这个话题,往往会引申出更多的话题。
还是那段经典代码:
var scope = "global scope";
function checkscope(){
var scope = "local scope";
function f(){
return scope;
}
return f;
}
var foo = checkscope();
foo(); // local scope
首先我们要分析一下这段代码中执行上下文栈和执行上下文的变化情况。
当 f
函数执行的时候,checkscope
函数上下文已经被销毁了啊(即从执行上下文栈中被弹出),怎么还会读取到 checkscope
作用域下的 scope
值呢?
当我们了解了具体的执行过程后,我们知道 f 执行上下文维护了一个作用域链:
因为这个作用域链:
f 函数
依然可以读取到 checkscopeContext.AO
的值;f 函数
引用了 checkscopeContext.AO
中的值的时候,即使 checkscopeContext
被销毁了,JavaScript 依然会让 checkscopeContext.AO
活在内存中;f 函数
依然可以通过 f 函数
的作用域链找到它,正是因为 JavaScript 做到了这一点,从而实现了闭包这个概念。多么浪漫的思想——只要你需要我,那我我本应该被销毁,你也能找到我~
直接上代码:
var child1;
var child2;
function parent() {
var x = 1;
child1 = function () {
console.log(++x)
};
child2 = function () {
console.log(--x)
};
}
parent();
child1(); // 2
child1(); // 3
child2(); // 2
大家可能不理解,child1
和child
他们两个函数在创建后都保存了上层上下文,万万没想到,同一个上下文创建的闭包是共用一个[[scope]]
属性的,某个闭包对其中[[Scope]]的变量做修改会影响到其他闭包对其变量的读取。
大家一定对下面这段代码很眼熟:
var arr = []
for(var i = 0; i < 10; i++){
arr[i] = function () {
console.log(i)
}
}
arr[0](); // 10
arr[1](); // 10
arr[2](); // 10
arr[3](); // 10
我们这么解释它:同一个上下文中创建的闭包是共用一个[[Scope]]属性的。
因此上层上下文中的变量i
是可以很容易就被改变的。
arr[0],arr[1]…arr[9]他们共用一个[[scope]],最终执行的时候结果当然一样。
如何利用闭包来解决这个问题呢?
var arr = []
for(var i = 0; i < 10; i++){
arr[i] = (function (i) {
return function () {
console.log(i);
}
})(i)
}
arr[0](); // 0
arr[1](); // 1
arr[2](); // 2
arr[3](); // 3
我们通过立即执行匿名函数的方式隔离了作用域,当执行 arr[0] 函数的时候,arr[0] 函数的作用域链发生了改变:
arr[0]Context = {
Scope: [AO, 匿名函数Context.AO globalContext.VO]
}
匿名函数执行上下文的AO为:
匿名函数Context = {
AO: {
arguments: {
0: 0,
length: 1
},
i: 0
}
}
我们看到,这时函数的[[Scope]]
属性就有了真正想要的值了,为了达到这样的目的,我们不得不在[[Scope]]
中创建额外的变量对象。要注意的是,在返回的函数中,如果要获取i
的值,那么该值还是会是10。
JavaScript内功系列:
关于我
其他沉淀
如果您看到了最后,不妨收藏、点赞、评论一下吧!!!
持续更新,您的三连就是我最大的动力,虚心接受大佬们的批评和指点,共勉!
本文地址:https://blog.csdn.net/jbj6568839z/article/details/106940646
如对本文有疑问, 点击进行留言回复!!
selenium + ajax抓取英雄联盟全部英雄的详细信息及多线程保存全部皮肤图片到本地
网友评论