1. Правильно використовуємо this при роботі з замиканнями
Призводить до помилки:
Чому це відбувається? Вся справа в контексті. Коли ви викликаєте setTimeout (), то насправді викликаєте window.setTimeout (). В результаті, анонімна функція, передана в setTimeout (), визначається в контексті об'єкта window, який не має методу clearBoard ().
Традиційне рішення, сумісне зі старими браузерами, передбачає просте збереження посилання на this в змінної, яка може бути збережена в замиканні:
Для нових браузерів можна використовувати метод bind (), що дозволяє зв'язати функцію з контекстом виконання:
3. Витоку пам'яті
Витоку пам'яті практично неминучі, якщо під час роботи ви не будете їх свідомо уникати. Існує чимало причин для появи витоків, але ми зупинимося лише на найбільш частих.
Посилання на неіснуючі об'єкти
Проаналізуємо цей код:
Якщо запустити код на виконання, то можна виявити масивну витік пам'яті зі швидкістю близько мегабайта в секунду. Створюється враження, що ми втрачаємо пам'ять виділену під longStr при кожному виклику replaceThing. В чому причина?
Типовий спосіб реалізації замикання - це створення зв'язку між кожним об'єктом-функцією і об'єктом-словником, що представляє собою лексичну область видимості для цієї функції. Якщо обидві функції (unused і someMethod), певні внутріreplaceThing, реально використовують priorThing, то важливо розуміти, що вони отримують один і той же об'єкт, навіть якщо priorThing переписується знову і знову, так як обидві функції використовують одну і ту ж лексичну область видимості. І як тільки змінна використовується в будь-якому з замикань, то вона потрапляє в лексичну область видимості, яка використовується всіма замиканнями в цій області видимості. І цей маленький нюанс призводить до потужної витоку пам'яті.
циклічні посилання
Розглянемо приклад коду:
- посилання на які містяться в стеку виклику (всі локальні змінні і параметри функцій, які зараз викликаються, а також всі змінні в області видимості замикання);
- всі глобальні змінні.
Об'єкти зберігається в пам'яті лише до тих пір, поки доступні з кореневих за посиланням або ланцюжку посилань.
У браузерах вбудований збирач сміття, який очищає пам'ять від недосяжних об'єктів. Тобто об'єкт буде видалений з пам'яті тільки якщо збирач сміття вирішить, що він недосяжний. На жаль, досить легко можуть накопичитися невикористовувані великі об'єкти, які вважаються «досяжними».
4. Нерозуміння рівності
Як показують два наведених прикладу, автоматичне перетворення типу іноді може заважати. Як правило краще використовувати === та! == замість == і! =, Щоб уникнути побічних ефектів перетворення типів.
До речі, порівняння NaN з чим-небудь (навіть з NaN!) Завжди дасть результат false. Таким чином, не можна використовувати оператори рівності (==, ===. =. ==) для визначення відповідності значення NaN. Замість цього потрібно використовувати вбудовану глобальну функцію isNaN ():
5. Правильно использова DOM
Якщо потрібно додати кілька елементів, то, в якості альтернативи, можна використовувати фрагменти документа:
6. Коректне використання визначень функцій всередині циклів for
Розглянемо приклад коду:
При кліці на будь-якому з 10 елементів з'являлося б повідомлення «This is element # 10». Причина в тому, що на той час, коли onclick викликається будь-яким з елементів, вищестоящий цикл for буде завершений, а значення i дорівнюватиме 10.
Приклад правильного коду:
makeHandler негайно запускається на кожній ітерації циклу, отримує поточне значення i + 1 і зберігає його у змінній num. Зовнішня функція повертає внутрішню функцію (яка також використовує змінну num) і встановлює її в якості обробника onclick. Це дозволяє гарантувати, що кожен onclick отримує і використовує правильне значення i.
7. Правильне спадкування через прототипи
Дивно багато розробників не мають чіткого розуміння механізму наслідування через прототипи. Розглянемо приклад коду:
Але якби ми написали так:
Але чи не краще повернути значення до default? Це можна легко зробити, якщо застосувати спадкування через прототипи:
Кожен екземпляр BaseObject успадковує властивість name свого прототипу, в якому йому присвоєно значення default. Таким чином, якщо конструктор викликаний без name, свойствоname за замовчуванням буде default. І точно так само, якщо властивість name буде видалено з примірника BaseObject, буде проведений пошук по ланцюжку прототипів і властивість nameбудет отримано з об'єкта prototype, в якому воно як і раніше дорівнює default:
8. Створюйте правильні посилання на методи примірників
Визначимо простий конструктор і за допомогою нього створимо об'єкт:
Для зручності, створимо посилання на метод whoAmI:
Виведемо значення нашої нової змінної whoAmI:
В консолі буде виведено:
А тепер зверніть увагу на різницю при викликах obj.whoAmI () і whoAmI ():
Що пішло не так? Коли ми присвоїли var whoAmI = obj.whoAmI ;, нова змінна була визначена в глобальному просторі імен. В результаті значення this виявилося равнимwindow, а не obj, екземпляру MyObject. Таким чином, якщо нам дійсно потрібно створити посилання на існуючий метод об'єкта, необхідно зробити це в межах простору імен цього об'єкта. наприклад:
9. Використання рядка в якості першого аргументу в setTimeout або setInterval
Само по собі це не є помилкою. І справа тут не тільки в продуктивності. Справа в тому, що коли ви передаєте строкову змінну першим аргументом в setTimeout або setInterval, вона буде передана конструктору Function для перетворення в нову функцію. Цей процес може бути повільним і неефективним. Альтернативою є використання функції як перший аргумент:
10. Відмова від використання «strict mode»
Це режим, в якому накладається ряд обмежень на виконуваний код, що підвищує безпеку і може запобігти появі деяких помилок. Звичайно, відмова від використання «суворого режиму» не є помилкою як такої. Просто в цьому випадку ви позбавляєте себе ряду переваг: