我想在执行以下代码期间了解调试内容:
var z = 2;
var SQUAREPLUSOTHER = x => y => ((x*x) + y + z);
var x = 3;
console.log("SQUAREPLUSOTHER", SQUAREPLUSOTHER);
var squareoftwoplusother = (SQUAREPLUSOTHER)(x);
x = 4;
z = 4;
var result = squareoftwoplusother(5);
console.log("result", result);
现在... 致电:
console.log("SQUAREPLUSOTHER", SQUAREPLUSOTHER);
调试清楚显示:
squareoftwoplusother: undefined
SQUAREPLUSOTHER: x=>...
x: 3
z: 2
在以下呼叫时:
var SQUAREPLUSOTHER = x => y => ((x*x) + y + z);
调试显示:
Local:
x: 3
Closure
z: 2
在以下呼叫时:
x = 4;
z = 4;
调试显示:
squareoftwoplusother: y=>...
SQUAREPLUSOTHER: x=>...
x: 4
z: 4
在以下呼叫时:
var result = squareoftwoplusother(5);
调试显示:
result: 18
squareoftwoplusother: y=>...
SQUAREPLUSOTHER: x=>...
Local:
x: 5
Closure
x: 3
Closure
z: 4
,在最后一次调用中,调试显示:
result: 18
squareoftwoplusother: y=>...
SQUAREPLUSOTHER: x=>...
x: 4
z: 4
现在的问题: 有多少“关闭”?他们属于? (即如何解释...)
Local:
x: 5
Closure
x: 3
Closure
z: 4
如何在javascript中管理变量的范围? 如何用“上下文”或“无论是什么”的方式对封闭的混乱产生一个“明确的”想法:-)?
预先感谢
Ed
答案 0 :(得分:0)
每次调用一个函数时,都会为该函数创建一个本地作用域。如果函数返回后仍然有对该本地作用域的引用,则创建一个闭包。一旦引用消失,闭包也将被垃圾回收。
如果将断点添加到每个函数的第一行,您将看到已创建范围,以后可能会变成闭包。
此处http://dmitrysoshnikov.com/ecmascript/javascript-the-core/#activation-object
有很好的解释我将在您的代码中添加一些括号以使其更容易,并且我将添加自调用函数以添加一定程度的闭包。
(() => {
var z = 2;
var SQUAREPLUSOTHER = (x) => {
debugger;
return (y) => {
debugger;
return (x * x) + y + z;
}
};
var x = 3;
console.log("SQUAREPLUSOTHER", SQUAREPLUSOTHER);
var squareoftwoplusother = (SQUAREPLUSOTHER)(x);
x = 4;
z = 4;
var result = squareoftwoplusother(5);
console.log("result", result);
debugger
})()
下面的屏幕截图来自Chrome DevTools,它最大程度地减少了关闭次数,即仅保留了对所需内容的引用。过去,整个闭包始终可用,您可以证明这一点,因为它显示在调试器的闭包窗格中。有关更多详细信息,请参见Javascript closures performance
答案 1 :(得分:0)
范围
您的代码中只有三个不同的作用域。一个是全局作用域,您在其中声明了所有变量,另外两个在这里:
var SQUAREPLUSOTHER = x => y => ((x*x) + y + z);
有两个函数范围,一个包含变量x
,另一个函数范围包含变量y
。如果将箭头功能替换为function
,可能会更容易:
// global scope
function SQUAREPLUSOTHER(x) {
// function scope containing x
return function(y) {
// function scope containing y
return (x*x) + y + z;
};
}
范围界定的工作原理非常简单:
每个{
都会启动一个新作用域,其计数器}
将其关闭,并在其中用let
和const
(或var
声明的变量但那稍微复杂一点)是范围的一部分。如果它是函数的范围,则参数(例如x
和y
)就是该范围的一部分。
从代码中,可以访问当前作用域或其父作用域中的所有变量,如果有多个具有相同名称的变量,则获得的是最内部的变量。因此,x
函数内部的SQUAREPLUSOTHER
引用了SQUAREPLUSOTHER
范围的变量,而对于它外部的代码,x
是全局变量,而函数变量不能被访问。
变量的作用域在运行时不会更改,您始终可以通过查看周围的{
.. }
直接查看变量属于tp的作用域。
现在,不同作用域中的不同变量必须在运行时保存值,这就是我们要做的:
环境记录
调用函数时,JavaScript引擎会创建一个新的“ EnvironmentRecord”(类似于内部对象),其中包含您调用的函数的所有变量,例如在这种情况下:
function test(a) {
let b;
}
然后,如果您调用该函数(test(1)
),则会创建一个包含a
和b
的新环境记录。现在,函数中的代码开始运行,并在其中查找每个变量。如果有两个函数嵌套在一起,则调用内部函数将创建一个环境记录,其中包含对外部函数的引用:
function test(a) {
function test2(b) {
}
test2(5);
}
现在调用test(1)
将创建一条记录,其中a
是1
。如果引擎随后执行第二个调用(test2(5)
),则它将创建另一个包含b
的记录,其中包含5
,并且该记录包含对包含a
的记录的引用。现在,如果您在a
内使用test2
,引擎将在当前环境记录中查找它,找不到它,然后在找到a
的父对象中查找它是1
。
关闭
通常,当执行到达}
时,这些记录将被删除,但是,如果有另一条记录将当前记录作为父记录,则不会将其删除。它会一直存在,直到删除所有子记录,然后还将删除父记录。这种行为(变量的生存期更长,因为可以从内部函数体内访问它们)被称为闭包。