Рубрики
Без рубрики

5 Темы, которые вы должны обрабатывать, чтобы начать быть серьезным разработчиком JavaScript

В этом посте @juanmagar объясняет 5 продвинутых тем JavaScript, что значительно улучшится качество вашего кода: замыкание, прототип функции, значение `Это, асинхроничность и модульные тестирования с TDD.

Автор оригинала: 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

И много других прохладных вещей.

Больше читать о закрытиях:

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}`
  }
}

Вы не уверены, что увидите много кода, который использует это Прототип функций, особенно с этим Класс состав.

Больше читать о прототипе:

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:

5. Тестирование и TDD

Если вы хотите серьезно относиться к JavaScript, вы должны доставить не только ваш Основной код , но и код, который тестирует, что ваш основной код делает то, что он должен.

Эти тесты обычно поставляются в виде единичных тестов.

А Устройство тестирования это кусок кода, который служит для проверки, работает ли другой кусок кода (обычно функция) правильно. Это код, который служит для проверки другого кода.

Эти модульные тестирования должны:

  • иметь возможность быть запущенным автоматически (это особенно важно для A непрерывную интеграцию )
  • Проверьте максимальный код возможен ( Code Coverage как можно выше)
  • быть в состоянии выполнить столько раз, сколько необходимо
  • быть независимым (выполнение одного теста не должно влиять на выполнение остальных).
  • Поддерживать качество кода (кодовая конвенция, передовая практика, документация …)

TDD (Тестовое развитие) – это методология, способ программирования, рабочий процесс, который в основном состоит в том, что сначала состоит в том, чтобы выполнить тесты (указывая, что должен делать наш код) и после этого выполнение кода, который передает эти тесты.

Рекомендуемый рабочий процесс в TDD:

  1. Мы пишем тесты на новую функцию (при условии, что имена методов, входные параметры, возвращенные значения …)
  2. Мы выполняем тесты и проверьте, что они не проходят (мы все еще не написали код, который будет проверен
  3. Напишем простейшее решение, которое передает тесты
  4. Мы рефикторуем код (более чистый и более эффективный код, который все еще проходит тесты)
  5. Мы повторяем эти шаги для другой особенности

Применяя 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:

И это все. Я пытался дать краткое объяснение этих концепций, но, конечно, есть, конечно, нужно объяснить и обсуждаться на каждой теме.

Во всяком случае, это мои пять лучших тем для повышения качества кода. Что твое?