Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   использование замыкания (https://javascript.ru/forum/misc/11614-ispolzovanie-zamykaniya.html)

jetli13 03.09.2010 13:24

использование замыкания
 
Такой вот вопрос

var a;

function getA()
{
  if (typeof(a) == 'undefined')
  {
    a = 1 + 1;
  }

  return a;
}

var b = getA();
var c = getA();


Можно ли в сделать то же самое но не объявляя переменную 'а' в глобальной области? Ну и чтоб а вычислялось 1 раз.

Skipp 03.09.2010 13:48

Что вы хотите этим сделать?

with-love-from-siberia 03.09.2010 14:14

Можно. Вариантов несколько. Только приведенный Вами пример говорит о бесполезности этого. На самом деле это может быть весьма полезным для создания единственного экземпляра объекта - фабрика объектов.

Sweet 03.09.2010 14:50

Учи основы!!! На сайте куча интересного, полезного, написанного доступным языком материала!
function getA(){
  var a = 1+1;
  return a;
}

var b = getA(), c = getA();

jetli13 03.09.2010 14:56

Skipp,with-love-from-siberia,
я хочу вычислять значение один раз.
которое буду использовать
with-love-from-siberia,
Цитата:

Можно. Вариантов несколько.
можно пример?

jetli13 03.09.2010 15:23

Sweet,
Спасибо за В'аш совет.
Только вот код написанный после В'ашего совета проблему не решает. Я ведь правильно понял, В'ы предложили решение?

Riim 03.09.2010 15:24

var getA = (function() {
var a;
return function getA()
{
  if (typeof(a) == 'undefined')
  {
    a = 1 + 1;
  }
 
  return a;
};
})();

var b = getA();
var c = getA();

jetli13 03.09.2010 15:33

Riim,
Спасибо! То что нужно!

with-love-from-siberia 03.09.2010 17:15

Цитата:

Сообщение от Sweet
Извращенец...

: )

Вот еще:
function getA()
{
    if ( typeof arguments.callee.a == 'undefined') {
        arguments.callee.a = 1 + 1;
    }
    return arguments.callee.a;
};

alert(getA());
alert(getA());

Sweet 03.09.2010 17:26

Цитата:

Сообщение от with-love-from-siberia
Вот еще

Ну нет, кстати.. Я так понял человеку нужно, чтобы к переменной не было доступа. Я только не понял, почему мой вариант "не решил его проблему"
UPD: Есть еще такой вариант:
function getA(){
  return 1+1;
};
:)

Riim 03.09.2010 17:48

with-love-from-siberia, в теме есть про замыкания, но да, тоже вариант.

jetli13 03.09.2010 18:06

Цитата:

function getA(){
return 1+1;
};
Данный вариант не подходит т.к. каждый раз при вызове ф-ции будет происходить вычисление выражения. В примере 1+1, в реале выражение может быть сколь угодно сложное и ресурсоемкое. Поэтому я искал вариант который позволит произвести вычисление один раз, далее же чтоб я просто мог использовать результат вычисления.

Sweet 03.09.2010 18:15

Цитата:

Сообщение от jetli13
в реале выражение может быть сколь угодно сложное и ресурсоемкое

Это многое объясняет:)

jetli13 03.09.2010 18:17

Sweet,
Рад что мы поняли друг друга!

Sweet 03.09.2010 18:23

А так не проще ли?
var getA = (function(){
  var a = 1 + 1;
  return a;
})();
var b = getA;
Или так?
var getA = 1 + 1;
var b = getA;

jetli13 03.09.2010 18:26

Цитата:

А так не проще ли?
показать чистый исходник в новом окнеСкрыть/показать номера строкпечать кода с сохранением подсветки
var getA = (function(){
var a = 1 + 1;
return a;
})();
var b = getA;
Или так?
var getA = 1 + 1;
var b = getA;
Проще, но - в этих случаях getA будет торчать в глобальной области памяти.
А это и было одним из условий вопроса

Sweet 03.09.2010 18:35

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

jetli13 03.09.2010 20:34

Цитата:

getA будет по-любому торчать в глобальной области. Я просто не могу понять, почему так важно, чтобы она была функцией, возвращающей значение "а", а не самой "а"
потому что если это будет не функция, то для инициализации переменной мне придется произвести вычисление выражения. А в случае функции я возможно и не вызову ее ни разу.

Sweet 03.09.2010 20:42

Все, вопросов больше не имею. Тогда вариант Riim'а действительно то, что надо.

jetli13 03.09.2010 20:57

Sweet,
Гут! )

jetli13 03.09.2010 20:58

Sweet,
Ксати, буду признателен, ели вы всеже отредактируете сообщение за которое я вам поставил минус

