[英]Javascript - understanding the scope chain's reference priority
I would like to better understand the scope chain the hopefully closure better and so the following code: 我想更好地了解范围链,希望可以更好地关闭,因此下面的代码:
function greet(whattosay){
return function(name) {
whattosay = "not hi"; //change the hi message
console.log(whattosay + ' ' + name);
function insideAnon() {
console.log(whattosay);
}
}
}
var sayHi = greet('Hi'); //get in return the anon function
sayHi("John"); //print not hi John on the console
I have recently learned that every "variable environment" is actually an array or an object with properties, and so each function has a reference to it's parent's function variable environment object/array. 我最近了解到,每个“可变环境”实际上都是一个数组或具有属性的对象,因此每个函数都有对其父对象的函数变量环境对象/数组的引用。 But my question is, what exactly is the scope chain?(I know that it is supposedly going "down" up until the global level), My current perception (Not sure) is that each child has it's parent and grandparent (and so on) variable environment references inside of it, and so it first check it's parent's variable environment object, if a variable is not there it looks at it's grandparent's reference and so on.
但是我的问题是,范围链到底是什么?(我知道它应该一直“下降”到全球范围),我目前的看法(不确定)是每个孩子都有父母和祖父母(依此类推) )内部的变量环境引用,因此它首先检查其父对象的变量环境对象,如果不存在该变量,它将查看其祖父母的引用,依此类推。 Or is it more like - Check the parent variable environment object reference, if not there that parent checks his parent's reference and so on.
还是更像-检查父变量环境对象引用,如果不存在,则检查父对象的引用,依此类推。 I HOPE i was very clear at what I was trying to say - How it really goes down the "chain".
我希望我很清楚我在说什么-它实际上是如何在“链条”上发展的。
You can get references about scope chains from this question: 您可以从以下问题获取有关范围链的参考:
Each function creates a scope object that contains the arguments for that function and the local variables defined in that function. 每个函数都会创建一个范围对象,该对象包含该函数的参数以及该函数中定义的局部变量。
Since Javascript uses lexical scoping, that means that a given piece of code can access variables defined within the current function or within any parent function. 由于Javascript使用词法作用域,因此这意味着给定的一段代码可以访问当前函数或任何父函数中定义的变量。 Javascript makes this work by maintaining a chain of scope objects so each scope has a reference to its parent scope.
Javascript通过维护一系列作用域对象来完成这项工作,因此每个作用域都有对其父作用域的引用。
When a given piece of code accesses a variable like: 当给定的代码段访问变量时,例如:
foo = 1;
Javascript, looks in the current scope object to see if something named foo
exists there. Javascript,在当前作用域对象中查找以查看是否存在名为
foo
对象。 If so, that variable is used. 如果是这样,则使用该变量。 If not, it gets the reference to the parent scope, checks that scope for something named
foo
and continues this process up the parent chain until it gets to the global scope and thus can go no further. 如果没有,它将获取对父作用域的引用,检查该作用域是否包含名为
foo
并继续此过程直至父链,直到到达全局作用域为止,因此无法继续进行下去。
So, in this example: 因此,在此示例中:
function one() {
var foo = 2;
function two() {
var fee = 3;
function three() {
foo = 1;
}
three();
}
two();
}
one();
The function one()
will execute and define foo
and two
in its scope. 函数
one()
将执行并在其范围内定义foo
和two
。 It will then call two()
which will execute and define fee
and three
in its scope. 然后它将调用
two()
来执行并定义fee
并在其范围内定义three
。 It will then call three()
which will execute and attempt to change the value of foo
. 然后它将调用
three()
,它将执行并尝试更改foo
的值。 It will first look in the scope object belonging to three
, but it will not find anything named foo
there so it will go to the parent scope of three
which will be the two
scope object. 它将首先查看属于
three
的作用域对象,但在那里找不到任何名为foo
东西,因此它将转到three
的父作用域,这将是two
作用域对象。 It will not find a variable named foo
in that scope object so it will go to the parent object of two
which will be the scope object of one
. 它不会找到一个变量命名为
foo
在该范围内的对象,因此会去的父对象two
,这将是范围对象的one
。 There it will find a variable named foo
so it will change the value of that variable. 在那里它将找到一个名为
foo
的变量,因此它将更改该变量的值。
On thing that is different about Javascript scope objects compared to something like a C++ stack frame is that they can continue to live after the function that created them has finished executing. 与C ++堆栈框架之类的Java范围对象不同的是,它们可以在创建它们的函数完成执行后继续存在。 This is a very common structure in Javascript and is often referred to as a closure.
这是Javascript中非常常见的结构,通常称为闭包。 Here's a simple example of that:
这是一个简单的例子:
function initializeCntr() {
var cntr = 0;
document.getElementById("test").addEventListener("click", function(e) {
++cntr;
document.getElementById("clickCnt").innerHTML = cntr;
});
}
initializeCntr();
In this short code example, there are three scopes present, the global scope (that contains the initializeCntr
symbol), the initializeCntr
scope that contains the cntr
variable and the scope that belongs to the anonymous function that is set as the event handler. 在此短代码示例中,存在三个作用域:全局作用域(包含
initializeCntr
符号),包含cntr
变量的initializeCntr
作用域和属于设置为事件处理程序的匿名函数的作用域。
When you call initializeCntr()
, it creates a function scope object that contains the variable cntr
. 当您调用
initializeCntr()
,它将创建一个包含变量cntr
的函数范围对象。 It then runs the addEventListener()
method and passes a reference to the inline anonymous event handler for addEventListener()
. 然后它运行
addEventListener()
方法,并传递到在线匿名事件处理程序的引用addEventListener()
The initializeCntr()
method then finishes execution. 然后,
initializeCntr()
方法完成执行。 But, because the inline anonymous function passed as the event handler contains a reference to the cntr
variable in the initializeCntr
scope object, that scope object is NOT garbage collected even though initializeCntr()
has finished executing. 但是,由于作为事件处理程序传递的嵌入式匿名函数在
initializeCntr
范围对象中包含对cntr
变量的引用,因此即使initializeCntr()
完成执行,该范围对象也不会被垃圾回收。 Instead, it is kept alive because of the lasting reference to the cntr
variable in it. 相反,由于持久引用了其中的
cntr
变量,因此它保持活动状态。 When that click event handler is then called sometime in the future, it can use that cntr
variable. 然后在将来某个时间调用该click事件处理程序时,它可以使用该
cntr
变量。 In fact, the initalizeCntr
scope will be kept alive as long as the event handler is active. 实际上,只要事件处理程序处于活动状态,
initalizeCntr
范围将保持活动状态。 If the event handler is removed or the DOM object it is attached to is removed, then and only then would the initializeCntr
scope object be eligible for garbage collection and eventually get freed. 如果事件处理程序被删除或它所附加的DOM对象被删除,则只有在那时,
initializeCntr
作用域对象才有资格进行垃圾回收并最终被释放。
Some JS implementations are smart enough to not necessarily retain the entire scope object and everything in it, but instead to only retain the elements of the scope object that are specifically referenced in child scopes that are still active. 一些JS实现很聪明,不必保留整个作用域对象及其中的所有内容,而是只保留在仍处于活动状态的子作用域中专门引用的作用域对象的元素。 So, if there were other variables next to
cntr
that were not used in the event handler, those other variables could be freed. 因此,如果
cntr
旁边还有其他未在事件处理程序中使用的变量,则可以释放这些其他变量。
FYI, this longer lasting scope is called a closure. 仅供参考,这种更长久的范围称为闭包。 Closures are very useful concepts in Javascript and something not available in a language like C++ (because they rely on garbage collection).
闭包在Javascript中是非常有用的概念,而某些东西在C ++等语言中是不可用的(因为它们依赖于垃圾回收)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.