因为该名称在封闭局部范围_深入研究范围链和封闭

因为该名称在封闭局部范围

by Kevin Turney

凯文·特尼(Kevin Turney)

深入研究范围链和封闭 (Deep dive into Scope Chains and Closures)

范围链和闭包如何在示例中起作用。 (How Scope chain and closures work under the hood with examples.)

了解JavaScript的范围和闭包 (Understanding Scope and Closures in JavaScript)

To dig deep and get the information you need, think like a journalist. Ask the six main questions: who, what, why, where, when, and how. If you can answer all these on a particular subject, then you have garnered the essence of what you need to know.

要深入挖掘并获取所需的信息,请像记者一样思考。 提出六个主要问题:谁,什么,为什么,在哪里,何时以及如何。 如果您可以针对特定主题回答所有这些问题,那么您已经掌握了需要知道的内容的精髓。

Before we get to closures, we have to have an understanding of scope.

在进行闭包之前,我们必须先了解范围。

First, if you know what [[scope]] (double bracket scope) is, then this article isn’t for you. You have more advanced knowledge and can move on.

首先,如果您知道[[scope]](双括号范围)是什么,那么本文不适合您。 您拥有更高级的知识,可以继续前进。

什么... (The what…)

What is scope and why does it matter?

范围是什么,为什么重要?

Scope is the context environment (also known as lexical environment) created when a function is written. This context defines what other data it has access to.

范围是编写函数时创建的上下文环境(也称为词法环境)。 该上下文定义了它可以访问的其他数据。

Put another way, scope is about access. Does the function have the ability to look up a variable for execution or manipulation, which variables are visible?

换句话说,范围与访问有关。 函数是否有能力查找变量以执行或操作,哪些变量是可见的?

There are two types of scope: local and global. Scope resolution, or finding what variables belong where, starts at the innermost context and proceeds outward until the identifier is found. Let’s start small…

有两种类型的范围:本地和全局。 范围解析或查找变量所属的位置,从最内部的上下文开始,向外进行直到找到标识符。 让我们开始吧……

var firstNum = 1;
function number() {  var secondNum = 2;  return firstNum + secondNum;}
number();

时间,原因和方式…执行上下文 (The when, why and how… execution context)

When function is invoked, it forms a new execution context. What is an execution context? Well, just as we have two types of scope, we have two types of execution context. They are a global execution context and a function execution context.

调用函数时,它形成一个新的执行上下文。 什么是执行上下文? 就像我们有两种类型的作用域一样,我们也有两种类型的执行上下文。 它们是全局执行上下文和函数执行上下文。

The global context is always running. In the case of a browser environment, it only stops when the browser is closed. When we call a function, we place that function’s execution context on top of the global execution context. Hence the terminology we stack them.

全局上下文始终在运行。 在浏览器环境中,它仅在关闭浏览器时停止。 调用函数时,会将函数的执行上下文放在全局执行上下文的顶部。 因此,我们将它们堆叠在一起。

JavaScript is a single threaded language, which means it can do only one thing at a time. When we call a function, the previous execution context is paused. The called function is on the top and it is then executed. When that finishes, it is popped off the stack and then the older execution context is resumed. This ‘stack’ of execution is what keeps track of the position of execution in our application. It also is important in looking up identifiers.

JavaScript是一种单线程语言,这意味着它一次只能做一件事。 当我们调用一个函数时,先前的执行上下文被暂停。 被调用的函数在顶部,然后执行。 完成后,将其弹出堆栈,然后恢复较旧的执行上下文。 执行的“堆栈”是跟踪应用程序中执行位置的原因。 在查找标识符中也很重要。

So now we have an execution context formed, what’s next?

现在我们形成了执行上下文,下一步是什么?

每个执行上下文都有一个关联的变量对象 (Each execution context has an associated variable object)

First, an Activation Object (not accessible by code, yet operates in the background) is formed. It is associated with this execution context. This object holds all declared variables, functions, and parameters passed within that context (its scope or accessibility range).

首先,形成一个激活对象 (无法通过代码访问,但在后台运行)。 它与此执行上下文关联。 该对象保存所有声明的变量函数参数 在该上下文(范围或可访问性范围)内传递。

Parameters to a function are implicitly defined. They are “local” to that function’s scope. These declared variables are “hoisted”, taken to the top of the scope that they belong to.