jetli13 03.09.2010 21:05

with-love-from-siberia,
спасибо!

Sweet 03.09.2010 21:15

Цитата:

Сообщение от jetli13
Ксати, буду признателен, ели вы всеже отредактируете сообщение за которое я вам поставил минус

А как мне узнать, за какое сообщение я получил минус???

jetli13 03.09.2010 21:18

Sweet,
ответил в личку

Kolyaj 03.09.2010 21:30

Лучше так
function getA() {
    var a = 1 + 1;
    getA = function() {
        return a;
    };
    return a;
}

B~Vladi 03.09.2010 21:32

:blink:
Интересный вариант...

with-love-from-siberia 03.09.2010 21:41

Kolyaj, поделитесь - чем лучше?

jetli13 03.09.2010 21:46

Kolyaj,
Спасибо! Круто!
with-love-from-siberia,
На мой взгляд не то что лучше, но более красивый или простой чтоли...

B~Vladi 03.09.2010 22:33

В примере от Kolyaj создается один scope, потому что выполняется всегда 1 функция, а не две. Меньший расход памяти.

Kolyaj 03.09.2010 22:36

Цитата:

Сообщение от with-love-from-siberia
поделитесь - чем лучше?

По сравнению с вариантом Riim? После вычисления значения, в последующих вызовах, никаких проверок не производится, а просто возвращается значение.

inGray 03.09.2010 22:57

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

jetli13 03.09.2010 23:14

B~Vladi,Kolyaj,
inGray,
более веские аргументы! + всем! Спасибо!

B~Vladi 04.09.2010 12:18

Цитата:

Сообщение от Kolyaj
никаких проверок не производится, а просто возвращается значение.

Цитата:

Сообщение от B~Vladi
Меньший расход памяти.

Я все на расход памяти смотрю последнее время. Наверно из-за огнелиса, отъедающего гигабайты оперативы.
В любом случае способ отличный, но далеко не везде можно применять. Беру на заметку:)

offtop:
Где можно почитать про бинарные операторы? Интересует именно использование их для оптимизации (в том числе и расхода памяти). Желательно с конкретными примерами.

Kolyaj 04.09.2010 14:27

Цитата:

Сообщение от B~Vladi
Я все на расход памяти смотрю последнее время.

Да обе причины фигня, не надо оптимизировать то, что не тормозит.

Цитата:

Сообщение от B~Vladi
Наверно из-за огнелиса, отъедающего гигабайты оперативы.

Это расширения отъедают, как правило.

B~Vladi 04.09.2010 15:36

Цитата:

Сообщение от Kolyaj
Это расширения отъедают, как правило.

Да, я знаю про firebag. Видел такой код, который постепенно съедал всю память системы - через таймеры шли запросы на сервер. Вот это проблема:)

float 04.09.2010 16:31

Цитата:

Где можно почитать про бинарные операторы? Интересует именно использование их для оптимизации (в том числе и расхода памяти). Желательно с конкретными примерами.
Обычно они описываются в самоучителях по языкам более низкого уровня.
Например в самоучителе по C# видел такие строчки:
мол бинарные операторы используются в ресурсоёмких программах, вроде перекодировки видео.
Сам я честно говоря не вижу в них особой выгоды. Ну да ладно не будем ворошить.
В вашей же проблеме:
Цитата:

через таймеры шли запросы на сервер
не вижу чем они могут помочь.

B@rmaley.e><e 04.09.2010 18:38

Только не бинарные операторы, а побитовые. Бинарные операторы это те, что принимают 2 операнда.

В плане расходов памяти - можно, например, упаковать кучу Boolean'ов в один Number. Можно строки сжимать (хотя тут побитовые операции лишь средство реализации алгоритма). Но в JS реальной нужды, ИМХО, в этом нет и польза микроскопическая.

B~Vladi 04.09.2010 23:12

Цитата:

Сообщение от float
не вижу чем они могут помочь.

Это как пример плохой программы, к побитовым операторам отношения не имеет.
Цитата:

Сообщение от B@rmaley.e><e
Только не бинарные операторы, а побитовые.

Ну да.
B@rmaley.e><e,
Пару примеров в студию)

B@rmaley.e><e 05.09.2010 00:14

Могу показать, как упаковать boolean'ы в number.
Вся магия заключается в том, что реально нужен для boolean'а всего 1 (один) бит, но занимает он 8 (?) бит.
Можно использовать Number как массив 8 (и более, зависит от размерности типа в битах) Boolean'ов.

