Главная » Статьи » Основные ошибки, допускаемые разработчиками JavaScript

Основные ошибки, допускаемые разработчиками JavaScript

Основные ошибки, допускаемые разработчиками JavaScript

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

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

Основные ошибки, допускаемые разработчиками JavaScript

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

Основные ошибки, допускаемые разработчиками JavaScript

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

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

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

Забыли об области действия переменных?

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

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

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

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

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

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

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

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

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

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

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

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

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

Если у вас есть какие-либо сомнения относительно методов или функций JS, вы всегда можете найти их в Google, но мы настоятельно рекомендуем использовать Руководство по 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);

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

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

Еще один пример, связанный с ключевым словом let, введенным в JS в 2015 году для объявления переменных (ECMA Script 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» и изменили значение глобальной переменной «b». Вне блока «a» и «c» вернулись к своим начальным значениям. После этого мы попытались изменить значения всех переменных: 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

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

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

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

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

или же

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

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

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

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

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

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

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

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

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

Возвращает нам еще одну ошибку: «undefined is not a function». Дело в том, что 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!";
}

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

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 объявлена внутри прослушивателя. Таким образом, когда мы удаляем ее, путь к объекту обрезается и память освобождается.

Автор: Anastasia Ovchinnikova

Источник: https://medium.com

Редакция: Команда webformyself.