函数的参数是隐式定义的。 它们是该功能范围的“本地”。 这些声明的变量被“提升”,并置于它们所属范围的顶部。

Before I go further, to avoid confusion — in the global execution context, a Variable Object is created, and if it is a function, it is an Activation Object. They are pretty much identical.

为了避免混淆,在进一步介绍之前,请在全局执行上下文中创建一个变量对象 ,如果它是一个函数,则它是一个激活对象 。 它们几乎相同。

Now when this function is invoked, a “scope chain” of these objects is created. Why? The scope chain is a way to link or provide a systematic access to all variables and other functions that the current execution context (function in this case) has access to. [[Scope]] is the hidden mechanism that links these variable objects for identifier lookup. This hidden [[Scope]] is a property of the function, created at declaration, not invocation.

现在,当调用此函数时,将创建这些对象的“作用域链”。 为什么? 范围链是一种链接或提供对当前执行上下文(在此情况下为功能)可以访问的所有变量和其他功能的系统访问的方式。 [[Scope]]是链接这些变量对象以进行标识符查找的隐藏机制。 隐藏的[[Scope]]是函数的属性,在声明时创建,而不是在调用时创建。

At the head of the scope chain train, if it is a function, is the Activation Object. This activation object has it’s own declared variables, arguments, and this.

如果是功能,则作用域链列的最顶端是Activation Object 。 该激活对象具有其自己的声明的变量,参数和this。

Next, on the scope chain, is the next object from the containing context. If it is a global variable it is a Variable Object. If it is a function, it is an Activation Object. This happens until we reach the global context. That is why you can see we start from the innermost context to the outermost, think Russian nesting dolls.

接下来,在作用域链上,是包含上下文中的下一个对象。 如果是全局变量,则为变量对象。 如果是函数,则为激活对象 。 这种情况一直持续到我们达到全球背景为止。 这就是为什么您可以看到我们从最内在的背景到最外在的东西,例如俄罗斯嵌套娃娃。

What is the difference between a variable that is declared and one that is undeclared? If the identifier is preceded by a var, let, or const, it is declared explicitly and memory space is allocated for that variable’s use. If the identifier is not explicitly declared, then it is implicitly declared in the global scope which we will explore shortly. For purposes of this article, I’m sticking with var, no particular reason.

已声明的变量和未声明的变量有什么区别? 如果标识符前面带有var,let或const,则会显式声明该标识符,并为该变量的使用分配内存空间。 如果未显式声明标识符,那么将在全局范围内隐式声明它,我们将在稍后进行探讨。 出于本文的目的,我坚持使用var,没有特殊原因。

I know, the above was a little technical, and to be honest as I wrote this, I only learned of the Variable and Activation objects myself. Now that you had the deep dive explanation, here’s a high angle description…

我知道,上面的内容有点技术性,说实话,我自己才了解变量和激活对象。 既然您已经进行了深入的解释,这里是一个高角度的描述…

The scope chain is similar to the prototype chain. If a variable or property is not found, it continues up the chain until it is either found or a error is thrown. The function creates a hidden [[scope]] property. This property links innermost scopes to outermost scopes. In this case, number’s scope chain is linked to the global window object (the containing context that holds function number). This is what allows the engine to look outside of function number to find firstNum and secondNum.

范围链类似于原型链。 如果未找到变量或属性,则它将继续在链中向上移动,直到找到它或引发错误为止。 该函数创建一个隐藏的[[scope]]属性。 此属性将最内部的作用域链接到最外部的作用域。 在这种情况下,数字的范围链链接到全局窗口对象(包含函数编号的包含上下文)。 这就是引擎可以在函数号之外查找firstNum和secondNum的原因。

For example, let’s take the same function number and change one thing:

例如,让我们采用相同的功能编号并更改一件事:

// global scope  - includes firstNum, secondNum, and the// function number
var firstNum = 1;
function number() {    // local scope for number - only thirdNum is local to number()    // because it was explicitly declared. secondNum is implicitly declared in the    // the global scope.
secondNum = 2;    var thirdNum = 3;    return firstNum + secondNum;  }// what do we have access to in the global scope?number(); // 3firstNum; // 1secondNum; // 2thirdNum; // Reference Error: thirdNum is not defined

