javascript函数内部范围
考虑下面的代码:
function nepaliBuddha() {
var a = 20;
return function buddhaNepal() {
console.log(a);
}
}
var closure = nepaliBuddha();
closure(); // logs 20
现在当我们调用closure
输出是20
。 这证明内部作用域属性( [[scope]]
被分配给内部函数,在该内部函数中定义或声明了内部函数。如果未在声明中分配此属性,则无法在调用时记录20不同的背景
调用closure()
函数上下文的作用域链在函数调用时创建,由当前上下文的激活对象或VO和此函数的内部[[scope]]
属性组成。
调用还会创建[[scope]]
属性,这意味着内部范围属性是在声明和执行时创建的吗?
通常这个定义说[[scope]]
属性是在运行时或函数调用时创建的,但是这并不正确,因为[[scope]]
属性也已经在声明中分配了。
我认为[[scope]]
属性可能在执行函数后得到更新,是吗? 请给出[[范围]]内部财产的明确定义。 如何以及何时在声明时或执行时或两次创建它。
哇,你不完全困惑。 好吧,我会尽可能简单地解释闭包。
首先,我们将从范围开始。 有两种类型的范围:
块范围在程序中出现时立即开始。 另一方面,函数范围直到函数被调用才开始。 因此,多次调用同一个函数会导致多个作用域被创建。
JavaScript没有块范围。 它只有功能范围。 因此,要模拟一个块范围,我们需要创建一个函数表达式并立即执行它。 这个patttern被称为立即调用的函数表达式(IIFE),它看起来像这样:
(function () {
// this is the JS equivalent of a block scope
}());
除了块范围和功能范围之外,还有另一种分类范围的方法。 因此我们也有:
此区别仅适用于函数作用域,因为块作用域始终在词汇范围内。 JavaScript只有词法范围。
为了理解词法作用域和动态作用域之间的区别,我们需要理解自由和约束变量之间的区别。
考虑以下程序:
function add(x, y) {
return x + y; // x and y are bound to add
}
在上面的程序中,变量x
和y
绑定到函数add,因为它们是在add
中声明的。
在另一方面变量x
和y
在下面的程序是免费的范围内的功能add
,因为它们不是内声明add
,但他们中使用add
:
function add() {
return x + y; // x and y are free within add
}
现在自由变量是一个问题。 他们需要被映射到某个值,但是哪个值? 这就是词汇和动态范围出现的地方。 我不会介绍主要细节,但是您可以在维基百科上阅读它。
范围很像原型继承。 当一个新的范围开始时,它从父范围继承,形成一个范围链,就像JavaScript中的原型链一样。
词法范围和动态范围因新范围继承的父范围而异。
由于JavaScript只有词法范围,所以我们不会为动态范围而烦恼。 考虑以下程序:
var count = 0;
function incrementCount() {
return ++count;
}
(function () {
var count = 100;
alert(incrementCount()); // 1
}());
这里的函数incrementCounter
有一个自由变量 - count
。 由于JavaScript具有词汇范围count
因此将映射到全局变量count
而不是在IIFE中声明的本地count
。 因此incrementCount
返回1
而不是101
。
现在关闭只适用于有词汇范围的语言。 考虑以下程序:
function getCounter() {
var count = 0;
return function () {
return ++count;
};
}
var counter = getCounter();
alert(counter()); // 1
alert(counter()); // 2
alert(counter()); // 3
在上面的程序中, getCounter
返回的函数是关于变量count
的闭包,因为:
count
在返回的函数(即counter
)内是空闲的。 count
的范围之外。 这两个条件对于称为闭包的函数都是必需的。 有关更多信息,请阅读以下答案:https://stackoverflow.com/a/12931785/783743
现在需要了解的重要一点是,尽管函数counter
可能永远不会被调用,但它仍然会被称为闭包。 闭包只是一个关闭变量的函数(称为闭包的上值)。
当我们调用getCounter
我们创建一个新的作用域(让我们调用这个作用域A
),每次我们调用getCounter
(即counter
)返回的函数时,我们创建一个继承范围A
的新作用域。 就这样。 没有创建新的封闭。
closure
是一种特殊的对象,它结合了两件事:函数和创建该函数的环境。 环境由创建闭包时在 范围内的任何局部变量组成。
function makeFunc() {
var name = "Mozilla";
function displayName() {
alert(name);
}
return displayName;
}
现在调用makeFunc()
var myFunc = makeFunc();
myFunc();
在这种情况下, myFunc
是一个闭包,它包含了displayName
函数和创建闭包时存在的“Mozilla”字符串,即MDN 。
所以,当我调用var myFunc = makeFunc();
时, scope
创建了var myFunc = makeFunc();
和myFunc()
( makeFunc()
调用的结果)现在是一个闭包。 所以,去那里的第一行
closure
是一种特殊的对象,它结合了两件事:函数和创建该函数的环境。
现在考虑这些
function nepaliBuddha() {
var a = 1;
return function buddhaNepal() {
a = a+1;
console.log(a);
}
}
var closure1 = nepaliBuddha(); // An individual scope
var closure2 = nepaliBuddha(); // An individual scope
closure1(); // 1
closure1(); // 2
closure2(); // 1
closure2(); // 2
closure2(); // 3
演示。
这意味着, closure1()
和closure2()
被关闭和两者都有自己的个人范围/环境,他们已经获得自己的范围,一旦他们得到它(在这种情况下,每次通话时间nepaliBuddha
你正在创建一个封闭,并给予/保存到一个变量)。
范围定义了功能,变量等可用的区域。 所以,当你在nepaliBuddha
(外部函数)中定义/声明了函数buddhaNepal
(内部函数)时, buddhaNepal
(内部函数)已经与全局范围分开,没有别的。 它不能访问全局范围内的任何东西,但它有自己的范围,就是这样。 nepaliBuddha
(外部函数)是buddhaNepal
(内部函数)的边界,在这种情况下, nepaliBuddha
外部函数的局部范围/环境是buddhaNepal
(内部函数)的全局范围。
在JavaScript中,这被称为Lexical Scopeing
( Lexical Scopeing
它定义了如何在嵌套函数中解析变量名。 Lexical Scope的其他名称是静态范围界定或关闭。 这意味着内部函数的作用域包含父函数的作用域。
上一篇: javascript function internal scope
下一篇: Is it true that every function in JavaScript is a closure?