Войти на сайт

Авторизация, ждите ...
×

ТЕМА: Константы и присвоение. Переменные - не объекты.

Константы и присвоение. Переменные - не объекты. 6 года 2 мес. назад #102596

  • Amphilohiy
  • Amphilohiy аватар
  • Вне сайта
  • Светлый дракон
  • Сообщений: 547
  • Спасибо получено: 666
  • ОраторПобедитель Сбитой кодировки2 место ГотвПрограммист RubyУчитель
Недавно я мучал доктора Бага небольшим скриптецом. Суть была показать как можно в отдельном модуле организовать обменник данными. Просто метод положить и метод достать, все просто. Как обычно я не задумываясь организовал это дело через коллекцию в константе, и в итоге пришлось разъяснять почему так можно, ибо кажется что менять константу нельзя. Посему я решил просто поизголятся и углубиться в эту теорию несколько глубже, хоть тема и кажется тривиальной.
Первое что может сбить с толку это ощущение что переменная и есть объект (перепрочтите заголовок). Но это не совсем так, переменные являются представлением объекта, что то вроде портала через который можно достать объект и помучать его. В этом плане 2 переменных, содержащих один объект, это два портала с одной точкой выхода.
А оператор присвоения (знак '=') это настройка телепорта на объект. Ага, если слева находится именно переменная/константа (а не конструкция посложнее) то объект и не меняется никаким образом. Разница между присвоением и конструкцией посложнее:
variable = []       # присвоение
variable.field = []   # метод field= объекта
variable['field'] = []  # метод []= объекта
Пример того как присваивание не меняет объект, но иная конструкция меняет:
array = [ { value: 'value' } ]
array2 = array
 
element = array[0]
element = { value: 'not value (obviously!)' }
 
p array # => [{:value=>"value"}] Мы назначили на переменную element новое значение, сам массив не постродал
 
array[0] = { value: 'not value (obviously!)' }
 
p array # => [{:value=>"not value (obviously!)"}] Мы использовали оператор []= который поменял исходный массив
 
array2 = []
 
p array # => [{:value=>"not value (obviously!)"}] Мы переприсвоили переменную, а не пересоздали объект

Позвольте мозгу поработать над этой идеей, ибо дальше будет продолжение. Так вот - константы. Единственное отличие константы от переменной это невозможность повторного использования оператора присвоения. Мда, это все. Тот же пример с константой:
ARRAY = [ { value: 'value' } ]
array2 = ARRAY
 
element = ARRAY[0]
element = { value: 'not value (obviously!)' }
 
p ARRAY # => [{:value=>"value"}]
 
ARRAY[0] = { value: 'not value (obviously!)' }
 
p ARRAY # => [{:value=>"not value (obviously!)"}]
 
array2 = []
 
p ARRAY # => [{:value=>"not value (obviously!)"}]

То есть разница между первым и вторым только в присвоении, как в следующем примере:
variable = []
variable = []
CONSTANT = []
CONSTANT = [] # Ошибка

И хоть я и пишу это про руби, те же 2 примера работают так же и в яваскрипте. Переменная:
var array = [ { value: 'value' } ];
var array2 = array;
 
var element = array[0];
element = { value: 'not value (obviously!)' };
 
console.log(array); // => [ { value: 'value' } ] Мы назначили на переменную element новое значение, сам массив не постродал
 
array[0] = { value: 'not value (obviously!)' };
 
console.log(array); // => [ { value: 'not value (obviously!)' } ] Мы использовали оператор []= который поменял исходный массив
 
array2 = [];
 
console.log(array); // => [ { value: 'not value (obviously!)' } ] Мы переприсвоили переменную, а не пересоздали объект

Константа:
const ARRAY = [ { value: 'value' } ];
var array2 = ARRAY;
 
var element = ARRAY[0];
element = { value: 'not value (obviously!)' };
 
console.log(ARRAY); // => [ { value: 'value' } ]
 
ARRAY[0] = { value: 'not value (obviously!)' };
 
console.log(ARRAY); // => [ { value: 'not value (obviously!)' } ]
 
array2 = [];
 
console.log(ARRAY); // => [ { value: 'not value (obviously!)' } ]

Таааак выходит что константа практически не влияет на возможность изменения объекта. В данном случае надо делить типы объектов на изменияемые и неизменяемые. Так например число неизменяемое, а массив изменяемое. Почему? Да потому что для первого не существующет метода, который бы мог изменить сам объект, вместо этого всегда возвращается новый объект в результате вызова. Например в Руби строки изменяемые, а в js нет:
CONSTANT = 'cat'
CONSTANT[0] = 'b'
puts CONSTANT # => bat
const CONSTANT = 'cat';
CONSTANT[0] = 'b'; // Может repl.it голимый, может так и положено, но ошибки небыло
console.log(CONSTANT); // 'cat'

И единственный выход в js это создать новую строку на основе той, что хранится в константе. Это, пожалуй, все. Все примеры, как вы уже поняли, я проверял на этом сайте.
Я верю, что иногда компьютер сбоит, и он выдает неожиданные результаты, но остальные 100% случаев это чья-то криворукость.
Администратор запретил публиковать записи гостям.
За этот пост поблагодарили: Dmy, Doctor_Bug