When speaking of global scope, variable declarations, non-nested function declarations, and function expressions (still considered a variable definition) are considered in the scope of the global window object in the browser. So as we see above, the window object has a properties firstNum, secondNum, and number added to it. If we proceed along the scope chain looking for it, we keep looking until we reach the global context’s variable object. If it’s not in there, then we get the Reference Error.

当谈到全局范围时,在浏览器中的全局窗口对象的范围内考虑变量声明,非嵌套函数声明和函数表达式(仍视为变量定义)。 因此,正如我们在上面看到的,window对象具有一个属性firstNum,secondNum和number。 如果我们继续在作用域链中寻找它,那么我们会一直寻找直到到达全局上下文的变量对象为止。 如果它不存在,那么我们将得到参考错误。

In a new tab, type "about:blank" in the search bar. A blank page will open and hit cmd-option-i to open dev tools.
Type the code above and remember, shift-enter for a new line!
Now type "window" and explore all the properties on the window object.
Look closely and you will see the properties firstNum, secondNum, and number are all available on the window object.

When we try to access thirdNum outside of where it was declared, we get a Reference Error. The engine that compiles the code failed to find an identifier in the window global scope object.

当我们尝试在声明的位置之外访问thirdNum时,会出现参考错误。 编译代码的引擎无法在窗口全局范围对象中找到标识符。

ThirdNum is only available inside of the function where it was declared. It is encapsulated or private to function number

ThirdNum仅在声明它的函数内部可用。 它被封装或专用于功能编号

The question you may have is “Does the global scope has access to everything inside of number?” Again, scope only works from the inside out, the innermost context, local, to the outermost context, global.

您可能会遇到的问题是“全局范围是否可以访问数字范围内的所有内容?” 同样,作用域仅从内部到外部,最内部的上下文(局部)到最外部的上下文(全局)起作用。

Starting with local scope, we can say that data and variables that are wrapped in a function are only accessible to members of that function. The scope chain is what links firstNum to number().

从局部作用域开始,我们可以说包装在函数中的数据和变量只能由该函数的成员访问。 作用域链将firstNum链接到number()。

When number() is invoked, the non-technical conversation goes like this…

调用number()时,非技术性对话如下:

Engine: “Number, I’m giving you a new execution context. Let me find what you need to run”

引擎: “数字,我给您一个新的执行环境。 让我找到您需要运行的东西”

Engine: “Ok, I see that thirdNum is explicitly declared. I’m setting space aside for you, go to the top of number’s function block and wait till I call you…

引擎 :“好吧,我看到了thirdNum被明确声明。 我正在为您留出空间,转到数字功能块的顶部,然后等到我打电话给您为止…

Engine: “Number, I see secondNum, does he belong to you?”

引擎 :“数字,我看到secondNum,他属于你吗?”

Number: “Nope.”

编号 :“不。”

Engine: “Ok, I see you’re linked to the global window object, let me look outside of you.”

引擎 :“好吧,我看到您已链接到全局窗口对象,让我向您外看。”

Engine: “Window, I have an identifier named secondNum, does he belong to you?”

引擎 :“窗口,我有一个名为secondNum的标识符,他属于您吗?”

Window: “He didn’t declare himself explicitly in Number with a var, let, or

窗口 :“他没有在数字中明确声明自己使用var,let或

Window: “He didn’t declare himself explicitly in Number with a var, let, orconst, so I’ll take him and set space aside.”

窗口 :“他没有在Number中明确声明自己带有var,let或 const,因此我将带他并留出空间。”

Engine: “Cool. Number, I see firstNum in your function block, does he belong to you?”

引擎 :“酷。 数字,我在您的功能块中看到firstNum,他属于您吗?”

Number: “Nope.”

编号 :“不。”

Engine: “Window, I see firstNum being used inside of Number, he needs him, does he belong to you?”

引擎 :“窗口,我看到在Number内使用firstNum,他需要他,他属于你吗?”

Window: “Yes, he was declared.”

窗口 :“是的,他被宣布了。”

Engine: “Everyone is accounted for, Now I’m assigning values to variables.”

引擎 :“每个人都考虑到了,现在我将值分配给变量。”

Engine: Number, I’m executing you, ready, go!”

引擎 :号,我在执行你,准备出发!”

That’s pretty much it for understanding scope, The key takeaways are:

