Перейти к содержимому



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

#1 ShowPrint

ShowPrint
  • Пользователь PRO
  • 2 536 сообщений
  • Репутация: 660
0

Отправлено 05 Декабрь 2017 - 17:54

Hi, all!

 

Продолжая изобретения велосипедов знакомлюсь всё с новыми и новыми особенностями JS и ему подобных.

Столкнулся с проблемами связанными с областью видимости функций внутри скриптов.

 

У меня для каждой страницы получается два файла скриптов: первый - общий для всех, второй - индивидуальный для страницы.

Собственно чтоб не грузить лишнего и не раздувать в размере "общий".

Получается что "общий" при переходе со страницы на страницу грузится из кеша и догружает небольшой "индивидуальный".

"Общий", дабы быстрее выдать страницу посетителю, отрабатывают по событию document.ready

 

Условно "общий" скрипт следующего вида:

$(document).ready(function(){ // ждём окончания загрузки страницы
  var ... // переменные
  function myFunc(){...} // ряд пользовательских функций и операций
  $.ajax({
    url:'/script/local.js', // загрузка "индивидуального" скрипта
    cache:true,
    dataType:'script',
    success:function(){
      // после загрузки производим всякие манипуляции
    }
  });
});

Ну и "индивидуальные" скрипты совершенно обычные - какие-то манипуляции, навешивание и обработка событий и прочая лабуда.

 

Гимор заключается в следующем:

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

alert(typeof(myFunc));

 выдает результат undefined

 

