Область видимости переменной в Javascript (ликбез)

    Для меня одним из самых проблемных мест Javascript было управление переменными. Излагаю простым русским языком.


    Области видимости переменных


    Переменные в Javascript бывают глобальными и локальными. Глобальная переменная доступна везде, локальная — только в текущей области видимости.

    Технически, глобальные переменные — всего лишь свойства объекта window, поскольку весь код выполняется в его контексте.

    <script>
      alert(location); // сообщит window.location
    </script>
    


    Из этого следует, что глобальные переменные могут затирать свойства window (я уже молчу о том, что они зло, нарушают инкапсуляцию и все такое).

    Объявление переменных


    При присвоении значения неопределенной локальной переменной используется или создается глобальная переменная.

    function foo() {
      a = 2;
      b = 3;
      return a+b;
    }
    alert(a); // undefined
    a = 'очень важное значение';
    alert(a); // очень важное значение
    foo();
    alert(a); // 2
    


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

    Явно объявлять переменные можно и нужно ключевым словом var.

    var a = 2;
    


    Такая строка всегда создает новую локальную переменную. Если объявление происходит вне функций, то она будет глобальной, что вполне логично.

    function foo() {
      var a = 2;
      var b = 3;
      return a+b;
    }
    alert(a); // undefined
    var a = 'очень важное значение';
    alert(a); // очень важное значение
    foo();
    alert(a); // очень важное значение
    


    Как объявить глобальную переменную из функции? Как обратиться к глобальной переменной, если есть локальная с таким же именем? Очень просто — нужно обратиться к ней как к свойству window:

    function foo() {
      var location = 'location';
      alert(location); // вернет 'location'
      alert(window.location); // вернет window.location
      window.a = 'переменная из функции';
    }
    alert(a); // undefined
    foo();
    alert(a); // переменная из функции
    


    Наследование области видимости


    Меня всегда смущало то, что в Javascript можно определять функции внутри функций, а использовать их потом где угодно. Ну да, если посмотреть, точно то же самое можно делать в Ruby, и, наверное, во многих других языках тоже.

    Переменные при этом передаются очень просто: если на момент определения функции переменная существовала, то она будет существовать и внутри функции. Откуда бы ее не вызывали.

    function alertOnTimeout(message, timeout) {
     return setTimeout(function() { 
        // message будет доступен в безымянной функции, переданной таймауту
        alert(message); 
      }, timeout);
    }
    


    Передача кода по старинке — строкой, которая прогоняется через eval() — не попадает под это правило, код исполняется в той области видимости, где и определен.

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

    function myObject() {
      var property = 0;
      // Cамо собой, property будет доступно только внутри объекта.
    }
    


    А еще в Javascript область видимости переменной ограничивается только функциями, а не блоками типа if (привет, Паскаль). Потому удобнее всего объявлять переменные в начале функции.

    this


    А что this? А то, что эта переменная автоматически появляется в методах объектов и затирает значение this из предыдущей области видимости. Решение простое — переприсваивать ее значение другой переменной.

    $('div.with-links').click(function() {
      var theDiv = this; //сохраняем значение this
      $(this).find('a').click(function() {
        alert($(this).attr('href')); // this - это ссылка
        theDiv.remove(); // а theDiv - это все еще дивак
      });
    });
    


    Отдельно замечу, что при оборачивании какой-то поведенческой логики в объект надо помнить, что в создаваемых DOM-событиях значение this самого объекта теряется.

    function myObject() {
      var _this = this; // сохраняем ссылку на родительский объект
      var linkRemoved = false;
    
      $('a').click(function() {
        $(this).remove(); // this - это объект ссылки
        _this.linkRemoved = true; // _this - это родительский объект
      });
    }
    
    Поделиться публикацией
    Ой, у вас баннер убежал!

    Ну, и что?
    Реклама
    Комментарии 24
    • +1
      Спасибо.
      Было уже не раз.
      Да-да, поиск не работает.
      • +10
        Поскольку объекты в Javascript — это тоже типа функции

        Объекты в Javascript — это «типа» объекты. Функция является объектом, но не наоборот.
        • –9
          Да ради бога, но выглядят-то они как функции.
          • +5
            Ничуть. Выглядят они как коллекции других объектов.
            Это просто оператор new принимает имя функции-конструктора.
        • +4
          Еще один человек, который даже не разобрался что же такое javascript, пытается нам что-то объяснить. Черт, да они никогда не закончатся.

          А ведь в интернетах об этом сотни раз писали на сотне разных языках.

          А еще в Javascript область видимости переменной ограничивается только функциями, а не блоками типа if (привет, Паскаль). Потому удобнее всего объявлять переменные в начале функции.
          Ага, а еще ява скрипт вообще не ооп язык — там классов нет.
          • +3
            Javascript очень даже ООП язык.
            • +1
              Мне кажется ashofthedream забыл тег «сарказм».
              • 0
                Судя по всему, вы правы. Признаю, не углядел.
            • 0
              Так автор же сказал «Излагаю простым русским языком.» Никакой попытки привнести монументальность этим «высером» не было!?
              • 0
                Дело тут в другом, автор, из за своей неосведомленности, в данной статье очень расходится с реальностю в которой мы живем. А это очень плохо.
                • 0
                  Относительная оценка. А выдаётся за абсолютную. Если автор связан c языком/технологией на определённом уровне, который позволит ему понять основную суть происходящего (без вникания в тонкости), то для очень простого объяснения (как автор и подаёт), сгодилось бы. Тот уровень, с которого рассматриваете Вы, тоже относительный, так же, кто-нибудь может сказать, что всё, что Вы расскажете, будет расходится с более глубоким уровнем.

                  Технические, конечно, в статье много неверно описано. Но, это удобный вариант для изучающих — они пишут статьи определённого уровня (среднего, выше/нижу среднего и т.д.) и сами учатся на этом.

                  А о чём именно говорите Вы — про «очень расходится с реальностю в которой мы живем»?
                  • 0
                    Удобный вариант для изучающих — понимать что на самом деле происходит. Я, к примеру, сейчас сейчас являюсь начинающим Java разработчиком, и поверьте мне, за последние недели три я наткнулся на столько фундаментальных косяков, непонятностей, неровностей, отличительных особенностей и прочего. Что если бы у меня под рукой не было ни человека, который мог бы объяснить что тут происходит, ни несчитаеных десятков замечательных англоязычных статей в которых обсасываеца каждый шаг виртуальной машины — то я бы за эти две недели так бы и остался человеком, который три часа будет искать банальную ошибку в сравнении двух объектов типа Integer.

                    То, что творится сейчас в мире JS (перечитал ну несравненно много мусора в интернетах когда пытался понять как же работает этот замечательный язык)- так это вообще какое-то безумие. Которое по большей части является плодом догадок разработчиков которые даже и не понимают фундаментальных основ данного языка. И эта статья — очень хороший пример подобного.

                    Если вам нравится выискивать информацию среди потоков откровенного говна, что ж, я Вас понять так и не смогу.
                    • +1
                      > Если вам нравится выискивать информацию среди потоков откровенного говна, что ж, я Вас понять так и не смогу.

                      Моя цель образовательная. Если человек пишет статью, он вкладывает в неё творческий процесс. То, что он пока на среднем уровне понимания технологии — это другой вопрос. И именно этот уровень поднимется за счет комментариев. Но комментариев не вашего плана. Я лишь отметил категоричный тон, как будто «вы знаете как надо, а статья — говно». Я не прав? Вы, кстати, не ответили, что такое «очень расходится с реальностю в которой мы живем». Но да ладно, меня это не столь волнует уже.

                      > То, что творится сейчас в мире JS (перечитал ну несравненно много мусора в интернетах когда пытался понять как же работает этот замечательный язык)- так это вообще какое-то безумие

                      А насколько глубоко вы различаете мусор от немусора в ES? Какова планка? Я ж отметил, статьи бывают разного уровня. В данном случае, у автора — статья: (а) прикладного уровня, (б) разбирающая основы, (в) разбирающая основы поверхностно; (г) полная технических ошибок — но это уже на более углублённом уровне, который автор не затрагивает.

                      Меня же всегда интересует вопрос справедливой оценки ;)

                      P.S.:

                      > Которое по большей части является плодом догадок разработчиков которые даже и не понимают фундаментальных основ данного языка

                      Вот вам «по меньшей части»: javascript.ru/ecmascript-in-detail (автор этих статей понимает фундаментальные основы JS; прошу простить, за то, что обратился к себе в третьем лице).
                      • 0
                        Извиняюсь конечно, если сейчас не получиться выразиться доходчиво…

                        Моя цель образовательная. Если человек пишет статью, он вкладывает в неё творческий процесс. То, что он пока на среднем уровне понимания технологии — это другой вопрос. И именно этот уровень поднимется за счет комментариев. Но комментариев не вашего плана. Я лишь отметил категоричный тон, как будто «вы знаете как надо, а статья — говно». Я не прав? Вы, кстати, не ответили, что такое «очень расходится с реальностю в которой мы живем». Но да ладно, меня это не столь волнует уже.

                        Я тоже так раньше думал, что любой творческий процесс достоин внимания. Что объяснение каких либо основ другим людям укрепит и свое собственное понимание. Но, теперь понимаю что это не так — да, это иногда забавно читать статью в которой человек делиться своим опытом, эмоциями, знаниями. Сейчас мы живем в таком времени, что любая домохозяйка может почувствовать себя именитым журналистом разбирающимся в «теме» и накатать статью на десяток листов А4. Тема может быть любая, но, как я выразился выше — в большинстве своем это мусор, который в силах написать каждый из нас. И он очень мешает если нам необходимы знания «как есть». Ваш коммент ниже, по поводу области видимости переменных, в десятки раз полезнее нежели десяток подобных статей от подобных авторов. Потому что он описывает суть языка, а не догадки человека который только-только начинает разбираться в теме. Да, я еще раз с удовольствием прочитал его, хотя информация, содержащаяся в нем мне давно знакома.

                        По поводу «вы знаете как надо, а статья — говно» — высказываться даже и не хочется, а вот за то, что упустил ваш вопрос по поводу реальности окружающей нас:

                        1. (упомянутое в вашем комментарии) — в JS, в отличии от другого языка по сути то и нет понятия локальная и глобальная переменная. Ибо замыкания такая чтука, что нечто-локальное, становится уже и не совсем локальным, а нечто глобальное может быть и не настолько глобальным — в плане лежать чуть выше по иерархии объектов нежели тот же объект window в браузере.

                        2. (опять же, упомнятнутое, но не рассписанное так же как и первый пункт) про указатель this. После прочтения данной статьи возникает масса вопросов — количество которых возратает если попытаться воспользоваться данным мануалом на практике, после прочтения одного абзаца в котором описывается почему this работает именно так как работает, в голове начинает ломаться привычная схема ооп основанная на классах, где этот указатель жестко привязан к объекту (у меня до сих пор, после довольно продолжительных скачков между php\js\java присутствует некий барьер в 1-2 недели, после которого мое мышление начинает переходить в js way...)

                        А насколько глубоко вы различаете мусор от немусора в ES? Какова планка?
                        Что касаеться ES, то тут планка задрана чуть выше нежели для остальных языков. Если примеры приведенные в статье мне непонятны (т.е. я не могу не запуская бразуер понять что произойдет, а могу только лишь догадываться) — то эта статья не помечается как мусор, в которые входят львиная доля «20 и еще три приема для разработки с помощью JQuery». Да, признаюсь еще в том, что так как js был моим вторым языком, то не делал упор на него, и поэтому большинство статей с чем-то жесткоким и мозговыносящим осталось так и не понято — и потому что не хватало времени на понимание данного материала, и потому что попросту данные знания не являлись жизненно необходимыми.

                        И кстати, спасибо за линк, статьи эти читал, не все конечно, но и после прочтения того что было прочитано также остались некоторые и неосвещенные моменты. Опять же причина понятна, js — не мой основной язык. Да и в ближайшем будущем сталкиваться буду с ним крайне мало.
                        • 0
                          Если примеры приведенные в статье мне непонятны (т.е. я не могу не запуская бразуер понять что произойдет, а могу только лишь догадываться) — то эта статья не помечается как мусор

                          Но я именно это и хотел отметить. Просто от уровня статьи, она не становится «мусором». Мусором она может стать, когда в ней пишется откровенный бред (если ещё позиционировать статью, как профессиональную, её «мусорность» в данном случае ещё больше укрепляется).

                          Тут дело в том, что иногда, в прикладных статьях, используется упрощение терминологии в угоду большей ясности и простоты изложения (кодовое название: «литература для студентов»). Т.е. может быть переврана официальная терминология и очень упрощенны описания механизмов, но, цель — чтобы студент понял основную суть и мог писать прикладные программы. Такая литература может также позиционироваться, как профессиональная (с прикладной точки зрения).

                          В теоретических углублённых статьях (кодовое название: «труды для коллег-профессоров»), это уже недопустимо. Там важна точность информации, в ущерб массовой аудитории (поскольку на каждому интересно изучать (любой) предмет глубоко).

                          Так вот с Вашей позиции («если мне понятны все примеры» статей первого уровня) я должен назвать практически все статьи — мусором. Но это не так. Это просто статьи другого плана, у них другая цель.

                          Даже в бумажных книгах иногда пишут сзади на обложке: «Уровень: начинающие» или «Уровень: опытные/профессионалы» (например, издательство «Питер» так делало).

                          В целом, да, Ваша позиция ясна. Спасибо, успехов.
              • 0
                А что Вы хотели передать, выделив цитату из текста?
              • +4
                Просто праздник какой-то.
                Хотел процитировать наиболее дурацкие фразы, но тогода придётся цитировать всё.
                Рано ещё писать статьи про язык. Вы его не знаете. Вообще.
                • +6
                  Переменные в Javascript бывают глобальными и локальными. Глобальная переменная доступна везде, локальная — только в текущей области видимости.

                  Да, можно и так сказать. Технически же, большой разницы — глобальные/локальные — нет, механизм одинаковый. Есть только цепочка областей видимости, связанная с активным в данный момент контекстом исполнения, и в этой цепочке ищутся переменные — начиная из самого глубокого (активного) контекста — и вверх — до глобального.

                  Технически, глобальные переменные — всего лишь свойства объекта window, поскольку весь код выполняется в его контексте.

                  Да, для упрощённого описания, и для browser scripting, можно и так сказать. Но надо понимать, что глобальные переменные — это, по стандарту, свойства глобального объекта, а не window. И, хоть в стандарте сказано, что window в DOM-модели — это рекурсивная ссылка на глобальный объект, в действительности это не всегда так. По факту же, да, обращение к свойствам window будет возвращать переменные глобального контекста.

                  При присвоении значения неопределенной локальной переменной используется или создается глобальная переменная.

                  Вот самая распространённая ошибка (опять же, с технической точки зрения). Запомните, переменные объявляются только с ключевым словом var. Присвоение же вроде:

                  a = 10;

                  лишь создаёт очередное свойство (но не переменную) в глобальном объекте. «Не переменную» не в том смысле, что её нельзя изменить, а «не переменную» в понятии переменных в ECMAscript. То, что в обоих случаях, это будут свойства глобального объекта — это уже следствие. Но, есть существенная разница между присвоением неопределённому идентификатору и объявлением переменной. Подробней.

                  Но для упрощённого описания и предостережения, Ваше описание подойдёт.

                  Явно объявлять переменные можно и нужно ключевым словом var

                  Можно убрать слова «явно» и «можно» ;)

                  Передача кода по старинке — строкой, которая прогоняется через eval() — не попадает под это правило, код исполняется в той области видимости, где и определен.

                  На самом деле, это связано с тем, что в этом случае создаётся функция, как если бы «new Function», а такие функции имеют в качестве [[Scope]] только глобальный объект. Подробней.

                  var a = 10;
                  (function () {
                      var a = 100;
                      setTimeout('alert(a);', 0); // 10, а не 100
                  })();


                  Поэтому, конечно, удобней использовать в setTimeout-e сразу объект-функцию, а не строку.

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

                  function myObject() {
                  var property = 0;
                  // Cамо собой, property будет доступно только внутри объекта.
                  }

                  Ну здесь Вы, конечно, отсебятину написали ;)

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

                  Не определяется «свойство объекта, как переменная» в данном случае. Если бы Вы говорили о глобальном контексте, тогда да — объявляя переменную, Вы создаёте свойство глобального объекта.

                  Иначе можно подумать, Вы имеете в виду, говоря о «свойстве объекта» в данном примере, что-то типа «myObject.property».

                  var — в контексте функции создаёт свойство в объекте переменных контекста функции. Дальше, если ссылок на этот объект переменных нет, он уничтожается. Если есть (например, замыкание) — остаётся существовать.

                  А еще в Javascript область видимости переменной ограничивается только функциями, а не блоками типа if (привет, Паскаль). Потому удобнее всего объявлять переменные в начале функции.

                  Есть такая практика у некоторых (поскольку, технически, действительно, var-ы парсятся при входе в контекст и доступны на протяжении всего контекста, независимо от того, объявили ли мы их в блоке if или нет). Хотя, иной раз удобней объявить переменную по месту.

                  А что this?

                  А this — вообще больная тема многих начинающих программировать на JS. Главное, что нужно запомнить, что this всегда определяется при вызове кода контекста и передаётся вызывающей стороной (caller-ом). Уже сходу обычную функцию можно вызвать с двумя разными значениями this (по форме выражения вызова):

                  function f() {
                    alert(this);
                  }
                  
                  f(); // global (window)
                  
                  f.prototype.constructor(); // f.prototype

                  Но вообще, это отдельная большая тема. Если интересно — можете почитать подробней (хотя, именно эту заметку я планирую переработать).

                  Успехов в изучении ES.
                  • 0
                    на ваши ссылки Опера ругается — «opera:illegal-url-0»
                    • 0
                      Парсер слово javascript съел (защита от XSS). Вместо буквы «с» написал "&/#99;" Причем, если использовать тег «а» с href="...". Выше написал просто ссылку — там не съел.
                    • 0
                      (некропост, ага)
                      А все эти заморочки сделаны с определенной целью?
                      • 0
                        до последнего момента ждал упоминания замыканий :)
                        • 0
                          >>Меня всегда смущало то, что в Javascript можно определять функции внутри функций, а использовать их потом где угодно.
                          Определить можно с ключевым словом var и она будет недоступна извне.
                          >>А еще в Javascript область видимости переменной ограничивается только функциями, а не блоками типа if (привет, Паскаль).
                          Интересно, расскажите, пожалуйста, где в Паскале область видимости ограничивается блоками? (Может, я чего-то не понимаю?)

                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                          Самое читаемое