Изучаем JavaScript, ч. 2: Функции: Подбираемся ближе к функциям JavaScript

мая 16, 2011  |  Published in ClientSide, JavaScript  |  1 Comment

В прошлой части мы познакомились с функциями в JavaScript, а в этой мы рассмотрим их более подробно опираясь на уже имеющиеся из прошой части RubyDev JavaScript учебника.

Область видимости и контекст выполнения

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

Функции — это особые объекты, которые кроме пар свойство — значение могут содержать еще и выполняемый код. Как и любой объект, функция отгораживается от внешнего мира, создает свой собственный маленький мирок, который называется областью видимости или пространством. Переменные объявленные в теле функции являются локальными, то есть они замкнуты в функции, в ее мальньком мирке. При этом областью видимости таких переменных является тело функции, обратиться к ним из вне невозможно! Пример функции с внутренними переменными:

function helloFunction(){
  var hello = "Hello!";
  alert(hello);
}

helloFunction();//Hello!

alert(hello);//Переменной hello в текущем пространстве вызова alert не существует!


Пояснения:
1. Переменная hello в теле функции является локальной — замкнутой. Поскольку она определена в функции,мы можем воспользоваться ею в любом месте функции.
2. Во втором случае мы пытаемся вызвать глобальную переменную hello, которая не объявлена. Глобальная переменная — это переменная объявленная вне каких-либо вложений или объявленная без использования выражения var.
3. Объявление переменной без выражения var называется неявным.

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

var hello = "Hi!";
var bye = "Bye!";

function helloFunction(){
  var hello = "Hello!";
  alert(hello); //Hello!
  alert(bye); //Bye!
  bye = "Bye-bye!";
}

helloFunction();

alert(hello); //Hi!
alert(bye); //Bye-bye!

alert(window.hello); //Hi!
alert(window.bye); //Bye-bye!

Пояснения:
1. В примере выше мы объявили две глобальные переменные hello и bye, далее мы определили локальную переменную hello в контексте функции, таким образом функция находя объявление переменной hello внутри себя не исчет ее во внешнем мире, тоестьглобальная переменная в конексте функции подменяется локальной. Поскольку переменная bye не определена в функции, то производится ее поиск во внешнем мире, то есть вне контекста функции, таким образом используется найденая глобальная переменная bye. Далее производится переопределение глобальной переменной bye в контексте функции, это возможно по той причине, что определение bye происходит без выражения var. В конце показано, что глобальная переменная hello не была переопределеная в функции, локальная переменная hello — это ссылка совсем на другой объект. Также в конце продемонстрировано что глобальные переменные являются свойствами объекта window.
2. Каждая переменная доступна не только в своей области видимости, но и по аналогии с глобальными переменными, во всех вложенных областях видимости.

В JavaScript только функции создают область видимости, пример:

var array = [1,2,3];

while(true){
  var rubydev = "RubyDev.ru";
  break;
}

alert(rubydev); // RubyDev.ru

Пояснения:
1. Хотя переменная rubydev была объявлена внутри блока кода и с использованием выражения var, она не является локальной для этого блока кода. Она является локальной для области в которой находится блок.

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

var age = 30;

myObj = {
  age : 40,
  name : "Frank",
  lname: "Sinatra",
}



myObj.getInfo = function(){
  alert(this.name + " " + this.lname + " " + age + " age old.");
}

myObj.getInfo(); //Frank Sinatra 30 age old.

myObj.getTrueInfo = function(){
  alert(this.name + " " + this.lname + " " + this.age + " age old.");
}

myObj.getTrueInfo(); //Frank Sinatra 40 age old.

Пояснения:
1. Был создан объект с тремя свойствами: name, lname и age, затем этому объекту были добавлены два метода, первый из которых предоставляет ложную информацию об объекте, а второй истинную. Сами функции были объявлены в глобальном контексте, плагодаря чему им доступны все переменные, которые доступны в глобальной области видимости, то есть переменная age со значением 30. Поскольку функции вызываются в контексте объекта obj, то указатели this используемые в них указывают на контекст вызова, то есть функциям доступны свойства объектов. В функции getInfo() перед переменной age нет указателя this, поэтому подставляется значение не из свойства объекта, а из области определения функции, то есть подставлется значение глобальной переменной age.

Еще один пример:

myObj = {
  age : 40,
  name : "Frank",
  lname: "Sinatra",
}

function getTrueInfo(){
  alert(this.name + " " + this.lname + " " + this.age + " age old.");
}

getTrueInfo.call(myObj);//Frank Sinatra 40 age old.

Пояснения:
1. Метод call функции getTrueInfo (это стандартный метод объекта функционального типа), позволяет вызвать функцию в указанном контексте, в данном случае в контексте объекта myObj. Этот случай не исключение и this ссылается на контекст в котором функция вызвана.

В JavaScript существует специальный скрытый объект [[scope]]. Объект [[scope]] занимается хранением всех локальных переменных определенной функции, у каждой функции свой объект [[scope]].

Немного о замыканиях

Для начала представьте себе замыкания просто как одну функцию объявленную в теле другой, для удобства назовем их внутренней и вмещающей функциями, а определение внутренней функции во вмещающей назовем самим замыканием. Итак, замкнув одну функцию в теле другой мы получаем внутреннюю функцию огражденную от внешнего мира, ей доступны только переменные вмещающей функции (всего дерева вмещающих функций), глобальные переменные, ну и конечно же свои собственные. Замыкание состоит не только в том, что одна функция прячется (замыкается) в другой, но и то, что внутренняя функция сохраняет в себе все переменные с их состоянием на момент завершения работы вмещающей функции (функций). Это «сохранение» заключается в том, что внутренняя функция получает ссылку на объект [[scope]] вмещающей функции и, не смотря на то, что вмещающая функция выполнила свою работу и ее объект был уничтожен, внутренняя функция имеет доступ к сохранившемуся объекту [[scope]], а значит ко всем переменным вмещающей функции. Когда имеется многоуровневая вложенность, то каждый [[scope]] вмещающей функции наслаивается на [[scope]] внутренней. Простой пример замыкания:

function outerFunction(property){
  var rubydev = "RubyDev.ru";
  var inner = function () {
    return {
      property: property,
      rubydev: rubydev,
      inner: inner
    };
  };
  return inner;
}

var someVar = outerFunction("JavaScript");
alert(someVar().property); //JavaScript
alert(someVar().rubydev);  //RubyDev.ru
alert(someVar().inner);    //возвратит саму себя

Пояснения:
1. Внутренняя функция inner имеет доступ ко всем переменным вмещающей функции outerFunction в том числе и к переменной inner, которая ссылается на внутреннюю функцию, в такой способ и достигается то, что функция может вернуть сама себя. такие функциии, которые возвращают сами себя называются самовоспроизводящимися.

Более подробно о замыканиях узнаете в одной из следущих глав.

Лучшая благодарность автору — ваши комментарии!

Tags: ,

Responses

  1. yerlankussainov says:

    мая 17, 2011 at 09:39 (#)

    Спасибо!

Leave a Response

Для подсветки кода используйте BB - коды: [language]...[/language], где language может быть: ruby, javascript, css, html.