Константы и присвоение. Переменные - не объекты. 6 года 2 мес. назад #102598

  • Doctor_Bug
  • Doctor_Bug аватар
  • Вне сайта
  • Светлый дракон
  • Из горизонта события! ▪_■
  • Сообщений: 568
  • Спасибо получено: 880
  • 3 место в Кодировке3 местоВетеранПроект месяца 3 местоПроект месяца 1 местоПрограммист Ruby
Не пойму, как это у тебя все работает? Начинаю детальную диагностику.
ВНИМАНИЕ: Спойлер! [ Нажмите, чтобы развернуть ]
Баг изучает Godot Engine. А слушает эту музыку ~~> Мое сердце
Администратор запретил публиковать записи гостям.

Константы и присвоение. Переменные - не объекты. 6 года 2 мес. назад #102599

  • EvilCat
  • EvilCat аватар
  • Вне сайта
  • Просветлённый
  • Сообщений: 469
  • Спасибо получено: 850
  • Учитель3 место Готв2 место
Всё это происходит в языках, где массивы являются объектами. Когда я пришла в Яваскрипт из php, то однажды долго искала непонятную ошибку, а оказалось, что при передаче массива как аргумента в функцию передаётся тот же самый массив, а не его копия, и все изменения в него внутри функции отражались и вне функции.

Как это в php (не тот же самый код, что в примере Амфи!)
$array = [ ['value' => 'value'] ];
$array2 = $array;
 
$array[0] = [ 'value' => 'not value (obviously!)' ];
 
var_dump($array2); # => array ( 'value' => 'value' ) - потому что массивы не объекты, и присвоив $array2 массив из $array, мы создали его независимую копию.

Есть два способа обойти это, если хочется массивов-объектов.

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

Во-вторых, или можно использовать специальное присвоение, с помощью которого две переменных обращаются к одному и тому же адресу.
$array = [ ['value' => 'value'] ];
$array2 =& $array;
 
$array[0] = [ 'value' => 'not value (obviously!)' ];
 
var_dump($array2); # => array ( 'value' => 'not value (obviously!)' ) - теперь содержимое переменных меняется одновременно.

$array2[0] = [ 'value' => 'another third value' ];
 
var_dump($array); # => array ( 'value' => 'another third value' ) - теперь содержимое переменных меняется одновременно.
 

Главное - не перепутать оператор =& (присвоить по тому же адресу) c &= (побитовое перемножение), а также не запутаться, если несколько переменных связаны этим отношением:
$a = 1;
$b = 2;
$b =& $a;
var_dump($b); # => int(1)

$b = 3;
var_dump($a); # => int(3)

$c = 100;
$a =& $c;
var_dump($b); # => int(3)
var_dump($a); # => int(100)

Протестировать php в онлайне можно здесь, только не забудьте <? в начале, ведь всё-таки php исконно - шаблонизатор.




Ещё немного php'шной магии. Связывать адресами можно не только переменные, но и элементы массивов. Например:
ВНИМАНИЕ: Спойлер! [ Нажмите, чтобы развернуть ]
Последнее редактирование: 6 года 2 мес. назад от EvilCat.
Администратор запретил публиковать записи гостям.
За этот пост поблагодарили: Dmy, Amphilohiy, Doctor_Bug

Константы и присвоение. Переменные - не объекты. 6 года 2 мес. назад #102601

  • Lekste
  • Lekste аватар
  • Вне сайта
  • Светлый дракон
  • Сообщений: 911
  • Спасибо получено: 565
  • Программист JavaScript Даритель СтимкеяОраторВетеранПрограммист Ruby
Но и это особо не спасет, если в вашем массиве другой массив, который также скопируется.
Или когда в массиве объекты, которые не будут копироваться вместе с самим массивом и, можно случайно поменять эти объекты внутри своей функции и напакостить тому, кто будет использовать их же вне вашей функции. :)

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

Константы и присвоение. Переменные - не объекты. 6 года 2 мес. назад #102604

  • EvilCat
  • EvilCat аватар
  • Вне сайта
  • Просветлённый
  • Сообщений: 469
  • Спасибо получено: 850
  • Учитель3 место Готв2 место
От всего этого спасёт правильное наименование функций. В Руби это очень удобно, что знак вопроса и восклицательный знак можно использовать в названии функции, поэтому деструктивные функции (не просто возвращающие что-то, а имеющие побочные эффекты) можно помечать им. А в C# (Unity), если не ошибаюсь, для передачи по ссылке нужно и в функции, и в вызове поставить ключевое слово out перед аргументом.
Администратор запретил публиковать записи гостям.
За этот пост поблагодарили: Dmy, Doctor_Bug

Константы и присвоение. Переменные - не объекты. 6 года 2 мес. назад #102605

  • Lekste
  • Lekste аватар
  • Вне сайта
  • Светлый дракон
  • Сообщений: 911
  • Спасибо получено: 565
  • Программист JavaScript Даритель СтимкеяОраторВетеранПрограммист Ruby