理解范围就差不多了,关键要点是:

  1. Identifier lookup works from the inside out and stops at the first match.

    标识符查找从内到外进行,并在第一个匹配项时停止。
  2. There are two types of scope, global and local

    有两种类型的范围,全局和局部
  3. The scope chain is created at function invocation and is based on where variables and/or blocks of code are written (lexical environment). Are variables or functions nested?

    作用域链是在函数调用时创建的,并且基于变量和/或代码块的写入位置(词法环境)。 变量或函数是否嵌套?
  4. In JavaScript, if an identifier is not proceeded with a var, let, or const, it is implicitly declared in the global scope.

    在JavaScript中,如果标识符不以var,let或const开头,则在全局范围中隐式声明它。
  5. Scope does not go 1 for 1 with a function, it goes 1 to 1 with function invocation. Execute a function 3 times, get 3 different scopes. Why? Because if the execution of a function is finished, it is popped off the execution stack and with it, its access to other variables via its scope chain. Thus, a new scope is created each time a function is executed. Closures work a little differently!

    作用域不会与函数一一对应,而与函数调用会一一对应。 执行3次功能,得到3个不同的作用域。 为什么? 因为如果一个函数的执行完成了,它就会从执行堆栈中弹出,并随同它通过作用域链访问其他变量。 因此,每次执行功能时都会创建一个新的作用域。 封盖的工作方式略有不同!

Let’s finish up with a more complex example before we move on to closures.

在继续讨论闭包之前,让我们结束一个更复杂的示例。

a = 1;var b = 2;
function outer(z) {  b = 3;  c = 4;  var d = 5;  e = 6;
function inner() {    var e = 0;    d = 2 * d;    return d;  }  return inner();  var e;}outer(1);
  1. Before we run anything, hoisting is started at the outside, global level. Therefore we start with a declaration for a variable b, and a function declaration for function object outer. At this point nothing is assigned, we only have these two keys set up in the global scope variable object.

    在我们进行任何操作之前,都要在外部,全球范围内开始吊装。 因此,我们从变量 b的声明和函数对象external的函数声明开始。 此时,什么都没有分配,我们在全局范围变量对象中仅设置了这两个键。

  2. Next, we start at a = 1. This is an assignment, or a “write to” statement, yet there is no formal declaration for it. So what happens in the global scope, and if not in “strict mode”, is that a will be implicitly declared as belonging to the global scope variable object.

    接下来,我们从a = 1开始这是一个赋值或“写入”语句,但尚无正式声明。 因此,在全局范围(如果不是在“严格模式”下)发生的事情是, a将隐式声明为属于全局范围变量对象。

  3. We move to the next line and look up identifier b, through hoisting it was accounted for and now we can assign a value, 2, to it.

    我们移至下一行,并通过提升标识符b来查找标识符b ,现在我们可以为其分配值2。

So far we have…

到目前为止,我们有…

全球范围 (Global Scope)

4. Since we built the function object outer, at hoisting time, we then jump to execution, outer(1);

4.由于我们在吊起时构建了函数对象 external,因此我们跳转到执行external(1);。

5. Remember that upon function invocation, an execution context is first created. With that we create an Activation Object. It contains data and variables local to that context. We also form the scope chain.

5.请记住,在调用函数时,首先会创建一个执行上下文。 这样我们就创建了一个激活对象。 它包含该上下文本地的数据和变量。 我们还形成了范围链。

6. The parameter z is implicitly declared for this function and is assigned 1.

6.为此函数隐式声明了参数z,并为其分配了1。

A quick side note: at this time, the function’s execution context creates its “this” binding. It also creates an arguments array, which is an array of parameters passed, in this case, z. This is beyond the scope of this article, so allow me to glance over it.

快速说明:这时,函数的执行上下文创建其“ this ”绑定。 它还创建一个arguments数组 ,它是传递的参数数组,在本例中为z。 超出了本文的范围,因此请允许我浏览一下。

7. Now we look for explicit variable declarations in function outer. We have d, and var e is declared after the function inner.

7.现在,我们在函数 external中寻找显式变量声明。 我们有d ,并且var e函数inner后面声明。

8. Here’s some hidden magic, at this time a hidden [[scope]] property for function outer links its scope chain of variable objects. In this case, it works like a Linked List with a parent type property connecting the function outer Activation Object to the global execution context’s Variable Object. You can see here that scope extends from the inside out to form this “linking”. This is the reference that allows us to proceed up the scope chain for lookups.

