Акт 1: «Ядро» — Часть 1 (Введение)

Введение

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

В этой серии мы попытаемся собрать и кратко представить все эти основные концепции, которые вам необходимо освоить, чтобы иметь возможность использовать JavaScript со 100% уверенностью во всех сценариях. Кроме того, его можно использовать в качестве руководства, когда вы либо создаете интервью по программированию, либо решаете участвовать в таком. Имейте в виду, что более важно понимать общую структуру, чем знать каждый бит синтаксиса. Есть хорошая поговорка: «Узнай, где это написано». Я надеюсь, что этот небольшой отчет способствует именно этому.

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

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

Материал будет разделен на три части (или действия):

  • Ядро
  • Клиент
  • Бэк-Энд

Поскольку лучше всего изучать программирование, запачкав руки, я предлагаю вам самостоятельно следовать некоторым фрагментам кода и проводить свои эксперименты. Запуск кода, который нацелен на основные функции JavaScript или внутреннюю логику, вы можете сделать это, установив Node.js на свой компьютер, поместив код в файл, например example.js, а затем из терминала, который вы запускаете.

node example.js

Для запуска кода, предназначенного для клиента, вы можете поместить весь код в скриптовую часть html-файла.

<html>
 <head>
  <script>
   // code goes here
  </script>
 </head>
 <body>
 </body>
</html>

а затем откройте файл в браузере. Обратите внимание, что Chrome или Firefox предоставляют консоли разработчика, которые позволяют вам проверять код и писать сообщения в консоль.

Я постараюсь организовать частоту новых историй, чтобы обеспечить плавный и, насколько это возможно, оптимальный опыт обучения. Если у вас есть вопросы, комментарии или вы обнаружите ошибки, не стесняйтесь комментировать историю. Желаю вам приятного чтения!

Типы:

JavaScript имеет следующие типы: undefined, BigInt, Symbol, Object, Function, Number, String, Boolean

Все эти типы можно проверить с помощью оператора typeof. Например,

typeof '';

возвращает 'string'.

Массивы и другие структурные типы, такие как Date, являются производными от Object, поэтому их невозможно различить с помощью typeof. Например, typeof [] приводит к 'object'.

Переменные:

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

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

Использование переменной без ее предварительного объявления приводит к тому, что переменная добавляется как свойство к глобальному объекту ( window ). Таким образом, просто запустив a = 10;, вы добавите a в качестве свойства к window в браузере.

Объявленные, но не инициализированные переменные имеют значение undefined.

Переменные, объявленные var, поднимаются, а переменные, объявленные let или const нет. То есть вполне нормально (но не хорошо) написать:

function fn(){
  a = 5;
  var a;
}

Но запись того же с использованием const или let приведет к ошибке.

Переменные, объявленные const, должны быть инициализированы при объявлении. Более того, их нельзя будет переназначить в дальнейшем на другое значение. Итак, вы не можете сделать это:

const a = 5;
a = 6;

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

Переменные, объявленные let, имеют блочную область действия. Это означает, что всякий раз, когда объявляется переменная с помощью let, ее область действия ограничивается ближайшим окружающим блоком: { ... }. Например:

{
 let a = 5;
}
console.log(a); // error!

Но:

{
  var a = 5;
}
console.log(a); // 5

Ярким примером, о котором следует помнить, является следующий:

for (var n = 0; n < 3; n++) {
  var i = n;
  setTimeout(() => console.log(i));
}

При этом регистрируются значения 2, 2, 2, поскольку область действия переменной i ограничена либо глобальным объектом, либо областью действия окружающей функции, которая выполняет оператор for.

Чтобы исправить это, можно использовать либо

for (var n = 0; n < 3; n++) {
  let i = n;
  setTimeout(() => console.log(i));
}

или напрямую for-let:

for (let n = 0; n < 3; n++) {
  setTimeout(() => console.log(n));
}

Оба будут печатать: 0, 1, 2.

Объем функций:

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

function f(){
  var a = 1;  // belongs to f, can be seen by g
  function g(){
    var a;  // belongs to g, overrides declaration
    a = 2;
    console.log(`in g: ${a}`);
  }
  g();
  console.log(`in f: ${a}`);
}
f();
// logs:
// in g: 2
// in f: 1

Все это не зависит от способа объявления переменных ( const, let, var ). Кроме того, переменные, объявленные внутри функции, недоступны извне.

Область действия функции распространяется на все переменные, на которые она ссылается. Такое поведение часто называют close-scope:

function counted(fn) {
  let called = 0;
  return function (...x) {
    called++;
    console.log(`times called: ${called}`);
    fn(...x);
  }
}
const f = counted(function(){});
f(); // 1
f(); // 2

Здесь counted принимает функцию в качестве аргумента и возвращает новую функцию, которая является оболочкой данной функции fn. При каждом вызове fn счетчик called добавляется 1. Интересно то, что когда counted возвращает значение, переменная called, принадлежащая области действия counted, но на которую ссылается возвращаемая функция, по-прежнему будет доступна для возвращаемой функции. Другими словами, область видимости возвращаемой функции расширяется переменной called. Если called не будет использоваться в этой возвращаемой функции, то после возврата counted сборщик мусора освободит память, связанную с called.

Параметры функции и возвращаемые значения:

Параметры функции передаются по значению, то есть присвоение другого значения переменной, используемой в качестве аргументов, не меняет значение, на которое ссылался этот аргумент при вызове функции:

var a = 1;
function f(x){
  x = 2;
}
f(a);
console.log(a);  // 1

Типы объектов дополнительно передаются по ссылке:

var obj = {
  a: 1
};
function f(x) {
  x.a = 2;
}
f(obj);
console.log(obj.a); // 2

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

Точно такое же поведение справедливо и для возвращаемого значения, которое передается по значению и, кроме того, по ссылке для типов объектов.

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

На этом пока все, и не забывайте оставлять комментарии по вопросам или ошибкам, которые вы обнаружите. Давайте посмотрим еще раз в следующей истории, если хотите.

Спасибо за чтение!