Пусть для начала n = 0. Это означает, что все boolean'ы по-умолчанию установлены в false. Двоичная форма 0 = 0b00000000.
Чтение
Чтобы прочитать i-ый (договоримся отсчитывать элементы с конца, нумерация начинается с нуля) символ, используем побитовое И.
n & (1 << i)
вернет нам 0, если значение в этой ячейке ложно или степень двойки (2^i), если значение истинно.

Запись
Чтобы записать, например в первую (нумеровать будем с конца) ячейку 1, нужно использовать побитовое ИЛИ, т.е. n = n | 1. Чтобы записать 1 во вторую ячейку, нужно использовать n = n | 2, т.о. для записи 1 в i-ю ячейку используем n = n | (1 << i)
n = 0;
n |= 1 << prompt('Введите число: ');
alert( n.toString(2) )


Чтобы записать туда 0, нужно использовать побитовое И, однако если мы используем тот же прием, что и в предыдущем случае, нас постигнет облом. Но мы сыграем на другом. Если одним из операндов побитового И есть 2^n - 1, т.е. число, в двоичной форме записываемое как набор единиц и только единиц, то результат будет неизменен. Но если хотя бы один "компонент" будет равен нулю, то соответствующее число в результирующем наборе тоже обнулится. Теперь встает вопрос: как получить такой набор? Вспоминаем, что у нас есть такой замечательный унарный оператор как ~! Т.е. нам нужно просто взять 0, записать в нужную позицию 1 и применить побитовое отрицание.
var n = parseInt('11011',2), i = prompt('Что будем обнулять?');
n = n & ~(0 | (1 << i));
alert(n.toString(2));


А теперь соберем все это вместе и напишем класс BoolSet.

<script type="text/javascript">
function BoolSet(defaultState){
  defaultState = defaultState || 0; // при желании тут можно обрабатывать строку или массив
  this.n = defaultState;
}

BoolSet.prototype = {
  get : function(i){
    return !!(this.n & (1 << i));
  },

  set : function(i, value){
    if(value = !!value) // 1
      this.n |= 1 << i;
    else // 0
      this.n &= ~(0 | (1 << i));
  }
};
</script>

<label>
<input type="checkbox" id="val" />
Записать в <input type="number" value="" id="idx-set" />-ый элемент true
</label> <input type="button" value="Go!" id="btn-set" /><br/>
Получить <input type="number" value="" id="idx-get" />-ый элемент <input type="button" value="Go!" id="btn-get" /><br/>

Значение сейчас: <span id="dec-val">0</span> (0b<span id="bin-val">0</span>)

<script type="text/javascript">
(function(){

var $ = function(id){return document.getElementById(id)},
      boolSet = new BoolSet(0);

$('btn-set').onclick = function(){
  var i = $('idx-set').value,
       val = $('val').checked;
   boolSet.set(i, val);
   $('dec-val').innerHTML = boolSet.n;
   $('bin-val').innerHTML = boolSet.n.toString(2);
}

$('btn-get').onclick = function(){
  var i = +$('idx-get').value;
  alert(boolSet.get(i));
};

}())
</script>


P.S. Это писалось по памяти по статье, прочитанной мною более года назад, поэтому где-то (особенно там, где мы записываем 0) я мог ошибиться.

with-love-from-siberia 05.09.2010 12:16

Как правильно было сказано, значениям типа Boolean достаточно одного бита для представления информации. Следовательно их можно "упаковать" в целочисленный тип.

Если взять, для примера, 32-битные целочисленные переменные, то их можно рассматривать как хранилища 32 булевых значений, где каждый бит - 0 и 1 - соответствует булевым значениям false и true. Такие булевые значения принято называть флагами.

Код:

В скобках указаны числа в двоичном формате
2^0 = 1 (0001)
2^1 = 2 (0010)
2^2 = 4 (0100)

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

Битовое НЕ (ноль - не единица, единица - не ноль)
~ 0 = 1
~ 1 = 0

Побитовое И (только все единицы)
0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1

Побитовое ИЛИ (хотя бы одна единица)
0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1

Побитовое исключающее ИЛИ (только одна единица)
0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0

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

F_DIRECTORY  = 0x0200;

F_OWNER_READ  = 0x0100;
F_OWNER_WRITE = 0x0080;
F_OWNER_EXEC  = 0x0040;
...

var fileAttr = ...

// 1. Если содержимое каталога можно прочитать
if ( fileAttr & (F_OWNER_READ | F_DIRECTORY) ) {
...

// 2. Установить права записи в каталог
fileAttr = fileAttr | F_OWNER_WRITE

// 3. Запретить права записи каталог
fileAttr = fileAttr & ~ F_OWNER_WRITE



Часовой пояс GMT +3, время: 21:36.