匿名函数范围内?

时间:2016-04-21 00:46:54

标签: javascript

我来自一个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

7 个答案:

答案 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知道task1task1也知道task2。但重要的作业顺序。考虑一下代码:

&#13;
&#13;
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;
&#13;
&#13;

  • foo1尝试使用a时,它已定义,但未分配。
  • foo3使用let代替varlet对某些人来说更直观 来自其他编程语言。但要小心,因为它是一部分 ES6规范尚未得到广泛支持。
  • foo4b之前定义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函数可以改变它的值,因为它们是子函数。

查看有关var&amp; let个关键字有效。

所以你的一些问题的答案是:

  1. 不,它在同一个父“createWorker”函数范围内。
  2. 未知,因为我不写ObjectiveC代码。其他人可以回答那个。
  3. 是&amp;否:是的,因为它可以告诉task1是一个函数,但是没有,因为task2的功能块中的代码无法看到task1的功能块内部。

答案 6 :(得分:0)

任务2不知道任务1中创建的变量,任务1和任务2确实知道workCount。