Методом тыка и экспериментов нашел два варианта решения проблемы:

  1. из "основного" скрипта убрать первую строку ( $(document).ready(function(){...}); ), но это откладывает отсрочку события окончания загрузки страницы, что не очень-то греет душу (
  2. в "индивидуальном" скрипте продублировать функцию, что и увеличивает размер скрипта, и как-то неправильно.

В общем, оба найденных варианта получаются как-то не по феншую  :(

 

Собственно вопрос: есть-ли какие-то варианты более изящного решения?

Логически предполагаю, что должно быть какое-то решение, либо в виде сделать "функцию myFunc глобальной" - видимой во всех подгружаемых (индивидуальных) скриптах, либо возможность передачи функции подгружаемым скриптам в виде объекта.

 

Прошу содействия в виде подсказки или ссылки где поискать и что почитать (желательно с примерами). Может хоть правильное название какого-нибудь правильного слова которое можно загуглить... Кто-чем может, плз...

Оффтопик


 

 

  • 0
MasterWEBS: третий дом - моё хобби и увлечение... Второй дом: работа - не меньше 12 часов в день...
Первый дом - под охраной: "Осторожно - злая жена!" (дрессировалась долго и надёжно) /*ссылку не просите - не дам!*/


robot

robot
  • Пользователь PRO
  • 2 652 сообщений
  • Репутация: 85
Советую обратить внимание на следующее:
  1. Работа с переменными Javascript
  2. Как поместить в значение формы переменную JavaScript?
  3. Что Делает функция javascript
  4. ООП в JavaScript
  5. Помогите с функцией

#2 Ixman

Ixman
  • Пользователь PRO
  • 2 699 сообщений
  • Репутация: 651

Отправлено 05 Декабрь 2017 - 19:41

Михаил, не знаю в тему или нет, но может стоит потыкать так. Так как у меня ощущение такое, что индивидуальный загружается раньше общего и из-за этого не видит общих функций. Нэ?


  • 0


#3 fedornabilkin

fedornabilkin
  • Пользователь
  • 745 сообщений
  • Репутация: 107

Отправлено 05 Декабрь 2017 - 20:06

Правильное название правильного слова - LexicalEnvironment или лексическое окружение.
Если ты используешь document.ready, то аргументом передаешь в него анонимную функцию.

$(document).ready(function(){var foo; myFunc(){}});

Тоже самое, только по полочкам:

var other = readyFunc(){
    var foo;
    myFunc(){};
};

$.document.ready(other);

Так вот у myFunc() свое лексическое окружение, которое не выходит за рамки readyFunc().

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

 

Так вот $.ajax() принимает объект, в котором success присваиваем анонимную функцию для получения ответа от сервера. Следовательно все твои индивидуальные видны внутри анонимной функции.

 

Один из вариантов решения (сам не пробовал).

В общем файле создаешь объект с необходимыми функциями и потом верти ими как хочешь, главное правильно начать. Запустишь, посмотри логи.

var myGlobalFunctions = function(){
    var cache = {}; // для каждого экземпляра своя переменная (замыкания - это отсюда начинаются)
    return {
        first: function(arg, varg){
            return arg + varg;
        },
        second: function(iname, fname){
            return 'Привет, ' +iname+ ' ' +fname;
        },
        setCache: function(key, val){
            cache[key] = val;
        },
        getCache: function(key){
            if(!key){
                return cache;
            }
            return typeof cache['key'] !== "undefined" ? cache['key']: null;
        }
    };
};

$(document).ready(function(){
    var myLocalFunc = myGlobalFunctions(); // получаем в переменную список функций
    var res_second_func = myLocalFunc.second('Имя', 'Фамилия'); // вызываем необходимую функцию
    console.log('second', res_second_func); // зырим, что получилось

    // сохраним значение в кэш
    myLocalFunc.setCache('name', 'Имя');
    // позырим, что в кэше
    console.log('myLocalFunc', myLocalFunc.getCache());

    // можно создать второй комплект (он совершенно новый и с чистым кэшем)
    // с таким же набором функций и передать его куда угодно или использовать по своему усмотрению
    var yourLocalFunc = myGlobalFunctions();
    // кэш пустой
    console.log('yourLocalFunc', yourLocalFunc.getCache());

    // шлем аякс и внутрях видим переменную myLocalFunc,
    // потому что она глобальна в пределах анонимной функции document ready

    // $.ajax({
    //     url:'/script/local.js', // загрузка "индивидуального" скрипта
    //     cache:true,
    //     dataType:'script',
    //     success:function(){
    //         // после загрузки производим всякие манипуляции
    //         // myLocalFunc тут доступна
    //     }
    // });
});

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


  • 1
Как часто в горестной разлуке,В моей блуждающей судьбе, ФО, я думал о тебе.


#4 ShowPrint

ShowPrint
    Topic Starter
  • Пользователь PRO
  • 2 536 сообщений
  • Репутация: 660

Отправлено 05 Декабрь 2017 - 21:04

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

Иван, я так не думаю...  :( (учебник по ссылке: "настольная книга" - всегда открыт в отдельной вкладке браузера, также как форум  :D )

 

 

Из "эйчтиэмэля" общий скрипт гружу также аяксом в асинхроне:

<script>
  $.ajax({url:'/script/main.js',cache:true,dataType:'script'});
</script>

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

Пробовал переделать объявление функции через var - не помогло, результат тот же...

 

"Не думаю", т.к. не понимаю следующей логики - когда скрипт выполняется без ожидания окончания загрузки дерева DOM, то он работает... Здесь что-то с "видимостью" не так...

 

О... Аха... Вот и Федор написал умных слов!!!...

 

 

Правильное название правильного слова - LexicalEnvironment или лексическое окружение.

Значит правильные слова я нашёл верно, про лексическое окружение и читал... Не до конца сложилось в голове понимание, но с твоим примером буду пробовать колдовать и сложить аккуратный стог в голове, чтоб не торчало в разные стороны...

 

 

 

у myFunc() свое лексическое окружение, которое не выходит за рамки readyFunc()

То есть содержимое local.js не видит лексического окружения в котором создана myFunc даже не смотря на то, что этот local.js загружен тем самым лексическим окружением? Так?

 

 

Получается, что если я съел конфетку - она еще не стала частью меня самого? В этом ошибка? Если да, то как-то неправильно получается: я же не стырил конфетку, я её достал из своего же кармана...  :(

 

Я вычитал вот это (по ссылке выше)


 

Интерпретатор, при доступе к переменной, сначала пытается найти переменную в текущем LexicalEnvironment, а затем, если её нет – ищет во внешнем объекте переменных. В данном случае им является window.

Такой порядок поиска возможен благодаря тому, что ссылка на внешний объект переменных хранится в специальном внутреннем свойстве функции, которое называется [[Scope]]. Это свойство закрыто от прямого доступа, но знание о нём очень важно для понимания того, как работает JavaScript.

При создании функция получает скрытое свойство [[Scope]], которое ссылается на лексическое окружение, в котором она была создана.

Просто я решил, что если в лексическом окружении local.js не нашлось myFunc, то он найдёт её "во внешнем объекте переменных", то бишь "внешним" я посчитал основной скрипт...

 

Буду пробовать разбираться с предложенным вариантом решения, это вполне понятный пример. Если бы я насерфил такой, то не было бы топика, но увы...

 

Сразу после беглого просмотра и осмысления вопрос: правильно я понимаю, что функции setCache и getCache типа "отладочные" и нужны для проверки работоспособности? (в смысле "после отладки можно удалить безболезненно?")

 

@fedornabilkin, гранд-мерси за пример!!!  :smile-thumb-up:

 

Оффтопик


  • 0
MasterWEBS: третий дом - моё хобби и увлечение... Второй дом: работа - не меньше 12 часов в день...
Первый дом - под охраной: "Осторожно - злая жена!" (дрессировалась долго и надёжно) /*ссылку не просите - не дам!*/


#5 fedornabilkin

fedornabilkin
  • Пользователь
  • 745 сообщений
  • Репутация: 107

Отправлено 06 Декабрь 2017 - 09:27

Сразу после беглого просмотра и осмысления вопрос: правильно я понимаю, что функции setCache и getCache типа "отладочные" и нужны для проверки работоспособности? (в смысле "после отладки можно удалить безболезненно?")

Да нет, они вполне себе самые обычные функции, которые можно использовать для записи и получения данных. Но, если нет им применения, то можно и удалить.

 

С конфетой все правильно, только сложность в том, что ты ее можешь достать только из одного кармана. Из других карманов конфета будет недоступна. А когда ты ее съел (передал в переменной), то она уже доступна для всего организма.

Просто я решил, что если в лексическом окружении local.js не нашлось myFunc, то он найдёт её "во внешнем объекте переменных", то бишь "внешним" я посчитал основной скрипт...

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


  • 0
Как часто в горестной разлуке,В моей блуждающей судьбе, ФО, я думал о тебе.


#6 ShowPrint

ShowPrint
    Topic Starter
  • Пользователь PRO
  • 2 536 сообщений
  • Репутация: 660

Отправлено 06 Декабрь 2017 - 15:22

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

Оффтопик

 

Федор, а есть в природе варианты явяскриптом подтянуть внешний скрипт в своё же лексическое окружение? может не через $.ajax и не через $.getScript - на самом деле после того как я основной запускаю по окончанию загрузки DOM и в асинхроне, то пофиг как он будет подтягиваться - всяко получится асинхрон... какой-нить вариант типа пыховой include/require?

 

Я просто рассматривая всякие js скрипты обращал внимание на то, что встречаются вещи типа объявления функций взятые в скобки (инкапсуляция?) или ещё объявление начинающееся с восклицательного знака...

Или это всё уже из совершенно другой оперы и не стоит мне как любителю лезть настолько глубоко?


  • 0
MasterWEBS: третий дом - моё хобби и увлечение... Второй дом: работа - не меньше 12 часов в день...
Первый дом - под охраной: "Осторожно - злая жена!" (дрессировалась долго и надёжно) /*ссылку не просите - не дам!*/


#7 fedornabilkin

fedornabilkin
  • Пользователь
  • 745 сообщений
  • Репутация: 107

Отправлено 06 Декабрь 2017 - 22:13

Я сейчас не совсем понял. Если хочешь использовать функции из общего файла в индивидуальном, то я написал пример. По идее должно работать. Если хочешь использовать функции из индивидуального файла в общем, то скорее всего что-то тут не так. Хотя есть еще колбэки (функции обратного вызова). Когда индивидуальный загрузится, то в колбэк передаешь функции индивидуального файла и пользуешься ими в общем. Соответственно все использование произойдет после загрузки. Такой вариант очень плохой, потому что быстро запутаешься.

Если я тебя не понял, напиши на пальцах, где какие функции и когда ты их хочешь использовать.


  • 0
Как часто в горестной разлуке,В моей блуждающей судьбе, ФО, я думал о тебе.


#8 ShowPrint

ShowPrint
    Topic Starter
  • Пользователь PRO
  • 2 536 сообщений
  • Репутация: 660

Отправлено 06 Декабрь 2017 - 22:33

Я сейчас не совсем понял

Неее... Всё понял правильно и еще раз спасибо за помощь!

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

Но видимо такова была задумка создателей JS и они руководствовались какими-то соображениями при этом. Соответственно самым правильным ответом на моё "почему" будет "потому что так"  :)

 

Я полез "в дебри" и пытаться разобраться с остальным. Видимо не даёт мне покоя отсутствие понимания принципа работы которое встречаю в виде функций обёрнутых в скобки. Вот и сейчас разглядываю, разбираюсь и пытаюсь понять одну из плюшек по ссылке на блог, которую "подкинул" Иван (тынц), вижу аналогичное обёртывание в коде с частью jQuery и наступает "бамц" (отсутствие понимания)

 

Забей... Буду реализовывать предложенный тобой вариант. Это так просто - не люблю тупо копипастить, предпочитаю понимать что делает код который я ставлю на сайт )))

 

