2014-12-10 6 views
0

Я начал изучать node.js, недавно прочитав Node JS в книге действий. Вероятно, это вопрос новичков, но после чтения нескольких сообщений о функциях обратного вызова и области переменных javascript у меня все еще есть проблема, понимающая идею этого кода в главе 5 книги.Node.js, вызывающий функцию обратного вызова внутри обратного вызова

function loadOrInitializeTaskArray(file, cb) { 
    fs.exists(file, function(exists) { 
    var tasks = []; 
    if (exists) { 
     fs.readFile(file, 'utf8', function(err, data) { 
     if (err) throw err; 
     var data = data.toString(); 
     var tasks = JSON.parse(data || '[]'); 
     cb(tasks); 
     }); 
    } else { 
     cb([]); 
    } 
    }); 
} 

function listTasks(file) { 
    loadOrInitializeTaskArray(file, function(tasks) { 
    for(var i in tasks) { 
     console.log(tasks[i]); 
    } 
    }); 
} 

Он включает в себя три функции обратного вызова, разделенные на две функции. listTasks (..) вызывается первым, и он вызывает loadorInitializeTaskArray (..) позже. Моя проблема начинается здесь, как этот вызов обрабатывает узел? loadOrInitializeTaskArray принимает два аргумента, а второй - это функция обратного вызова, которая не должна принимать какие-либо параметры в соответствии с сигнатурой, но она делает !!
когда cb (..) вызывается в loadorInitializeTaskArray и что это (ту же функцию, которая вызывает вспомогательную функцию)?

«tasks» - это массив, объявленный внутри функции loadOrInitializeTaskArray, как у нас есть доступ к нему в функции listTasks (..)?

Я знаю, что в Javascript область видимости внутри функции определяется и все вложенные функции. Но мне это трудно понять. Так может кто-нибудь объяснить, что здесь происходит? Спасибо

ответ

3

Вы действительно трудное время в понимании объема переменной внутри вложенных функций. Итак, давайте начнем с этого.
Давайте рассмотрим этот код

function foo(){ 
    var a = 3; 
    function bar(){ 
    console.log(a); 
    } 
    return bar; 
} 
var baz = foo(); 
baz(); // the value of a i.e 3 will appear on console 

Если вы знаете, как охват языков C, C++ ... Ваш мозг будет интерпретировать этот код, как это. Если вы не знаете, что кто-либо из них просто проигнорирует очки.

  • Когда foo() называется переменная a будет объявлена ​​и присваивается значение 3.
  • Когда foo возвращается стек, содержащий будет уничтожен. И, следовательно, а также будет уничтожен.
  • Тогда как в мире baz() выводит 3. ???

Ну, в javascript, когда функция называется тем, что происходит, отличается от того, что происходит на C. Итак, сначала пусть все ваши вещи C исчезнут из вашего ума, прежде чем читать дальше.

В разрешении области javascript выполняется перемещение по цепочке объектов, которая определяет переменные, которые являются «в области видимости» для этого кода. Посмотрим, как?

  • Когда вы объявляете глобальную переменную JavaScript, вы фактически определяете свойство глобального объекта.
  • В коде JavaScript верхнего уровня (то есть код, не содержащийся в каких-либо определениях функций) цепочка областей видимости состоит из одного объекта - глобального объекта.
  • В не-вложенной функции цепочка областей видимости состоит из двух объектов. Первый - это объект, который определяет параметры функции и локальные переменные, а второй - глобальный объект.
  • Во вложенной функции цепочка областей видимости имеет три или более объекта. Когда функция определена, она сохраняет цепочку областей действия в действии. Когда эта функция вызывается, она создает новый объект для хранения своих локальных переменных и добавляет этот новый объект в цепочку хранимых цепей для создания новой более длинной цепочки, которая представляет область действия для этой функции.

Итак, когда выполнено foo(). Он создает новый объект для хранения локальных переменных. Таким образом, переменная a будет сохранена в этом объекте. А также определяется бар. Когда бар определен, он сохраняет действующую цепочку. Итак, цепочка областей видимости теперь содержит объект, который имеет переменную a. Таким образом, когда возвращается bar, он имеет ссылку на свою цепочку видимости. И, следовательно, он знает a.

Итак, я думаю, это отвечает на ваш вопрос, как узел обрабатывает код.

Вы писали:

loadOrInitializeTaskArray принимает два аргумента, а вторые из них является функция обратного вызова, которая не должен принимать какие-либо параметры в соответствии с сигнатурами, но это делает !!

Функция обратного вызова

function(tasks) { 
    for(var i in tasks) { 
     console.log(tasks[i]); 
    } 
} 

И он принимает аргумент tasks. Итак, вы здесь не правы.

И когда loadOrIntializeTaskArray называется cb, это относится к этой функции обратного вызова. И cb(arg) в основном делает это tasks = arg где задачи - это аргумент в функции обратного вызова.

Думаю, у вас все еще будет много вопросов. Дайте мне знать в комментариях. И я настоятельно рекомендую вам пройти Core Javascript перед погружением в узел.

+1

Спасибо, Vishal за то, что вы положили на этот комментарий. Я не понял, что отсутствие врожденного знания Javascript оказывает такое влияние на изучение node.js. Я давно использовал javascript на стороне клиента, и я думал, что все будет в порядке. Можете ли вы отсылать меня к промежуточному/расширенному руководству по JavaScript, особенно по таким темам, как область охвата, закрытие ...? И вы правы, исходя из java-языка, кодирование в узле запутывает именно его асинхронную модель обратного вызова. – Reza

+0

Да, конечно ... Мне нравится эта книга [JavaScript: окончательное руководство] (http://shop.oreilly.com/product/9780596805531.do). Это действительно потрясающий источник как для ядра, так и для javascript на стороне клиента. Чтобы начать работу с узлом, вам просто нужно прочитать основной javascript. – Vishal

0

Я не уверен, что вы подразумеваете под «второй - это функция обратного вызова, которая не должна принимать какие-либо параметры в соответствии с сигнатурой». Подпись обратного вызова (function(tasks)), безусловно, ожидает аргумент (tasks).

cb функция обратного вызова, которая была принята в В этом случае буквально анонимная функция:.

function(tasks) { 
    for(var i in tasks) { 
    console.log(tasks[i]); 
    } 
} 

tasks это название двух различных переменных (но они указывают на тот же объект, так как массивы переданы по ссылке) в разных областях. Один из них указан в loadOrInitializeTaskArray(), как вы отметили, другой параметр для обратного вызова, переданного в loadOrInitializeTaskArray().

Кроме того, в случае несвязанного примечания обычно обратные вызовы следуют за форматом «error-first». Это позволяет обрабатывать ошибки вверх по течению. Изменение кода, чтобы следовать этой конвенции приведет:

function loadOrInitializeTaskArray(file, cb) { 
    fs.exists(file, function(exists) { 
    var tasks = []; 
    if (exists) { 
     fs.readFile(file, 'utf8', function(err, data) { 
     if (err) 
      return cb(err); 

     var data = data.toString(); 

     // JSON.parse() throws errors/exceptions on parse errors 
     try { 
      var tasks = JSON.parse(data || '[]'); 
     } catch (ex) { 
      return cb(ex); 
     } 

     cb(null, tasks); 
     }); 
    } else { 
     cb(null, []); 
    } 
    }); 
} 

function listTasks(file) { 
    loadOrInitializeTaskArray(file, function(err, tasks) { 
    if (err) throw err; 
    for (var i in tasks) { 
     console.log(tasks[i]); 
    } 
    }); 
} 
+0

mscdex, Спасибо за ваш ответ. Мои знания в javascript не так глубоки. Исходя из языка C++, языка Java, когда мы определяем такую ​​функцию, как функция loadOrInitializeTaskArray (файл, cb), ее нельзя назвать loadOrInitializeTaskArray (файл, функция (TASKS)) с аргументом массива. Но, конечно, этот код находится в Javascript! – Reza

+0

Использование встроенных анонимных функций, подобных этому, является общим. Вы можете вытащить функцию, дать ей имя, а затем передать одну и ту же функцию по имени вместо литерала функции. Возможно, вы захотите проверить [Руководство по JavaScript MDN] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide), чтобы лучше ознакомиться с языком и его отличиями от Java/C++. – mscdex

0

Во-первых, на самом деле не такая вещь, как функция подписи в JavaScript. Вы можете передать столько или несколько аргументов функции, которая вам нравится. Второй аргумент loadOrInitialiseTaskArray просто присваивается локальной переменной cb при вызове функции. Затем строка cb(tasks) вызывает это значение, поэтому второй аргумент лучше был функцией.

Когда вы вызываете loadOrInitializeTaskArray из listTasks, второй аргумент действительно является функцией, а первый аргумент этой функции называется tasks в своей области. Он не достигает значения loadOrInitializeTaskArray и использует переменную, объявленную в области этой функции. Вы явно передали это значение при вызове cb.

код будет работать так же, и, возможно, будет легче понять, если мы переименовали переменные:

function loadOrInitializeTaskArray(file, cb) { 
    fs.exists(file, function(exists) { 
    var taskArray = []; 
    if (exists) { 
     fs.readFile(file, 'utf8', function(err, data) { 
     if (err) throw err; 
     var data = data.toString(); 
     var taskArray = JSON.parse(data || '[]'); 
     cb(taskArray); 
     }); 
    } else { 
     cb([]); 
    } 
    }); 
} 

function listTasks(file) { 
    loadOrInitializeTaskArray(file, function(listOfTasks) { 
    for(var i in listOfTasks) { 
     console.log(listOfTasks[i]); 
    } 
    }); 
} 
+0

Благодарим вас за быстрый ответ. Да, у меня нет большого опыта работы с Javascript. Как побочный вопрос, разве вы не думаете, что если мы перепишем эти две функции в одном, было бы легче понять? – Reza

Смежные вопросы