8.这是一些隐藏的魔术, 这时函数外部的隐藏[[scope]]属性将其可变对象的作用域链链接起来。 在这种情况下,它的工作方式类似于带有父类型属性的链接列表,将属性外部激活对象连接到全局执行上下文的变量对象。 您可以在此处看到范围从内到外延伸,形成了这种“链接”。 这是使我们能够继续进行范围链查找的参考。

外部功能范围 (Scope for Function outer)

9. We step inside of outer and start at b = 3. Is b declared? Nope. So JavaScript uses the hidden [[scope]] property attached to function outer to move up the scope chain to find a “b”. It finds it in the global scope object and, since we are in the body of function outer, we assign b the value 3.

9.我们进入外部并从b = 3开始。是否声明了b ? 不。 因此,JavaScript使用附加到外部函数的[[scope]]隐藏属性将作用域链向上移动以查找“ b ”。 它在全局作用域对象中找到它,并且由于我们在外部函数的主体中,因此将b赋值为3。

全球范围再次 (Global Scope again)

10. Next line, c = 4. Since this is a write to identifier c, was c explicitly declared in function outer? No, and therefore it is not found by lookup in outer’s Activation Object. So it moves up the scope chain and looks in the global scope Variable Object. It is not there. Because this is a write to/ assignment operation, the global scope will handle it and place it on its Variable Object.

10.下一行c =4。由于这是对标识符c的写操作,因此c是在函数external中明确声明的吗? 不,因此无法通过外部的“激活对象”中的查找来找到它。 因此,它在范围链中向上移动,并查看全局范围的变量对象。 它不在那里。 因为这是写入/分配操作,所以全局范围将对其进行处理并将其放置在其Variable Object上。

全局范围变量对象 (Global Scope Variable Object)

11. d = 5. Yes, it is here so we assign it 5.

11. d =5。是的,在这里,因此我们将其分配为5。

外部功能范围 (Scope for function outer)

12. e = 6. Remember that straggler, var e? It was still declared in the body of outer and so we already had a place for it — so we assign it 6. If it wasn’t declared like c, we would move up the scope chain for a lookup. Since it is a write and not a read operation and not in ‘strict mode’, it would have been placed in the global’s scope.

12. e = 6.记住,散乱者var e吗? 它仍然在外部主体中声明,因此我们已经有了它的位置-因此我们将其赋值为6。如果未像c那样声明,我们将在范围链中向上移动以进行查找。 由于它是写操作而不是读操作,并且不在“严格模式”下,因此它将被放置在全局范围内。

13. We get to invoking function inner. We start all over like we did with function outer: hoisting, set up an Activation Object, and create a hidden [[scope]] property. In this case, the containing context is function outer, and outer “points” to the global scope.

13.我们开始调用函数inner 。 我们像使用函数external一样从头开始吊起,设置激活对象并创建隐藏的[[scope]]属性。 在这种情况下,包含上下文是函数external ,并且是指向全局范围的外部 “点”。

内部功能范围 (Scope for function inner)

14. Now with e and in general, variables that are given the same name work like this. Since identifier lookup starts from the innermost scope to the outermost scope, lookup stops at the first finding of that identifier. In the body of inner, we see var e= 0, done, stop, go no further. The e in the body of function outer is “inaccessible”. The term that is commonly used is “shadowing” e in function inner “shadows” or obscures the e in function outer.

14.现在使用e和一般来说,具有相同名称的变量的工作方式如下。 由于标识符查找从最内部的范围开始到最外部的范围,因此查找在该标识符的第一次找到处停止。 在内部主体中我们看到var e = 0,完成,停止,不再走。 函数外部e是“不可访问的”。 常用的术语是“遮盖”功能内部的 e “阴影”或遮盖功能外部e

15. Next line is d = 2 * d. Before we assign a value to d on the left, we have to evaluate the expression on the right, 2 * d. Since d is not local in scope to inner, we move up the scope chain to find a variable for d and whether it has a value associated with it. We find it in the outer scope in function outer and it is there that the value is changed.

15.下一行是d = 2 * d 。 在为左边的d赋值之前,我们必须对右边的表达式2 * d求值。 由于d内部范围不是局部的,因此我们在范围链中上移以找到d的变量以及该变量是否具有与之关联的值。 我们在函数external外部范围中找到它,并且在那里更改了值。