UPD

а есть в природе варианты явяскриптом подтянуть внешний скрипт в своё же лексическое окружение ... какой-нить вариант типа пыховой include/require?

"На пальцах": имел в виду какой-то вариант чтоб интерпретатор начал заполнение лексического окружения, потом загрузил что нужно (в моём случае "индивидуальный" скрипт и потом продолжил формирование лексического окружения уже с этим загруженным скриптом  :D

 

Предвижу ответ "Много хочешь, мы не ищем лёгких путей"  :lol:


Сообщение отредактировал ShowPrint: 06 Декабрь 2017 - 23:56

  • 0
MasterWEBS: третий дом - моё хобби и увлечение... Второй дом: работа - не меньше 12 часов в день...
Первый дом - под охраной: "Осторожно - злая жена!" (дрессировалась долго и надёжно) /*ссылку не просите - не дам!*/


#9 fedornabilkin

fedornabilkin
  • Пользователь
  • 745 сообщений
  • Репутация: 107

Отправлено 07 Декабрь 2017 - 14:39

$.ajax это самостоятельная функция и у нее есть еще success, тоже самостоятельная функция. Когда грузишь скрипт, он появляется на два уровня ниже твоего общего лексического окружения. Поэтому ты не видишь функции индивидуального скрипта в общем окружении.

Пыховый include/require это вот твоя подгрузка ajax. Если хочешь заполнять лексическое окружение, вероятно, тебе подойдет прототипное наследование. Я такой подход использовал в SAR.


До сих пор сомневаюсь в целесообразности подгрузки скриптов ajax'ом. Не на то тратишь драгоценное время.


  • 0
Как часто в горестной разлуке,В моей блуждающей судьбе, ФО, я думал о тебе.


#10 ShowPrint

ShowPrint
    Topic Starter
  • Пользователь PRO
  • 2 536 сообщений
  • Репутация: 660

Отправлено 07 Декабрь 2017 - 14:42

@fedornabilkin, большое спасибо, более-менее осознал, бум дальше разбираться! =)


  • 0
MasterWEBS: третий дом - моё хобби и увлечение... Второй дом: работа - не меньше 12 часов в день...
Первый дом - под охраной: "Осторожно - злая жена!" (дрессировалась долго и надёжно) /*ссылку не просите - не дам!*/


robot

robot
  • Пользователь PRO
  • 2 652 сообщений
  • Репутация: 85


Оформление форума – IPBSkins.ru