JavaScript - это язык программирования, который позволяет вам реализовывать сложные функции на веб-страницах, и, короче говоря, вы уже много знаете о JS, поскольку это самый популярный язык программирования в 2019 году (это не наше мнение, все цифры мы получили из Developer Survey 2019 от Stackoverflow). Если вы не слышали об этом опросе, вам стоит взглянуть на него, пока мы продолжим знакомство.

Поскольку JavaScript является основой любого веб-приложения, мы не будем обсуждать преимущества JS или список возможностей JS. Вместо этого мы покажем вам некоторые типичные ошибки, которые делал почти каждый программист JS за свою карьеру.

Согласно тому же опросу Stackoverflow, 41% программистов, принявших участие в опросе, имеют менее пяти лет профессионального опыта программирования.

Эта статья предназначена в основном для таких разработчиков. Новые разработчики (0–2 года) могут найти примеры из статьи полезными, потому что это плохой код, на котором можно учиться. Более опытные разработчики (старше 3 лет) могут улыбнуться, признав ошибки, которые вы совершали в прошлом. В любом случае, если вы потратите некоторое время на чтение этой статьи, это принесет вам либо знания, либо развлечение. Наслаждайся чтением!

Список ошибок:

  • Вы помните разницу между «=», «==» и «===»?
  • Забывая об объеме переменных.
  • Непонимание разницы между let, const и var.
  • Неправильные ссылки на методы экземпляра.
  • Трудности использования.

Вы помните разницу между «=», «==» и «===»?

Скорее всего, вы впервые столкнулись с проблемой с таким кодом:

var x = 1;
if (x = 7) { 
  alert("Hello"); 
} else {
  alert("Nope");
}

И вы получите «Привет»! Почему? Ответ очень прост: вы не понимаете разницы между тремя упомянутыми выше операторами. Это несложная ошибка, и если вы ее усвоите, то вряд ли ее забудете. Поскольку эта ошибка очень проста, вы можете не обращать на нее внимания, когда дело касается условий выхода из цикла.

Давай покончим с этим и пойдем дальше:

«=» - это оператор равенства, поэтому он используется для присваивания. В нашем примере мы присваиваем семерку «x» в условии и получаем слова приветствия «Hello».

Правильный код выглядит так:

var x = 1;
if (x == 7) {
  alert("Hello");
} else {
  alert("Nope");
}

Мы получаем «Нет».

«==» - это оператор сравнения с произвольным равенством. Почему проигрывает? Потому что он позволяет преобразовывать значения из одного типа в другой для их сравнения. Даже если мы присвоим x строковое значение «7» и сравним его с числовым значением «7», код вернет нам «Hello». Однако приведенный ниже код возвращает «Нет»:

Почему? Потому что === - это оператор сравнения строгого равенства. Если этот оператор возвращает истина, это означает, что наши значения идентичны как по значению, так и по типу. Для === есть аналог - метод Object.is. Он имеет некоторые различия в обработке значений -0, +0 и NaN, но некоторые из вас знают, в чем эти различия, а другие могут обратиться к Руководству по JavaScript. И в целом это хорошая практика:

Если у вас есть какие-либо сомнения относительно методов или функций JS, вы всегда можете погуглить, но мы настоятельно рекомендуем использовать Руководство по JavaScript.

Забывая об объеме переменных

Еще одна довольно простая ошибка:

let arr = [1,2,3,4,5,6,7];
var j;
for (j=0;  j < arr.length; j++) {
  console.log (arr[j]);
} 
// …some long code
console.log ( j ); // we get the number “7”

И легко забыть, что наша переменная меняет свое значение после цикла. Эта ошибка существует не только в JS-сообществе, но и в целом. В некоторых языках вы определяете переменную только внутри цикла, и она уничтожается по окончании цикла, но не в JavaScript.

И обратная ситуация, когда вы пытаетесь получить доступ к переменной, которая была определена в их локальной области (это относится к области действия). Пример:

function myFunction() {
  var me = "You can't touch me!";
} 
console.log(me);

«Я» не определено, извините, вы можете связаться со своим юристом или просто запомнить объем переменных в JavaScript. Правильный код:

var me;
function myFunction() {
  me = "You can't touch me!";
}
console.log(me + ‘I Can, sorry’);

