В этой части 7 серии « Голый Javascript» я расскажу о нескольких интересных причудах о функциях и области видимости в JavaScript, а также расскажу о странной функции под названием «Подъем».
Первое, что нужно знать, это то, что в JavaScript у нас нет областей действия на уровне блоков. Вместо этого в JavaScript функции отвечают за создание новых блоков области видимости. Позвольте мне уточнить. В других языках программирования, таких как Java, если вы пишете такой блок кода, как этот
public void someMethod(){ for(int i=i;i<10,i++){ //Some usage of variable i } //Compiler error System.out.println(i); }
Когда вы пытаетесь скомпилировать эту программу на Java, вы получаете сообщение об ошибке в строке, где вы пытаетесь вывести значение переменной ‘i’. Это потому, что, выйдя за цикл for, вы не можете получить доступ к переменной «i». Область действия переменной ‘i’ находится только в блоке кода, в котором она была объявлена в Java. Приступая к JavaScript, вы должны понимать, что, хотя javascript использует блоки кода точно так же, как и в Java, область действия переменной лежит во всей функции, в которой она была объявлена. Это означает, что следующий код будет успешно печатать 10 при выполнении в JavaScript.
public void someMethod(){ for(int i=i;i<10,i++){ //Some usage of variable i } //Prints 10 console.log(i); }
В приведенном выше коде, хотя мы печатали значение переменной после того, как она была объявлена и инициализирована, но за пределами фигурных скобок она работала отлично. Теперь давайте возьмем другой пример.
var n = 1; function someRandomFunction() { console.log(n); // This prints 1. }
В этой функции мы объявили переменную вне функции. И были легко в состоянии распечатать его значение внутри функции, из-за замыканий. Теперь рассмотрим следующий фрагмент кода.
var n = 1; function someRandomFunction() { console.log(n); // This prints undefined. var n = 100; console.log(n); // This prints 100 }
Как ни странно, первый вывод консоли выводит неопределенное значение. Но почему? Почему он не может видеть объявление переменной ‘n’, которая была частью замыкания, как в предыдущем примере? Причиной этого странного поведения является функция под названием «Подъем». Подъем можно объяснить очень простым способом. Для любой переменной, объявленной в области видимости, JavaScript разделяет объявление переменной и инициализацию переменной и переносит объявление в первую строку области и инициализирует переменные в соответствующих строках, в которых они были инициализированы программистом.
Исходя из вышеприведенного объяснения, это то, что JavaScript видел, когда запускал нашу программу.
var n = 1; function someRandomFunction() { var n; //The variable declaration hides under harry potter's cloak. So you cant see it. console.log(n); // Now you know why its undefined here n = 100; //Now the variable gets initialized. console.log(n); // And prints out 100 here. }
Как вы видите в приведенном выше коде, переменная была неопределенной, потому что она была скрыта локальным объявлением переменной в функции. (По крайней мере, они правильно поняли эту часть!). Но затем введите функции! И у вас есть еще несколько неприятных кодов для решения.
Посмотрите на следующий фрагмент кода, в котором мы объявляем переменную, функцию, использующую объявление обычной функции, и вторую функцию, использующую объявление стиля переменной.
function hoistingContainer(){ console.log(n); innerOne(); innerTwo(); //Throws an exception var n = 100; function innerOne(){ console.log('innerOne'); } var innerTwo = function innerTwo(){ console.log('innerTwo'); } //This will execute but the code never //reaches here because of the previous exception innerTwo(); }
В приведенном выше примере запишите различные способы, которыми мы объявили две функции. Если вы запустите приведенный выше код, вы обнаружите, что первый вызов функции innnerTwo () приводит к исключению, тогда как вызов функции innerOne выполняется нормально. Единственная разница между этими двумя функциями заключалась в том, как они были инициализированы. Возможно, можно сделать вывод, что когда функции явно объявлены с использованием переменной вместо обычной записи, инициализация не поднимается. Однако, используя обычную запись объявления функции, даже тело функции поднимается вместе с объявлением функции.
Это подразумевает, что если вы определяете функции, используя стандартную нотацию вместо нотации переменной, вы можете создавать функции в любом месте области действия и использовать их в любой точке одной и той же области независимо от точки их объявления.
Следующая интересная вещь, о которой я хочу поговорить, — это то, что может произойти, если вы случайно забудете использовать ключевое слово «var» при объявлении переменной внутри области видимости. Обычно, когда вы объявляете локальные переменные внутри функции, вы используете ключевое слово var. Только переменные, объявленные с использованием ключевого слова var, становятся локальными. Однако, если вы случайно забудете ключевое слово var, javascript не будет жаловаться, вместо этого он просто создаст свойство с тем же именем в глобальной области видимости, то есть в объекте ‘window’. И, как мы уже знаем, это может иметь катастрофические последствия.
Взгляните на следующий код.
window.constant = 10; //prints 10, the constant from the global //window object, console.log(constant); function outerFunction(){ var name = 'xyz'; //Missing var keyword constant = 20; } //Invoke the function outerFunction(); //Prints undefined since name was //locally scoped in the function console.log(name); //Prints 20, because its value was //reassigned inside the function console.log(constant);
Мораль истории, не забывайте использовать ключевое слово «var» при объявлении локальных переменных внутри области видимости.
Области применения и закрытия.
Замыкания широко используют преимущества области видимости JavaScript. Когда функция объявляется в области действия другой функции, внутренняя функция сохраняет «скрытые ссылки» на переменные в области действия, в которой она была объявлена. Вы также можете думать об этом, как будто функции имеют свою собственную область памяти. Функции — это поведение. Но функции Javascript могут не только содержать поведение, но и действовать как хранилище данных. Вы можете читатьдругая статья мой, в котором я подробно остановился на нескольких примерах.
Точка с запятой.
Это действительно странная особенность в JavaScript. Механизм обработки JavaScript пытается действовать разумно и обнаруживать, если точка с запятой отсутствует там, где ее следовало поставить. Если обнаруживается, что он отсутствует, он просто вставляет пропущенную точку с запятой и выполняет код без предупреждения. Простейшим примером, как показали многие другие, является проблема вставки точки с запятой с помощью оператора return. Следующий код возвращает неопределенное.
function badStyle(){ return { name:'value' }; } function goodStyle(){ return { name:'value' }; } //Runs as expected console.log(goodStyle()); //Prints undefined console.log(badStyle());
Во второй строке выводится неопределенное значение, поскольку, поскольку в javascript ожидалось, что оператор return завершается точкой с запятой, он вставляет его, когда не видит ни одного в функции badStyle. В функции goodStyle она не вставила ее, потому что перед завершением строки она встретила символ для объявления литерала объекта.
Большинство проблем, возникающих из-за вышеупомянутых особенностей языка, можно избежать, написав простой, но прилично отформатированный код. Объявления переменных в верхней части, объявления функций перед их использованием, а также правильное открытие и закрытие фигурных скобок и всегда добавление точки с запятой, когда вы завершаете свои операторы, не только избегают этих проблем, но и делают ваш код читабельным и понятным. Кто знает, потратив всего пару минут на реструктуризацию кода, вы можете сэкономить часы отладки.