我来自一个Actionscript背景和(很晚才到派对)我正在尝试学习JavaScript。我正在浏览YouTube上的这个AngularJS - Video Tutorial For Beginners(非常好)并且看到了一些我不理解的基本内容。
在第5行定义了var workcount
。然后定义两个匿名函数并在对象中返回。函数引用workcount
但不是workcount
在不同的范围内?这与Objective-C中的blocks
类似,其中本地变量仍可在块中访问。这是什么名字?
或如果函数“知道”以前在其范围内定义的变量,函数task2
“是否知道”task1
?
让我觉得我无法理解这一点。
更新:感谢所有回复。我现在明白了 - 虽然之前我已经看过“封闭”一词,但我从来没有理解它(这似乎不是一个非常具有描述性的术语。在阅读中,我看到了“叠帧”一词,然后灯泡点亮了:stack ...参考框架);
var createWorker = function(){
var workCount = 0;
var task1 = function(){
workCount += 1;
console.log("task1" , workCount);
};
var task2 = function(){
workCount += 1;
console.log("task2" , workCount);
};
return {
job1: task1,
job2:task2
}
};
worker=createWorker();
worker.job1();
worker.job2();
输出:
task1 1
task2 2
答案 0 :(得分:12)
请注意,变量和两个匿名函数都包含在同一个函数中(让我们称之为父函数)。因此,此父函数中可以使用此变量的范围。
所以现在这个变量充当这两个内部函数的全局变量但是范围仅限于父函数。两个内部函数共享同一个变量。在一个函数中更改变量的值也会对其他函数产生影响。
所以在帖子中使用逻辑让我们说我们一个接一个地执行task1和task2。该变量最初设置为0.然后在您的task1中,它增加1。这使得变量值为1(0 + 1)。现在在任务2中它也增加了一个,使其值为2(1 + 1)。
此范围概念在JavaScript中称为 closure 。
答案 1 :(得分:9)
这在JavaScript中称为闭包。
JavaScript中闭包的范围是词法,这意味着闭包所属的函数中包含的所有东西都可以访问其中的任何变量
基本上,createWorker是一个范围,因为任务1和任务2在createWorker中声明,所以它们可以访问createWorkers范围内声明的所有变量。
但是createWorker
无权访问任务1和任务2中声明的任何变量。
答案 2 :(得分:2)
是的,功能了解其范围内的所有内容,包括彼此。
您的问题分为两部分。
第二部分首先容易回答:范围内的所有变量和函数都是" hoisted,"允许您在声明变量之前使用变量:
x = 5;
var x;
console.log(x); // Gives 5
回到问题的第一部分:就范围而言,我不会对其进行过多扩展,因为它是本网站和其他网站上广泛涉及的主题。 w3schools has a good guide on this
基本上,它归结为全球和本地范围。全局范围可以像您想象的那样工作,变量(或函数)可在全局范围内使用:
var x = 10;
function foo() {
console.log('Global scope! ' + x);
}
本地范围基本上是闭包内的所有内容(超出此问题的主题),其功能如下:
function foo() {
bar(); // This will work, since foo and bar share scope
foobar(); // This will not work: foobar is only in scope within bar
}
function bar() {
function foobar() {
console.log('foobar');
};
console.log('bar');
foobar(); // This will work, since foobar is defined within bar's local scope
}
var
声明使事情变得复杂一些。 ES6 let
声明大大简化了这一点。 Read more
顺便说一下,虽然你的函数是匿名的,但它们并不是真的,因为你保存了对它们的引用。从功能上讲,下面两个例子完全等同:
// These give the exact same result
function foo() {}
var foo = function() {}
// You can use either by calling
foo();
答案 3 :(得分:2)
此代码说明了它的工作原理。
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Closure JS</title>
<script type="text/javascript">
var createWorker = function () {
var workCount = 0;
var task1 = function () {
var t = t || 0;
workCount += 1;
console.log("task1: " + workCount);
console.log("task1 t: " + (t++));
}
var task2 = function () {
var t = t || 0;
workCount += 1;
console.log("task2: " + workCount);
console.log("task2 t: " + (t++));
}
return {
job1: task1,
job2: task2
};
}
var app = new createWorker();
</script>
</head>
<body>
<div>
<input type="button" value="task1" onclick="app.job1()" />
<input type="button" value="task2" onclick="app.job2()" />
</div>
</body>
</html>
按钮几次点击后控制台输出:
task1: 1
task1 t: 0
task1: 2
task1 t: 0
task2: 3
task2 t: 0
task2: 4
task2 t: 0
很容易看出task1和task2知道了他们的父范围,并且对彼此以及之前的执行情况一无所知。
这样球反弹。
答案 4 :(得分:2)
您可以编写代码,如下所示,javascript将对其进行相同的解释。
var createWorker = function(){
var workCount, task1, task2;
workCount = 0;
task1 = function(){
workCount += 1;
console.log("task1" , workCount);
};
task2 = function(){
workCount += 1;
console.log("task2" , workCount);
};
return {
job1: task1,
job2:task2
}
};
这里发生的是,变量是在封闭功能块的顶部定义的。无论以何种顺序定义它们。因此,不仅task2
知道task1
,task1
也知道task2
。但重要的作业顺序。考虑一下代码:
function foo1() {
console.log("foo1: " + a);
var a = "Hello";
}
function foo2() {
var a = "Hello";
console.log("foo2: " + a);
}
function foo3() {
console.log("foo3: " + a);
let a = "Hello";
}
function foo4() {
console.log("foo4: " + b);
}
var b = 5;
foo1(); // undefined
foo2(); // Hello
try {
foo3(); // Throws ReferenceError
} catch (e) {
console.log("foo3: " + e.message);
}
foo4(); // 5
&#13;
<script src="http://gh-canon.github.io/stack-snippet-console/console.min.js"></script>
&#13;
foo1
尝试使用a
时,它已定义,但未分配。foo3
使用let
代替var
。 let
对某些人来说更直观
来自其他编程语言。但要小心,因为它是一部分
ES6规范尚未得到广泛支持。foo4
自b
之前定义foo4
以来就有效
定义。在b
被调用之前已分配foo4
。答案 5 :(得分:1)
JavaScript有一些有趣的变量范围规则。这是一个快速概述:
x = 0; // Global, as no "var" keyword preceeds it. Btw: var is optional!
var x = 0; // This is scoped to it's parent fn. Child fn's can use it.
let x = 0; // This is similar to var, but has a special use case. (See below.)
作为一个额外的好处,下一行代码看起来像是一个变量声明,但事实并非如此。它定义了一个常量。它是EcmaScript 2015规范AKA ES6的一部分。这是what's new in ES6。
const x = 0; // This is a constant (locked variable) and can't be changed.
同时var
和&amp; let
可通过其直接功能访问{他们的子功能,这里是他们不同的地方:let
关键字可以允许某人从父母和父母的内部制作具有相同名称的重复变量。儿童功能!它只是让JS变得陌生!
由于workCount是使用“var”关键字在父createWorker
函数内定义的,因此task1
&amp; task2
函数可以改变它的值,因为它们是子函数。
所以你的一些问题的答案是:
答案 6 :(得分:0)
任务2不知道任务1中创建的变量,任务1和任务2确实知道workCount。