外部功能范围 (Scope for function outer)

The important thing here is that inner is manipulating data in its outer scope!

这里重要的是, 内部正在外部范围内操纵数据!

16. Function inner returns a value d, 10.

16.函数inner返回值d ,10。

17. Function outer returns the value of function inner.

17.函数external返回函数inner的值。

18. Result is 10.

18.结果是10。

19. Once function outer has completely finished executing, garbage collection takes place. Garbage collection is the freeing up of resources that are longer needed. It starts at the global scope and works as far as it has “reachability”.

19.一旦外部函数完全完成执行,便进行垃圾回收 。 垃圾收集是释放更长的资源。 它始于全球范围,并在具有“可达性”的范围内起作用。

The global scope in this example has no handle to function outer or function inner, so whoosh, gone. This is important when we get to closures, because there, we need data and some variables to stick around even after a function has finished running.

在此示例中,全局作用域没有用于外部函数或内部函数的句柄,所以糟透了。 对于闭包而言,这一点很重要,因为在那里,即使函数完成运行后,我们也需要数据和一些变量来保持。

最后,让我们关闭一下! (Finally, let’s get some Closure!)

我们应该如何定义闭包? (How shall we define a closure?)

Let’s start with a few definitions, all correct, some more in depth, but that get to the same point.

让我们从一些定义开始,所有的定义都是正确的,还有一些更深入的定义,但是到了同一点。

1. Closures are functions that have access to variables from another function's scope. This is accomplished by creating a function inside another function.
2. A Closure is a function that returns another function.
3. A Closure is an implicit, permanent link between a function and its scope chain.
为什么要关闭? (Why Closures?)

Without being able to leverage scope chain rules, async operations would be impossible. Because there is no guarantee that data will still be around to use later. JavaScript only has function scope as its encapsulation mechanism.

如果无法利用范围链规则,那么异步操作将是不可能的。 因为不能保证以后仍然可以使用数据。 JavaScript仅将功能范围作为其封装机制。

Closures are the best form of privacy for functions and variables. This is evident in the use of many module patterns. A module pattern returns an object to expose a public API. It also keeps other methods and variables private. Closures are used in event handling and callbacks.

封闭是函数和变量的最佳隐私形式。 这在使用许多模块模式时很明显。 模块模式返回一个对象以公开公共API。 它还将其他方法和变量设为私有。 闭包用于事件处理和回调。

An example of a module …

一个模块的例子……

var Toaster = (function(){    var setting = 0;    var temperature;    var low = 100;    var med = 200;    var high = 300;    // public    var turnOn = function(){        return heatSetting();    };    var adjustSetting = function(setting){        if(setting <= 3){            temperature = low;        }if (setting >3  && setting <= 6){            temperature = med;        }if (setting > 6 && setting <= 10){            temperature = high;
}return temperature;    };    // private    var heatSetting = function(adjustSetting){        var thermostat = adjustSetting;        return thermostat;        };    return{            turnOn:turnOn,            adjustSetting:adjustSetting        };})();
Toaster.adjustSetting(5);Toaster.adjustSetting(8);

The module Toaster has private locals and a public interface and is written as an Immediately Invoked Function Expression (IIFE). We create a function, immediately invoke it, and grab the return value.

Toaster模块具有专用的本地语言环境和公共接口,并被编写为立即调用函数表达式(IIFE)。 我们创建一个函数,立即调用它,并获取返回值。

Another small example:

另一个小例子:

function firstName(first){    function fullName(last){        console.log(first + " " + last);    }    return fullName;}var name = firstName("Mister");name("Smith") // Mister Smithname("Jones"); //Mister Jones

The inner function fullName( ) is accessing the variable, first, in its outer scope, firstName( ). Even after the inner function, fullName, has returned, it still has access to that variable. How is this possible? The inner function’s scope chain includes the scope of its outer scope.

内部函数fullName()首先在其外部范围firstName()中访问变量。 即使内部函数fullName返回之后,它仍然可以访问该变量 。 这怎么可能? 内部函数的作用域链包括其外部作用域的范围。

When a function is called, an execution context and a scope chain are created. Also the function get’s a hidden [[Scope]] property. The Activation Object for the function is initialized and placed in the chain. Then the outer function’s activation object is placed in the chain. In this case, finally the global Variable Object.

