Автор оригинала: JuanMa Garrido.
Я работаю с JavaScript за последние 15 лет, и делать тренинги, чтобы научить его за последние восемь лет. За последние два года я был сосредоточен на преподавании JavaScript для нескольких полных загрузочных загрузочных компьютеров.
Основной целью этих BootCamps состоит в том, чтобы подготовить людей, чтобы войти на рынок работы на веб-разработке. Моя миссия как учитель была, среди прочего, указывать, сосредоточить и объяснять те тем, и ресурсы, которые я считаю (из моего опыта) поможет своим ученикам понять код от других и эффективно решать задачи/проблемы в своих будущих работах в качестве веб Разработчики.
Из моего опыта, есть набор тем, которые трудно понять в начале, но, как только вы получите их, вы действительно начнете понимать сложный код, и вы можете написать элегантный, чистый и эффективный код.
Если бы мне пришлось сделать топ-5 список тем, которые вызывают большее влияние на улучшение качества вашего кода, они будут:
- Закрытие
Прототип
функции- Значение
это
- Асинхроничность
- Тестирование подразделения и TDD
Это не единственные, но понимая их, вы сможете понять большую часть кода, который вы найдете, и сможете продлить этот код или исправить его в тестированном, поддерживаемым и понятным способам.
Допустим, это знания, которые вам нужно удалить младший Ярлык от вашего заголовка работы JavaScript.
Давайте начнем с закрытия.
1. Закрытие
Закрытие создается, когда функция, возвращаемая другой функцией, поддерживает доступ к переменным, расположенным на его родительской функции.
Понять закрытие Нам сначала нужно понимать две другие концепции: Цепочка охвата и Лексический охват Отказ
Функции имеют Цепочка охвата Это означает, что Функция n ()
внутри другой функции f ()
будет иметь доступ ко всем его переменным (те, которые в его объеме) и те, которые в прицепе отца Отказ
const a = 1 function f() { const b = 1 function n() { const c = 3 } }
В приведенном выше примере, от n ()
Мы можем получить доступ к А
и B
, но из f ()
Мы не можем получить доступ к C
Отказ
Функции имеют Лексический охват , это означает, что они Создать их прицел (какие переменные они могут получить доступ) Когда они определены, не когда они выполняются Отказ
Если мы определим это …
function f1(){ const a = 1; return f2(); } function f2(){ return a; }
Что мы получим, если мы выполним F1 ()
? Что мы получим, если определим var
И тогда мы попробуем еще раз F1 ()
?
>>> f1(); a is not defined >>> var a = 5; >>> f1(); 5 >>> a = 55; >>> f1(); 55 >>> delete a; true >>> f1(); a is not defined
Когда F2 ()
Создан, JavaScript проверяет, что он использует переменную с именем А
это не объявлено локально, поэтому любое использование А
будет искать в родительской области (глобальный объем в этом случае).
Вот почему, только когда мы присваиваем ценность для глобального А
, мы возвращаем эту ценность, когда мы выполняем F2 ()
Отказ
Обратите внимание, как это не имеет значения, где мы выполняем эту функцию. Когда функция создана, переменные функции могут получить доступ и от того, где становятся частью определения функции.
Итак, смешивая эти две концепции с тем, что мы можем вернуть функцию из другой функции, мы можем достичь любопытного эффекта.
function f(){ var b = "b"; return function(){ return b; } }
>>> b b is not defined >>> var n = f(); >>> n(); "b"
Из-за Лексический охват и Цепочка охвата Возвращенная функция поддерживает доступ к B
Даже когда не является частью функции возвращается и даже когда f ()
закончил свое исполнение. Этот эффект возвращенной функции поддержания доступа к переменным его ограждающей функции называется A закрытие Отказ
Закрытия везде.
С ними мы можем сделать функциональное программирование …
const mathy = x => y => z => (x / y) - z mathy(10)(2)(3) // 2 => (10/2)-3
Мы можем создавать частные и скрытые переменные …
var howManyExecutions = (() => { let executions = 0 return () => ++executions })() howManyExecutions() // 1 howManyExecutions() // 2 howManyExecutions() // 3
И много других прохладных вещей.
Больше читать о закрытиях:
- Закрытие |. MDN
- Закрытия JavaScript |. jibbering.com
- Понять закрытие JavaScript с легкостью | JavaScript это сексуально
- Объяснение JavaScript Cakure & Case цепи с примерами | risingstack.com
2. Прототип функции
Прототип
является свойством функций конструктора, которые содержат объект, который будет доступен другими объектами, созданными этой функцией.
В JavaScript, не только объект
это объект
Отказ массив
также объект
Отказ И а Функция
также объект
, но особый, потому что мы можем их выполнять.
({}) instanceof Object // true ([]) instanceof Object // true (function (){}) instanceof Object // true
Как объекты, функции могут иметь свойства. Итак, мы можем сказать …
function Hi() { return Hi.msg || 'Hello!' } Hi.msg = "Hola!"
По умолчанию все функции, когда созданные имеют свойство с именем Прототип
Отказ
Прототип
Свойство функций требует только для специального типа функции, Конструктор Функции, это, это функции, которые мы используем для создания объектов с помощью Новый
ключевое слово.
С Конструктор Функции, мы можем подготовить как шаблон для создания таких объектов …
function Person(name, city) { this.name = name this.city = city }
Если мы назовем эту функцию с Новый
Ключевое слово, мы получаем объект со всеми свойствами, назначенными на это
В функции конструктора. Это…
var me = new Person('Johnny','Barcelona') me // { name: 'Johnny', city: 'Barcelona' } var him = new Person('Marty','Hill Valley') him // { name: 'Marty', city: 'Hill Valley' }
Эти объекты, созданные Конструктор Функция получить доступ к Прототип
этого конструктора, даже после того, как они были созданы.
Итак, если мы сделаем …
Person.prototype.sayHi = function() { return "Hello I'm" + this.name }
Тогда мы можем сделать …
me.sayHi() // Hello I'm Johnny him.sayHi() // Hello I'm Marty
Методы размещения в Прототип
из Конструктор Функция – это очень эффективный способ обмена функциями среди его объектов. Общее правило при использовании Конструктор Функции для создания объектов:
- Свойства присваиваются объекту в функции конструктора
- Методы назначены на
Прототип
так что эти объекты могут получить доступ к ним в любом случае
С Класс
Ключевое слово, мы можем создать Конструктор Функция и назначать методы его прототипа непосредственно под той же структурой.
class Person { constructor(name, city) { this.name = name this.city = city } sayHi() { return `Hello I'm ${this.name}` } }
Вы не уверены, что увидите много кода, который использует это Прототип
функций, особенно с этим Класс
состав.
Больше читать о прототипе:
- Цепь наследования и татотипов | MDN
- Прототип JavaScript на простым языке | JavaScript это сексуально
3. Значение этого
это
Это специальное ключевое слово, доступное во всех функциях, которые принимают разные значения в зависимости от того, как называется функция.
Область применения функции устанавливается при определении функции. Но есть элемент, который значение которого устанавливается при выполнении функции. Это случай это
Отказ
const me = { name: 'Johnny', sayHi: function() { return `Hi, I'm ${this.name}` } } me.name // Johnny me.sayHi() // Hi, I'm Johnny
Когда мы используем это
Внутри способа объектов, он точки (по умолчанию) на объект, который содержит метод.
Но мы можем назвать этот метод и назначить еще один это
к этому, выполняя метод через Позвоните
Отказ
me.sayHi.call({ name: 'Jesie'}) // Hi, I'm Jesie
Мы также можем прикрепить постоянную это
к методу с привязывать
Отказ
const HiStuart = me.sayHi.bind({ name: 'Stuart'}) HiStuart() // Hi, I'm Stuart
И, как мы видели, когда используется в Конструктор функция и ее Прототип , это
представляет объект возвращен.
class Person { constructor(name, city) { this.name = name this.city = city } sayHi() { return `Hello I'm ${this.name}` } } const me = new Person('JuanMa','Barcelona') me.name // JuanMa me.city // Barcelona me.sayHi() // Hello I'm JuanMa
При использовании в обработчиках событий это
представляет элемент DOM, который слушает к мероприятию.
const myButton = document.querySelector('button) myButton.addEventListener('click', function() { console.log(this.innerText) });
При нажатии на кнопку отобразится в консоли Нажмите на меня
потому что это
В обработчике представляет выбор DOM.
Я думаю, что эти примеры о это
Покройте большую часть случаев использования, которые вы найдете там.
Больше читать об этом:
4. асинхроничность
Асинхронные операции – это те, которые запускаются, чтобы быть разрешены в течение некоторого времени в будущем, пока наш код продолжает его выполнение.
Асинхроничность является одним из крупных сделок в JavaScript. Выполняя операции асинхронно, мы можем оптимизировать производительность нашего приложения, потому что мы можем продолжать делать другие вещи, пока выполняется операция (или операции).
Например, в Nodejs мы можем прочитать и писать файлы синхронно …
const contentFile = fs.readFileSync('notes.txt') const numWords = contentFile.split(' ') const numLines = contentFile.split('\n') fs.writeFileSync('notes.txt', JSON.stringify({ numWords, numLines })) console.log(`Results written succesfully`)
В этом случае мы должны ждать в каждой строке, пока процесс не будет завершен, чтобы продолжить обработку следующей строки.
ПРИМЕЧАНИЕ. Я буду пропустить обработку ошибок в примерах с целью упрощения Отказ
… Или мы можем запустить эти операции асинхронно с помощью функций обратного вызова …
fs.readFile('notes.txt', (_, contentFile) => { const numWords = contentFile.split(' ') const numLines = contentFile.split('\n') fs.writeFile('results.txt', JSON.stringify({ numWords, numLines }), () => { console.log(`Results written succesfully`) }) });
Второй аргумент Fs.readfile
(а третий из fs.writefile
) – это то, что называется Функция обратного вызова Поскольку он будет выполнен с результатом, полученным в операции, когда этот результат готов.
Как мы можем видеть в примере, если мы объединяем несколько асинхронных операций с этим способом обратного вызова, код начинает все сложнее понимать и поддерживать (и мы можем заканчиваться чем-то называться Callback Hell ).
Другим способом управления асинхронными операциями более элегантным образом является использование обещаний.
А Обещание является объектом, который инкапсулирует асинхронную операцию, которая вернет либо некоторые данные, либо некоторые ошибки в будущем.
У нас много библиотек, которые уже выполняют операции и возвращающиеся обещания, но мы можем похищать Эти туземцы ввода/вывода ( обратный путь ) Методы …
const psReadFile = fileToRead => new Promise( resolve => { fs.readFile('notes.txt', (_, contentFile) => { resolve(contentFile) } }) const psWriteFile = contentToWrite => new Promise(resolve => { fs.writeFile('results.txt', contentToWrite, err => { resolve(`Results written succesfully`) } })
После того, как у нас есть выдиращенная версия этих двух методов, мы можем выразить наши операции более чистым способом …
psReadFile('notes.txt') .then(contentFile => { const numWords = contentFile.split(' ') const numLines = contentFile.split('\n') return JSON.stringify({ numWords, numLines }) }) .then(psWriteFile) .then(console.log)
Но мы можем пойти на шаг дальше и управлять этими обещаниями в более синхронном виде с Async-await
…
(async () => { const contentFile = await psReadFile('notes.txt') const numWords = contentFile.split(' ') const numLines = contentFile.split('\n') const writeFileResult = await psWriteFile(JSON.stringify({ numWords, numLines })) console.log(writeFileResult) })()
Этот последний код очень похож на первый, который использовал синхронные методы, верно?
Как вы можете видеть, с Async-await
мы можем получить синхронный синтаксис для асинхронные операции (так что мы можем иметь лучшее из обоих миров).
Больше читать о асинхроничности в JS:
- Эволюция асинхронного JavaScript | risingstack.com
- Знакомству, что asynchronous JavaScript: обратные вызовы, обещания и Async/ждут | medium.com
5. Тестирование и TDD
Если вы хотите серьезно относиться к JavaScript, вы должны доставить не только ваш Основной код , но и код, который тестирует, что ваш основной код делает то, что он должен.
Эти тесты обычно поставляются в виде единичных тестов.
А Устройство тестирования это кусок кода, который служит для проверки, работает ли другой кусок кода (обычно функция) правильно. Это код, который служит для проверки другого кода.
Эти модульные тестирования должны:
- иметь возможность быть запущенным автоматически (это особенно важно для A непрерывную интеграцию )
- Проверьте максимальный код возможен ( Code Coverage как можно выше)
- быть в состоянии выполнить столько раз, сколько необходимо
- быть независимым (выполнение одного теста не должно влиять на выполнение остальных).
- Поддерживать качество кода (кодовая конвенция, передовая практика, документация …)
TDD (Тестовое развитие) – это методология, способ программирования, рабочий процесс, который в основном состоит в том, что сначала состоит в том, чтобы выполнить тесты (указывая, что должен делать наш код) и после этого выполнение кода, который передает эти тесты.
Рекомендуемый рабочий процесс в TDD:
- Мы пишем тесты на новую функцию (при условии, что имена методов, входные параметры, возвращенные значения …)
- Мы выполняем тесты и проверьте, что они не проходят (мы все еще не написали код, который будет проверен
- Напишем простейшее решение, которое передает тесты
- Мы рефикторуем код (более чистый и более эффективный код, который все еще проходит тесты)
- Мы повторяем эти шаги для другой особенности
Применяя TDD, мы можем сосредоточиться на интерфейсе (API, методы, входы и выходы) больше, чем в деталях реализации.
Чтобы написать тестирование подразделений в рабочем процессе TDD, мы можем использовать помощь структуров тестирования, которые предоставляют нам:
- Структуры для записи тестов в организованном и простым для чтения
- Утверждения (или Matchers), которые являются семантическими функциями для сравнения ожидаемого значения с текущим нашим кодом, возвращается.
Итак, для этого куска кода …
function addValues( a, b ) { return a + b; };
Устройство тестирования в Жасмин может быть
describe("addValues(a, b) function", function() { it("should return the sum of the numbers passed as parameters", function(){ expect( addValues(1, 2) ).toBe( 3 ); }); it("should work also for decimal numbers", function(){ expect( addValues(1.75, 2) ).toBe( 3.75 ); }); it("should NOT return a String", function(){ expect( addValues(1, 2) ).not.toBe( "3" ); }); it("should accept numbers as strings", function(){ expect( addValues(1, '2') ).toBe( 3 ); }); });
В этом примере последний тест потерпит неудачу, и это нормально при использовании TDD. Помнить! Выполните тест → Выполните его → Проверьте его не удалось → выполнить код, чтобы пройти тест.
Итак, после того, как мы изменим код, как это:
function addValues( a, b ) { return parseInt(a) + parseInt(b); };
Все наши тесты пройдут. Таким образом, мы можем продолжать писать тесты, используя рабочий процесс TDD, пока не решим, что код достаточно протестирован
Больше читать о тестировании подразделения и TDD в JS:
- Обучение JavaScript Test-Delized Delivery на примере | sitepoint.com
- TDD обрядный путь | medium.com
- Учить тестируемое управление развитию (TDD) | GitHub.com
И это все. Я пытался дать краткое объяснение этих концепций, но, конечно, есть, конечно, нужно объяснить и обсуждаться на каждой теме.
Во всяком случае, это мои пять лучших тем для повышения качества кода. Что твое?