Но, возврат по ссылке же логику направленную в одну сторону - от входов к выходу делает ненаправленной. :(
Да и функции, которые делают 2 и больше действий же нарушают принцип выполнения разделения ответственности.
Хотя как-обычно в особых случаях, вроде упомянутой деструктивной или какого-нибудь save или send. Выглядит нормально.
Однако даже без знака вопроса, можно просто к имени такой функции приписать «try”, например. Вроде trySendUserStatistics(stats, &error). И сразу понятно, что может пройти не успешно.
Хотя часто удобней в таких случаях кидать исключения. С ними и описание функции не забьётся ничем лишним, и ловить можно в удобных местах.

А побочные эффекты пусть создаются в выделенных для них местах, а не где попало, чтоб не лазить и не собирать их потом по коду.
Последнее редактирование: 6 года 2 мес. назад от Lekste.
Администратор запретил публиковать записи гостям.

Константы и присвоение. Переменные - не объекты. 6 года 2 мес. назад #102606

  • EvilCat
  • EvilCat аватар
  • Вне сайта
  • Просветлённый
  • Сообщений: 469
  • Спасибо получено: 850
  • Учитель3 место Готв2 место
Ловить исключения удобно, пока не начинаешь писать асинхронные методы.
Администратор запретил публиковать записи гостям.

Константы и присвоение. Переменные - не объекты. 6 года 2 мес. назад #102608

  • Lekste
  • Lekste аватар
  • Вне сайта
  • Светлый дракон
  • Сообщений: 911
  • Спасибо получено: 565
  • Программист JavaScript Даритель СтимкеяОраторВетеранПрограммист Ruby
Но... Вызывающая функция не должна зависеть от результата асинхронного вызова, т.к. Она его не ждёт и отпочковавшийся тред уже сам по-себе и результаты он возвращает через коллбек, что по-идее такое же линейное продолжение выполнения.
Вообщем, если вызвавший поток ждёт результата, то он и вызывает синхронно, если не ждёт, то ему и результат вызова не нужен.
Не понял в чем проблема

Или речь о псевдоассинхронности, когда функция разбита на фрагменты и периодически приостанавливается?
Так я думал там можно спокойно исключения кидать, раз вызывающая функция ждёт результата.
Последнее редактирование: 6 года 2 мес. назад от Lekste.
Администратор запретил публиковать записи гостям.

Константы и присвоение. Переменные - не объекты. 6 года 2 мес. назад #102609

  • EvilCat
  • EvilCat аватар
  • Вне сайта
  • Просветлённый
  • Сообщений: 469
  • Спасибо получено: 850
  • Учитель3 место Готв2 место
Она его не ждёт и отпочковавшийся тред уже сам по-себе и результаты он возвращает через коллбек, что по-идее такое же линейное продолжение выполнения.

Линейное-то оно может быть и линейное, но исключения с помощью try...catch уже не половишь.
Администратор запретил публиковать записи гостям.

Константы и присвоение. Переменные - не объекты. 6 года 2 мес. назад #102611

  • Lekste
  • Lekste аватар
  • Вне сайта
  • Светлый дракон
  • Сообщений: 911
  • Спасибо получено: 565
  • Программист JavaScript Даритель СтимкеяОраторВетеранПрограммист Ruby
Как не половишь? Коллбек Это же обычная функция. Только ловить надо там, где этот коллбек вызывается
Администратор запретил публиковать записи гостям.

Константы и присвоение. Переменные - не объекты. 6 года 2 мес. назад #102612

  • EvilCat
  • EvilCat аватар
  • Вне сайта
  • Просветлённый
  • Сообщений: 469
  • Спасибо получено: 850
  • Учитель3 место Готв2 место
Ты знаешь, что если в коллбеке у Promise (в Javascript) возникает исключение, то оно никуда не пробрасывается, а вместо этого Promise разрешается с провалом, и вызывается уже соответствующий провалу коллбек? А если такого коллбека нет, то исключение просто проглатывается, и Promise тихо разрешается провалом.

А если ставить к каждому Promise свой коллбек на провал, то ты попадаешь в ад коллбеков... Это серьёзная проблема, для которой даже придумали специальный сахар, чтобы как-то облегчить. Его даже теоретически уже можно использовать, хотя не факт, что в MV...

P.S. В своём проекте на php я мечтаю перейти на функции-генераторы, которые, когда им нужно что-то асинхронно получить делают
$result = yield new Need(задача, задача, задача);

В зависимости от того, как создан объект Need, он сам разбирается, считать ли провал одной из задач общим провалом, возвращать ли какие-то значения по умолчанию в случае провала, а то и вообще не возвращаться к этому исполнению... Таким образом, на простейшие случаи не нужно тратить лишние try...catch и делать чтение кода нелинейным.
Последнее редактирование: 6 года 2 мес. назад от EvilCat.
Администратор запретил публиковать записи гостям.
Время создания страницы: 0.397 секунд