Область внутри анонимной функции?

Я иду из фона ActionScript и (очень поздно для вечеринки). Я пытаюсь изучить JavaScript. Я прохожу через AngularJS - Видеоурок для начинающих на YouTube (это довольно хорошо) и увидел что-то действительно основное, что я не понимаю.

В строке 5 определяется var workcount. Затем две анонимные функции определяются и возвращаются в объекте. Функции ссылаются workcount, но не workcount в другой области? Это похоже на blocks в Objective-C, где локальные вары остаются доступными внутри блока. Есть ли название для этого?

Или, если функция "знает о" ранее определенные в области видимости vars, функция task2 "знает о" task1?

Мне кажется, что я не могу понять это.

Обновление: Спасибо за все ответы. Я получаю это сейчас - и пока я видел термин "закрытие" раньше, я его никогда не понимал (кажется, это не очень описательный термин. При чтении я увидел термин "стоп-кадры", а затем загорелась лампочка: стек... система координат);


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

Ответ 1

Просто обратите внимание, что переменная и две анонимные функции обернуты внутри одной и той же функции (пусть называют ее родительской функцией). Таким образом, объем этой переменной доступен в этой родительской функции.

Итак, теперь эта переменная действует как глобальная переменная для этих двух внутренних функций Но область действия ограничена родительской функцией. Обе внутренние функции имеют одну и ту же переменную. Изменение значения переменной в одной функции также будет иметь эффект и в другой функции.

Итак, возьмем логику в сообщении. Скажем, мы выполняем task1 и task2 один за другим. Первоначально переменная установлена ​​в 0. Затем в вашей задаче 1 она увеличивается на единицу. Что делает переменное значение 1 (0 + 1). Теперь в задаче два также увеличивается на единицу, делая ее значение 2 (1 + 1).

Эта концепция области называется закрытием в JavaScript.

Ответ 2

Это называется замыканием в JavaScript.

Объем замыкания в JavaScript лексический, что означает, что все, что содержится в функции, к которой принадлежит замыкание, имеет доступ к любой переменной, которая находится в ней

В основном createWorker является областью действия, и поскольку задача 1 и задача 2 объявляются внутри createWorker, они имеют доступ ко всем переменным, объявленным в области createWorkers.

Но createWorker не имеет доступа к любым переменным, объявленным внутри задачи 1 и задаче 2.

Ответ 3

Да, функции знают все в своей области, включая друг друга.

На ваш вопрос две части.

Во второй части легко ответить первым: все переменные и функции в области " hoisted," позволяющие вам использовать переменную прежде чем вы его объявите:

x = 5;
var x;
console.log(x); // Gives 5

Вернемся к первой части вашего вопроса: с точки зрения охвата я не буду слишком сильно ее использовать, так как это широко распространенная тема на этом и других сайтах. w3schools имеет хорошее руководство по этому вопросу.

В принципе, это сводится к глобальному и локальному охвату. Глобальная область действия работает, как вы могли себе представить, с доступной переменной (или функцией) во всем мире:

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 local scope
}

Все становится немного сложнее с объявлениями var. Это значительно упрощается благодаря объявлению ES6 let. Подробнее.

И, кстати, пока ваши функции анонимны, они на самом деле не так, потому что вы сохраняете ссылки на них. Функционально два примера ниже являются абсолютно эквивалентными:

// These give the exact same result
function foo() {}
var foo = function() {}

// You can use either by calling
foo();

Ответ 4

Этот код иллюстрирует, как он работает.

<!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 знают о своей родительской области и ничего не знают друг о друге и о своем предыдущем исполнении.
Таким образом мяч отскакивает.

Ответ 5

Вы можете написать свой код, как показано ниже, и 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
<script src="http://gh-canon.github.io/stack-snippet-console/console.min.js"></script>

Ответ 6

В JavaScript есть некоторые интересные правила определения переменных. Вот краткий обзор:

x = 0; // Global, as no "var" keyword preceeds it. Btw: var is optional!
var x =  0; // This is scoped to it parent fn. Child fn can use it.
let x = 0; // This is similar to var, but has a special use case. (See below.)

В качестве дополнительного бонуса следующая строка кода выглядит как объявление переменной, но это не так. Он определяет константу. Это часть EcmaScript 2015 spec, AKA ES6. Здесь что нового в ES6.

const x = 0; // This is a constant (locked variable) and can't be changed.

Хотя обе функции var и let доступны по их непосредственной функции и их дочерним функциям, здесь они различны: ключевое слово let позволяет кому-то делать дубликаты переменных с тем же именем, как из внутри родительских и дочерних функций! Это просто делает JS незнакомцем!

Так как workCount определен внутри родительской функции createWorker с ключевым словом "var", функции task1 и task2 могут изменять это значение, поскольку они являются дочерними функциями.

Ознакомьтесь с спецификациями MDN о том, как var и let работают.

Итак, ответы на некоторые из ваших вопросов:

  • Нет, он находится в одной и той же родительской области действия createWorker.
  • Неизвестно, поскольку я не пишу код ObjectiveC. Кто-то может ответить на этот вопрос.
  • Да и Нет: Да, потому что он может сказать, что task1 является функцией, но Нет, потому что код в функциональном блоке task2 не может видеть внутри функционального блока task1.

Ответ 7

задача 2 не будет знать о переменных, созданных в задаче 1, задача 1 и задача 2 действительно знают о workCount.