调用函数时,将创建执行上下文和作用域链。 同样,该函数还具有隐藏的[[Scope]]属性。 该功能的激活对象已初始化并放置在链中。 然后将外部函数的激活对象放置在链中。 在这种情况下,最后是全局变量对象

In this example, fullName is defined. A [[Scope]] property is created. The containing function’s activation object is added to fullName’s scope chain. It is also added to the global variable object. This reference to an outer function’s activation object enables access to all of the containing scopes variables. It does not get garbage collected.

在此示例中,定义了fullName。 创建一个[[Scope]]属性。 包含函数的激活对象将添加到fullName的作用域链中。 它也被添加到全局变量对象中。 通过引用外部函数的激活对象,可以访问所有包含的作用域变量。 它不会收集垃圾。

This is most important. The activation object of the outer function, firstName(), cannot be destroyed once it is finished executing, because the reference still exists in fullName’s scope chain. After firstName( )execution completes, its scope chain for that execution context is destroyed. But the activation object will remain in memory until fullName( ) is destroyed. We can do that by setting its reference to null.

这是最重要的。 外部函数firstName()的激活对象一旦完成执行就无法销毁,因为该引用仍然存在于fullName的作用域链中。 firstName() 执行完成后,该执行上下文的作用域链被破坏。 但是激活对象将保留在内存中,直到fullName()被销毁为止。 我们可以通过将其引用设置为null来实现。

The keen observer will note that we return a reference to fullName, not the return value of fullName( )!

敏锐的观察者将注意到,我们返回对fullName的引用,而不是fullName()的返回值!

This is what we mean by an implicit, permanent link between and function and it’s scope chain.

这就是功能与作用域及其作用域链之间的隐式,永久链接。

A closure always gets the last value from the containing function because the reference to the variable object is stored.

闭包总是从包含的函数中获取最后一个值,因为存储了对变量对象的引用。

For instance …

例如 …

var myFunctions= [];function createMyFunction(i) {    return function() {           console.log("My value: " + i);            };        }for (var i = 0; i < 10; i++) {myFunctions[i] = createMyFunction(i);myFunctions[i]();}
My value: 0 My value: 1 My value: 2 My value: 3 My value: 4 My value: 5 My value: 6 My value: 7 My value: 8 My value: 9

If we go back to our original scope example and change one thing:

如果我们回到原始范围示例并更改一件事:

a = 1;var b = 2;
function outer(z) {  b = 3;  c = 4;  var d = 5;  e = 6;
function inner() {    var e = 0;    d = 2 * d;    return d;  }  return inner; // we remove the call operator, now we are returning a reference to function inner.  var e;}myG = outer(1); // store a reference to function inner in the global scope (the return value of outer)myG(); // when we execute myG, inner's [[Scope]] property is copied to recreate the scope chain,    //  and that gives it access to the scopes that contain function inner, outter then global. We got inner and inner's got outter.

Here are a few more examples:

以下是一些示例:

function make_calculator() {    var n = 0;  // this calculator stores a single number n    return {      add: function(a) { n += a; return n; },      multiply: function(a) { n *= a; return n; }    };}
first_calculator = make_calculator();second_calculator = make_calculator();
first_calculator.add(3);                   // returns 3second_calculator.add(400);                // returns 400
first_calculator.multiply(11);             // returns 33second_calculator.multiply(10);            // returns 4000

Suppose we wanted to execute an array of functions:

假设我们要执行一个函数数组:

function buildList(list) {    var result = [];    for (var i = 0; i < list.length; i++) {        result.push(function number(i) {          var item = 'item' + list[i];          console.log(item + ' ' + list[i])} );    }    return result;}buildList([1,2,3,4,5]);
function testList() {     var fnlist = buildList([1,2,3,4,5]);     for (var i = 0; i < fnlist.length; i++) {       fnlist[i](i); // another IIFE with i passed as a parameter!!     } } testList();

I hope that this explanation of scope and closures helps. Play around with the patterns you see here, experiment. Actually writing this article was difficult — I gained a far deeper understanding than I had when I started.

我希望对范围和闭包的这种解释有所帮助。 尝试使用您在此处看到的模式进行实验。 实际上写这篇文章很困难-与开始时相比,我获得了更深刻的理解。

翻译自: https://www.freecodecamp.org/news/deep-dive-into-scope-chains-and-closures-21ee18b71dd9/

因为该名称在封闭局部范围

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值