Другой пример после обновления JS в 2015 году, когда в JS появилось ключевое слово let для объявления переменных (сценарий ECMA 6):

let arr = [1,2,3,4,5,6,7];
for (let j = 0; j < arr.length; j++) {
  console.log(arr[j]); // the output: 1, 2, 3, 4, 5, 6, 7
} 
console.log(j) // j = 0.

Ключевое слово let не изменило переменную j по сравнению с первым примером. И этот вопрос - тема нашего следующего реферата.

Непонимание разницы между let, const и var

Это тесно связано с предыдущей проблемой, но поскольку почти все гуглили «разницу между var, const и let »мы разделяем этот вопрос. Давайте сначала посмотрим на код ниже:

console.log(x); // undefined
var x = 5;
console.log(x); // the output is 5

Код на выходе логичен, вопросов нет. Другой пример:

console.log(x); // Error: cannot access “x” before the initialization
let x = 5;
console.log(x);

Причина в том, что var имеет область видимости функции, а let - область действия блока. Когда вы объявляете переменную с ключевым словом let, они перемещаются в начало блока. Это может привести к ошибке ссылки при попытке доступа к переменной перед инициализацией.

Это называется временная мертвая зона, если вы хотите узнать о ней больше, вы можете посетить официальный веб-сайт JS-разработчиков Mozilla JavaScript Guide.

Но перейдем к следующему участнику и покажем пример, чтобы все описать:

let a = 5;
var b = 10;
const c = 11;
 
if (a === 5) {
  let a = 4; 	// The scope is inside the if-block
  var b = 1; 	// The scope is global
  const c = 15; // The scope is inside the if-block
  
  console.log(a);   // 4, 
  console.log(b);   // 1
  console.log(c);   // 15
} 
console.log(a);  // 5, the value changes to the initial 
console.log(b);  // 1, the value from if-block saves
console.log(c);  // 11, the value changes to the initial

И последний код в этой главе:

a = 10; 	// it’s OK, the value of a is changed to 10
b = 20; 	// it’s OK, the value of b is changed to 20
c = 7; 		// SyntaxError: Identifier "c" has already beed declared 
const c = 15; 	// The same error

Что случилось? В блоке if мы объявили переменные a и c в блоке if и изменили значение глобальной переменной b. Вне блока «а» и «с» вернулись к исходным значениям. После этого мы попытались изменить значения всех переменных: let и var позволяют нам это сделать, а const вернул ошибку. Причина в том, что const объявляет доступную только для чтения ссылку на значение в определенной области (она может быть локальной или глобальной). Вот почему нам удалось объявить новое значение переменной «C» в блоке if, но не удалось изменить значение вне его.

Неправильные ссылки на методы экземпляра

Давайте создадим новый объект и воспользуемся свойством prototype функции, чтобы добавить метод whoAmI. Затем создайте экземпляр «obj» нашего объекта (код ниже):

var MyObject = function() {}
MyObject.prototype.whoAmI = function() { 
  console.log(this === window ? "window" : "MyObj"); 
}
var obj = new MyObject();

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

obj.whoAmI(); // MyObj
var anotherMethod = obj.whoAmI;
anotherMethod(); // window

И мы получаем выходное «окно» вместо ожидаемого «MyObj».

Почему? Итак, когда мы создаем ссылку varanotherMethod = obj. whoAmI, метод whoAmI был определен в глобальной области видимости. . Глобальная область видимости - это объект окна в браузере, поэтому ключевое слово this становится равным окну, а не экземпляру MyObject. Если мы хотим сделать правильную ссылку на метод экземпляра, то нам нужно вызвать этот метод из самого объекта или сделать ссылку на объект, а не только на метод объекта.

Правильная ссылка будет выглядеть так:

var obj = new MyObject(); 
var anotherObj = obj;
anotherObj.whoAmI() // MyObj

or

obj.link = obj.whoAmI
obj.link(); // MyObj

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

Трудности использования этого

JavaScript стал довольно сложным языком. Это ключевое слово в JavaScript, значение которого оценивается во время выполнения, в зависимости от контекста.

function myFunction() {
  var myObject = {
     objProperty: "some text",
     objMethod: function() {
        alert(objProperty);
        }
     }
  myObject.objMethod();
} 
myFunction();

