Давай начнем этот модуль с небольшого фрагмента кода.
let reaction = 'почка';
reaction[0] = 'т';
console.log(reaction);
Что ты ожидаешь от этого кода? Мы этого еще не разбирали, так что ничего страшного, если ты не уверен. Попробуй ответить на вопрос, используя свои текущие знания JavaScript.
Теперь я хочу, чтобы ты потратил несколько минут и пошагово записал свой точный процесс мышления для каждой строки этого кода. Обрати внимание на любые пробелы или неопределенности в твоей ментальной модели и запиши их тоже. Если у тебя возникли какие-либо сомнения по этому поводу, постарайся сформулировать их как можно более четко.
НИЖЕ СПОЙЛЕР
Не скроль ниже, пока не закончишь с размышлениями.
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
Вот и ответ. Этот код выдаст либо "почка", либо выкинет ошибку в зависимости от того, используешь ли ты строгий режим. Такой код никогда не выведет "точка".
Примитивные значения неизменны
Получил ли ты верный ответ? Это может показаться пустяковым вопросом, который задают люди в интервью по JavaScript, но на практике это мало что дает. Тем не менее, это иллюстрирует важный момент о примитивных значениях.
Я не могу менять примитивные значения.
Я объясню это на небольшом примере. Строки (которые являются примитивными) и массивы (которые не примитивные - они объекты) имеют поверхностное сходство. Массив - это последовательность элементов, а строка - это последовательность символов:
let arr = [228, 1488, 6610];
let str = 'приветики';
Ты можешь получить доступ к первому элементу массива так же, как и к первому символу строки. Кажется, что строки - это массивы (но это не так!):
console.log(arr[0]); // 228
console.log(str[0]); // "п"
Ты можешь изменить первый элемент массива:
arr[0] = 911;
console.log(arr); // [911, 1488, 6610]
Таким образом, легко предположить, что ты можешь сделать то же самое со строкой:
str[0] = 'к'; // ???
Но ты не можешь.
Вот важный момент, который нужно добавить в ментальную модель. Строка является примитивным значением. И это очень много значит!
Все примитивные значения неизменны. "Неизменный" (англ. immutable) - это причудливый латинский способ сказать "неизменный" (англ. unchangeable). Эти значения только для чтения (англ. read-only).
Если ты попытаетешься установить свойство для примитивного значения, будь то число, строка или что-то еще, JavaScript не позволит сделать этого. Будет ли молча отклонять такой запрос или выдаст ошибку, зависит от того, в каком режиме работает твой код.
Но будь уверен, что это никогда не сработает:
let breakLaw = 228;
breakLaw.idk = 'money'; // НЕТ!
Как и любое число, 228 является примитивным значением, и ты не можешь устанавливать для него свойства.
В моей JavaScript-вселенной все примитивные значения существуют во внешнем круге дальше от моего кода - как далекие звезды. Это напоминает мне, что хоть я и могу ссылаться на них из своего кода, я не могу их менять. Они остаются такими, какие они есть.
Странно, что это знание успокаивает меня.
Противоречие?
Я только что продемонстрировал, что примитивные значения доступны только для чтения или неизменны. Вот фрагмент кода для проверки ментальной модели.
let pet = 'псина';
pet = 'кот';
console.log(pet);
Как и раньше, запиши свой мыслительный процесс в несколько предложений. Не торопись. Обрати пристальное внимание на каждую строку кода. Важна ли здесь неизменность строк?
НИЖЕ СПОЙЛЕР
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
------
Если тебе показалось, что я пытаюсь тебя запутать, то ты абсолютно прав! Ответ - 'кот' - неизменность строк роли не играет.
Не отчаивайся, если ошибся! Два последних примера определенно противоречат друг другу.
Это важно для осознания.
Когда ты плохо знаком с языком, может возникнуть соблазн игнорировать противоречия. В конце концов, если ты будешь преследовать каждое противоречие, то упадешь в кроличью нору слишком глубоко, чтобы чему-то научиться. Но теперь, когда ты выстраиваешь свою ментальную модель, необходимо ставить под сомнение противоречия. Противоречия раскрывают пробелы в ментальных моделях.
Переменные - это провода
Давай посмотрим на этот пример еще раз.
let pet = 'псина';
pet = 'кот';
console.log(pet); // кот
Мы знаем, что строковые значения не могут измениться, потому что они примитивны. Но переменная pet меняется на 'кот'. Почему?
Может показаться, что это противоречие, но это не так. Мы только что удостоверились, что лишь примитивные значения не могут изменяться. При этом ничего не говорили о переменных!
По мере того, как мы совершенствуем нашу ментальную модель, нам может потребоваться распутать связанные понятия.
Переменные не являются значениями.
Переменные указывают на значения.
В моей вселенной переменная - это провод. У него два конца и направление: оно начинается с имени в моем коде и заканчивается указанием на какое-то значение во вселенной.
Например, я могу указать переменную pet на значение 'нарвал':
let pet = 'нарвал';
После этого я могу сделать две вещи с переменной.
Присвоение значение переменной
Единственное, что я могу сделать, - это присвоить какое-то другое значение для этой переменной:
pet = 'the кракен';
Все, что я делаю здесь, это инструктирую JavaScript, чтобы провод с левой стороны (переменная pet) указывал на значение с правой стороны ('the кракен'). Он будет продолжать указывать на это значение, если я не назначу ему что-то другое.
Обрати внимание, что я ничего не могу сделать с левой стороной:
'война' = 'мир'; // Никак. (попробуй сам в коонсоли)
Левая сторона назначения должна быть "проводом". Пока мы знаем только то, что переменные являются "проводами". Но есть еще один вид "провода", о котором мы поговорим в следующем модуле. Возможно, ты уже догадался, что это? (Подсказка: это квадратные скобки или точка, и мы уже видели их пару раз.)
Есть и другое правило.
Правая часть назначения должна быть выражением. Это может быть что-то простое, например 2 или 'привет', или более сложное выражение - например:
pet = count + ' Долматинцы';
Здесь count + ' Долматинцы' - это выражение - вопрос к JavaScript. JavaScript ответит на это значением (например, '101 далматинец'). С этого момента pet "провод" начнет указывать на это значение.
Если правая часть должна быть выражением, означает ли это то, что числа типа 2 или строки типа 'the кракен', написанные в коде, также являются выражениями? Да! Такие выражения называются литералами - потому что мы буквально записываем их значения.
Чтение значения переменной
Я также могу прочитать значение переменной - например, чтобы вывести его в консоли:
I can also read the value of variable — for example, to log it:
console.log(pet);
Вряд ли это удивительно.
Но обратите внимание, что это не переменная pet, которую мы передаем в console.log. Мы можем так говорить в разговорной речи, но в действительности мы не передаем переменные в функции. Мы отправляем текущее значение этой переменной. Как это работает?
Оказывается, что имя переменной вроде pet тоже может быть выражением! Когда мы пишем pet, мы задаем JavaScript вопрос: «Каково текущее значение pet?» Чтобы ответить на наш вопрос, JavaScript следует по pet "проводу" и возвращает нам значение в конце этого "провода".
Таким образом, одно и то же выражение может дать нам разные значения в разное время!
Существительные и глаголы
Кому какое дело, если вы скажете "передать переменную" или "передать значение"? Разве это не безнадежно педантичная разница? Я, конечно, не рекомендую мешать вашим коллегам исправлять их - или даже тебе самому. Это было бы пустой тратой времени.
Но в твоих мыслях должна быть ясность относительно того, что ты можешь сделать со своей идеей. Ты не можешь кататься на велосипеде как скейте. Ты не можешь летать на авокадо. Ты не можешь петь как комар. И ты не можешь "передать переменную" - по крайней мере, не в JavaScript.
Вот небольшой пример того, почему это так важно.
function double(x) {
x = x * 2;
}
let money = 10;
double(money);
console.log(money); // ?
Если бы мы думали, что double(money) передают переменную, мы могли бы ожидать, что x = x * 2 удвоит эту переменную. Но это не так. Мы знаем, что double(money) означает получить значение money, а затем передать это значение в double. Итак, ответ 10. Каков обман!
Какие существуют существительные и глаголы JavaScript в твоей голове? Как они связаны друг с другом? Запишите краткий список наиболее часто используемых.
Подытожим
Теперь давайте вернемся к первому примеру из первой статьи "ментальные модели":
let x = 10;
let y = x;
x = 0;
Я предлагаю вам взять лист бумаги или приложение для рисования и сделать шаг за шагом набросок схемы того, что происходит с "проводами" переменных x и y.
Первая строка мало что делает:
Вторая строка короткая, но она делает некоторые вещи:
Наконец, мы переходим к третьей строке:
В конце переменная x указывает на значение 0, а переменная y указывает на значение 10. Обрати внимание, что y = x не означает связать y с x. Мы не можем связывать переменные друг с другом! Переменные всегда связаны со значением. Когда мы видим присваивание, мы "спрашиваем" значение правой стороны и направляем на него "провод" с левой стороны.
Я упоминал в ментальных моделях, что переменные довольно часто воспринимаются как коробки. Вселенная, которую мы строим, вообще не будет иметь никаких коробок. Только провода! Такой подход может показаться раздражающим. Почему мы не можем просто поместить 0 и 10 значения в переменные вместо того, чтобы указывать переменные на них?
Использование проводов будет очень важно для объяснения множества других понятий, таких как строгое равенство, идентичность объекта и мутация. Мы и дальше будем использовать провода, так что привыкай!
Моя вселенная заполнена проводами.
Итог
Упражнения
В этом модуле также есть упражнения для практики!
Жми сюда, чтобы закрепить эту ментальную модель несколькими короткими упражнениями.
Даже если ты, вероятно, знаком с концепцией переменных, эти упражнения помогут закрепить ментальную модель, которую мы создаем. Нам нужен этот фундамент, прежде чем мы сможем перейти к более сложным темам.
----
Адаптация и перевод статьи Just JavaScript от Den Abramov. Подписаться на почтовую рассылку на английском языке можно на сайте Just JavaScript.