И получаем ReferenceError: objProperty is not defined. Функции, определенные в объекте JavaScript, получают доступ к свойствам этого объекта JavaScript и не используют ссылочный идентификатор this. Правильный код выглядит так (не наш этот =)):

function myFunction() {
  var myObject = {
     objProperty: "some text",
     objMethod: function() {
        alert(this.objProperty);
        }
     }
  myObject.objMethod();
}
myFunction();

Идея проста: когда вызывается myObject. objMethod, this становится myObject во время вызова objMethod. Когда мы определяем объект и хотим получить доступ к его свойствам и методам, нам сначала нужно получить доступ к самому объекту. (звучит логично) Но бывают и обратные ситуации, когда this используется неправильно.

Game.prototype.restart = function () {
  this.clearLocalStorage();
  this.timer = setTimeout(function() {
    this.clearBoard(); 
  }, 0);
}

Он возвращает нам еще одну ошибку: undefined не является функцией.

Дело в том, что this в строке this. clearBoard () здесь не требуется, потому что при вызове setTimeout () вы работаете с window. setTimeout (), поэтому вы вызываете объект окна в браузере. В окне объекта нет метода clearBoard (). Правильная форма будет выглядеть так:

Game.prototype.restart = function () {
  var self = this;
  this.clearLocalStorage();
  this.timer = setTimeout(function() {
    self.clearBoard(); // this = window
  }, 0);
}

И пример, который существует с момента выпуска EcmaScript2015:

Game.prototype.restart = function () {
  this.clearLocalStorage();
  this.timer = setTimeout(() => {
    this.clearBoard(); // this = Game
  }, 0);
}

Это также стало возможным после ECMAScript 6. Когда мы используем стрелочную функцию, мы остаемся в области действия предыдущей функции, не создавая новую локальную область.

Утечки памяти, что скрывается за ее пределами

Начнем с кода:

function myFunction() {
  me = "You can't touch me!";
}

Это измененный пример из второй главы этой статьи, вы видите разницу?

Если да, это прекрасно - вы знаете об объявлении ненужных глобальных переменных и будьте осторожны, когда дело касается скорости вашего кода. Проблема с этим кодом в том, что когда мы вызываем функцию myFunction, мы создаем ненужную глобальную переменную, которая скрывается в фоновом режиме, пока код не завершится. Глобальная переменная создается, потому что мы присваиваем значение переменной, которая не была объявлена ​​ранее.

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

Используйте локальные переменные:

function myFunction() {
  var me = "You can't touch me!";
}

Используйте директиву use strict, которая не позволяет вызывать необъявленную переменную:

function myFunction() {
  “strict mode”
  me = "You can't touch me!"; //me is not defined
}

Утечки памяти происходят, когда приложение хранит ненужные данные, которые сборщик мусора не очищает при запуске. Еще одно событие, которое приводит к утечкам памяти, - это когда приложение потребляет память для определенной задачи: после выполнения задачи память освобождается, но иногда это не так. Таким образом, приложение сохраняет память без причины (поскольку задача выполнена).

Рассмотрим другой код:

var trigger = document.getElementById("trigger");
var elem = document.getElementById('elementToDelete');
trigger.addEventListener("click", function() {
  elem.remove();
});

Когда мы выполняем код, elementToDelete удаляется из DOM. Но у нас все еще есть ссылка на него в слушателе, и в этот момент происходит утечка памяти, потому что выделенная память для объекта все еще используется.

Решение здесь:

var trigger = document.getElementById("trigger");
trigger.addEventListener("click", function() {
  var elem = document.getElementById('elementToDelete');
  elem.remove();
});

Здесь внутри слушателя объявлен elem. Таким образом, когда мы удаляем его, путь для объекта обрезается, и память освобождается.

О Flatlogic

В Flatlogic мы разрабатываем веб-шаблоны и мобильные шаблоны. Мы вошли в топ-20 компаний по веб-разработке из Беларуси и Литвы. За последние годы мы успешно реализовали более 50 крупных проектов для малых стартапов и крупных предприятий.
Шаблоны Flatlogic
Примеры использования Flatlogic

Вот небольшой видеоролик о шаблонах Flatlogic, услугах веб-разработки и партнерской программе.

Первоначально опубликовано на flatlogic.com - шаблоны и темы React, Angular, Vue, Bootstrap и React Native.

Источник текста: Основные ошибки разработчиков JavaScript