почему важно писать хороший код |

1656522256 pochemu vazhno pisat horoshij kod

Используя простую терминологию и реальный пример, публикация объясняет, что именно this есть и почему это полезно.

Это для вас

Я заметил, что много объяснений для this в JavaScript обучают, предполагая, что вы используете какой-то объектно-ориентированный язык программирования, таких как Java, C++ или Python. Эта публикация предназначена для тех, кто не имеет предубеждений о том, что вы думаете this есть или то, что должно быть. Я попробую объяснить что this есть и почему это полезно простой манерой без лишнего жаргона.

Возможно, вы медлили с погружением this потому что это выглядело странно и страшно. Или, возможно, вы используете его только потому, что StackOverflow говорит, что он вам нужен для выполнения определенных действий в React.

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

Функциональное против объектно-ориентированного программирования

Вы можете знать или не знать, что JavaScript имеет как функциональные, так и объектно-ориентированные конструкции, поэтому вы можете сосредоточиться на одном или другом или использовать обе.

Я принял функциональное программирование в начале своего пути в JavaScript и избегал объектно-ориентированного программирования как от чумы. Я не знал и не понимал объектно-ориентированных ключевых слов, таких как this. Я думаю, что одна из причин, почему я этого не понял, заключалась в том, что я действительно не понимал, зачем это нужно. Казалось, я могу сделать все, что нужно, не полагаясь на это this.

И я был прав.

Разновидность. Возможно, вы можете получить, сосредоточившись на одной парадигме и никогда не узнавая о другой, но вы будете ограничены как разработчик JavaScript. Чтобы проиллюстрировать различия между функциональным и объектно-ориентированным программированием, я собираюсь использовать массив данных друзей Facebook в качестве примера.

Скажем, вы создаете веб-приложение, в которое пользователь входит с помощью Facebook, и вы показываете некоторые данные о его друзьях в Facebook. Чтобы получить данные их друзей, вам нужно будет перейти к конечной точке Facebook. Он может иметь некоторую информацию, например firstName, lastName,username, numFriends, friendData, birthdayи lastTenPosts.

const data = [
  {
    firstName: 'Bob',
    lastName: 'Ross',
    username: 'bob.ross',    
    numFriends: 125,
    birthday: '2/23/1985',
    lastTenPosts: ['What a nice day', 'I love Kanye West', ...],
  },
  ...
]

Вышеприведенные данные – это то, что вы получаете от (поддельного, воображаемого) API Facebook. Теперь вам нужно трансформировать его, чтобы он был в формате, который будет полезен вам и вашему проекту. Скажем, вы хотите показать следующее для каждого из друзей пользователя:

  • Их название в формате `${firstName} ${lastName}`
  • Три случайных поста
  • Количество дней до дня рождения

Функциональный подход

Функциональный подход заключался в передаче целого массива или каждого элемента массива в функцию, возвращающую нужные вам манипулированные данные:

const fullNames = getFullNames(data)
// ['Ross, Bob', 'Smith, Joanna', ...]

Вы начинаете с необработанных данных (с API Facebook). Чтобы заставить его преобразовать в полезные для вас данные, вы передаете данные в функцию, а исходные данные содержат или включают манипулированные данные, которые можно использовать в своей программе для отображения пользователю.

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

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

Объектно-ориентированный подход

Объектно-ориентированный подход может быть несколько сложнее для понимания тем, кто только начинает программировать и изучает JavaScript. Идея заключается в том, что вы превращаете каждого друга в объект, который имеет все необходимое для создания чего ты как нужно разработчику

Вы можете создавать объекты, которые имеют fullName собственность и две функции getThreeRandomPosts и getDaysUntilBirthday которые характерны для этого друга.

function initializeFriend(data) {
  return {
    fullName: `${data.firstName} ${data.lastName}`,
    getThreeRandomPosts: function() {
      // get three random posts from data.lastTenPosts
    },
    getDaysUntilBirthday: function() {
      // use data.birthday to get the num days until birthday
    }
  };
}
const objectFriends = data.map(initializeFriend)
objectFriends[0].getThreeRandomPosts() 
// Gets three of Bob Ross's posts

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

Какое отношение это имеет к этому?

Возможно, вы никогда не думали написать что-то подобное initializeFriend выше, и вы можете подумать, что нечто подобное может быть очень полезно. Однако вы также можете заметить, что это не так действительно объектно-ориентированный.

Единственная причина, что методы getThreeRandomPosts или getDaysUntilBirthday даже сработает в приведенном выше примере из-за закрытия. Они все еще имеют доступ к data после initializeFriend возвращается через закрытие. Чтобы получить дополнительную информацию о закрытии, просмотрите вы не знаете JS: объем и закрытие.

Что если бы у вас был другой метод, назовем его greeting. Обратите внимание, что метод (относительно объекта в JavaScript) — просто атрибут, значением которого является функция. Мы хотим greeting сделать нечто подобное:

function initializeFriend(data) {
  return {
    fullName: `${data.firstName} ${data.lastName}`,
    getThreeRandomPosts: function() {
      // get three random posts from data.lastTenPosts
    },
    getDaysUntilBirthday: function() {
      // use data.birthday to get the num days until birthday
    },
    greeting: function() {
      return `Hello, this is ${fullName}'s data!`
    }
  };
}

Это сработает?

Нет!

Все в нашем вновь созданном объекте имеет доступ ко всем переменным в initializeFriend но не любой из атрибутов или методов в самом объекте. Конечно, вы зададите вопрос:

Вы не могли бы просто использовать data.firstName и data.lastName вернуть приветствие?

Да, вы совершенно могли. Но что, если бы мы тоже хотели указать в поздравлении, сколько дней до дня рождения этого друга? Надо бы как-то найти способ позвонить getDaysUntilBirthday изнутри greeting.

НАСТАЛО ВРЕМЯ this !

CjfAp0G6O8yFJPu4aKOV8tvPjs2kt0eCaWct
Фото: Сидни Рэй на Unsplash

Наконец, что это такое

this может ссылаться на разные вещи при разных обстоятельствах. По умолчанию, this ссылается на глобальный объект (в браузере это файл window объект), что не очень полезно. The this Правило, которое сейчас для нас полезно:

Если this используется в методе объекта, и метод вызывается в контексте этого объекта, this относится к самому объекту.

Вы говорите «вызываемое в контексте этого объекта»… что это вообще означает?

Не беспокойтесь, мы поговорим об этом позже!

Так что если мы хотим позвонить getDaysUntilBirthday изнутри greeting мы можем просто позвонить this.getDaysUntilBirthday поскольку this в этом сценарии просто ссылается на сам объект.

БОКОВАЯ ПРИМЕЧАНИЕ: Не используйте this в обычной функции OLE в глобальной области или в области действия другой функции! this является объектно-ориентированной конструкцией. Поэтому оно имеет значение только в контексте объекта (или класса)!

Давайте переделаем initializeFriend использовать this:

function initializeFriend(data) {
  return {
    lastTenPosts: data.lastTenPosts,
    birthday: data.birthday,    
    fullName: `${data.firstName} ${data.lastName}`,
    getThreeRandomPosts: function() {
      // get three random posts from this.lastTenPosts
    },
    getDaysUntilBirthday: function() {
      // use this.birthday to get the num days until birthday
    },
    greeting: function() {
      const numDays = this.getDaysUntilBirthday()      
      return `Hello, this is ${this.fullName}'s data! It is ${numDays} until ${this.fullName}'s birthday!`
    }
  };
}

Теперь все, что нужно этому объекту, однократно применено к самому объекту intializeFriend выполняется. Наши методы больше не полагаются на закрытие. Они используют только информацию, содержащуюся в самом объекте.

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

Бывают случаи, когда хочется заставить this быть чем-то особенным. Хороший пример для обработчиков событий. Скажем, мы хотели бы открыть страницу друга в Facebook, когда пользователь нажимает на него. Мы можем добавить onClick метод к нашему объекту:

function initializeFriend(data) {
  return {
    lastTenPosts: data.lastTenPosts,
    birthday: data.birthday,
    username: data.username,    
    fullName: `${data.firstName} ${data.lastName}`,
    getThreeRandomPosts: function() {
      // get three random posts from this.lastTenPosts
    },
    getDaysUntilBirthday: function() {
      // use this.birthday to get the num days until birthday
    },
    greeting: function() {
      const numDays = this.getDaysUntilBirthday()      
      return `Hello, this is ${this.fullName}'s data! It is ${numDays} until ${this.fullName}'s birthday!`
    },
    onFriendClick: function() {
      window.open(`
    }
  };
}

Обратите внимание, что мы добавили username к нашему объекту, так что onFriendClick имел доступ к нему, чтобы мы могли открыть новое окно со страницей Facebook этого друга. Теперь нам просто нужно написать HTML:

<button id="Bob_Ross">
  <!-- A bunch of info associated with Bob Ross -->
</button> 

А теперь JavaScript:

const bobRossObj = initializeFriend(data[0])
const bobRossDOMEl = document.getElementById('Bob_Ross')
bobRossDOMEl.addEventListener("onclick", bobRossObj.onFriendClick)

В вышеприведенном коде мы создаем объект для Боба Росса. Мы получаем элемент DOM, связанный с Бобом Россом. А теперь мы хотим выполнить onFriendClick способ открыть страницу Боба в Facebook. Должен работать, как ожидалось, не правда ли?

Нет!

Что пошло не так ли?

Обратите внимание, что функция, которую мы выбрали для обработчика onclick, была bobRossObj.onFriendClick . Вы уже видите проблему? Что если бы мы переписали это так:

bobRossDOMEl.addEventListener("onclick", function() {  window.open(`})bobRossDOMEl.addEventListener("onclick", function() {
  window.open(`
})

Теперь вы видите проблему? Когда мы устанавливаем обработчик onclick как bobRossObj.onFriendClick то, что мы на самом деле делаем, это увлечение функции, которая сохраняется в bobRossObj.onFriendClick и передавая это как довод. Он больше не «привязан». bobRossObj что означает this больше не относится к bobRossObj . На самом деле это относится к глобальному объекту, что означает, что this.username является неопределенным. Кажется, нам не повезло сейчас.

НАСТАЛО ВРЕМЯ для bind!

o36QYF-UudyA0jO8JbooQYneFJo5jeA2oAts
Фото Ксении Макагоновой на Unsplash

Явно обязывает это

То, что нам нужно сделать, это явно привязать. this к bobRossObj. Мы можем сделать это с помощью bind:

const bobRossObj = initializeFriend(data[0])
const bobRossDOMEl = document.getElementById('Bob_Ross')
bobRossObj.onFriendClick = bobRossObj.onFriendClick.bind(bobRossObj)
bobRossDOMEl.addEventListener("onclick", bobRossObj.onFriendClick)

раньше, this было установлено на основе правила по умолчанию. С использованием bindмы явно устанавливаем значение this в bobRossObj.onFriendClick быть самим объектом, или bobRossObj.

До этого момента мы видели, почему this полезно и почему вы можете захотеть явно привязать this. Последняя тема, которую мы рассмотрим this являются стрелочными функциями.

Функции стрелки

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

Возможно, самый простой способ описать, как функции стрелок отличаются:

Что угодно this ссылается на то место, где объявляется функция стрелки, this ссылается на то же внутри этой стрелковой функции.

Хорошо…это не помогает…Я думал, что это нормальное поведение?

Объясним с нашим initializeFriend пример. Скажем, мы хотели добавить внутрь небольшую вспомогательную функцию greeting :

function initializeFriend(data) {
  return {
    lastTenPosts: data.lastTenPosts,
    birthday: data.birthday,
    username: data.username,    
    fullName: `${data.firstName} ${data.lastName}`,
    getThreeRandomPosts: function() {
      // get three random posts from this.lastTenPosts
    },
    getDaysUntilBirthday: function() {
      // use this.birthday to get the num days until birthday
    },
    greeting: function() {
      function getLastPost() {
        return this.lastTenPosts[0]
      }
      const lastPost = getLastPost()           
      return `Hello, this is ${this.fullName}'s data!
             ${this.fullName}'s last post was ${lastPost}.`
    },
    onFriendClick: function() {
      window.open(`
    }
  };
}

Сработает ли это? Если нет, то как мы можем изменить его, чтобы он работал?

Нет, это не получится. Поскольку getLastPost не вызывается в контексте объекта, this внутри getLastPost возвращается к правилу по умолчанию, являющемуся глобальным объектом.

Вы говорите, что он не называется «в контексте объекта»… разве вы не знаете, что он вызывается внутри возвращаемого объекта initializeFriend? Если это не называется «в контексте объекта», то я не знаю, что такое.

Я знаю, что «в контексте объекта» – это расплывчатая терминология. Возможно, хороший способ определить, называется ли функция «в контексте объекта» — это поговорить о том, как она вызывается, и определить, присоединен ли объект к функции.

Давайте поговорим о том, что происходит, когда мы выполняем bobRossObj.onFriendClick(). «Возьми мне предмет bobRossObjнайдите атрибут onFriendClick и вызвать функцию, предназначенную этому атрибуту».

Теперь давайте поговорим о том, что происходит, когда мы выполняем getLastPost(). «Возьмите мне функцию с названием getLastPost и позвоните по телефону». Обратите внимание, как не было упоминания об объекте?

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

functionCaller(fn) {
  fn()
}

Что если бы мы сделали это: functionCaller(bobRossObj.onFriendClick)? Вы бы так сказали onFriendClick назывался «в контексте объекта»? Б this.username определить?

Давайте поговорим об этом: «Возьмите предмет bobRossObj и найдите атрибут onFriendClick. Возьмите его значение (являющееся функцией), передайте его functionCallerи назовите его fn. Теперь выполните функцию с названием fn». Обратите внимание, что функция «отключается». bobRossObj перед его вызовом и, следовательно, не называется «в контексте объекта bobRossObj», что означает это this.username будет неопределенно.

На помощь приходят функции стрелки:

function initializeFriend(data) {
  return {
    lastTenPosts: data.lastTenPosts,
    birthday: data.birthday,
    username: data.username,    
    fullName: `${data.firstName} ${data.lastName}`,
    getThreeRandomPosts: function() {
      // get three random posts from this.lastTenPosts
    },
    getDaysUntilBirthday: function() {
      // use this.birthday to get the num days until birthday
    },
    greeting: function() {
      const getLastPost = () => {
        return this.lastTenPosts[0]
      }
      const lastPost = getLastPost()           
      return `Hello, this is ${this.fullName}'s data!
             ${this.fullName}'s last post was ${lastPost}.`
    },
    onFriendClick: function() {
      window.open(`
    }
  };
}

Наше правило сверху:

Что угодно this ссылается на то место, где объявляется функция стрелки, this ссылается на то же внутри этой стрелковой функции.

Функция стрелки объявляется внутри greeting . Мы знаем это, когда используем this в greeting это относится к самому объекту. поэтому this Внутри стрелочной функции ссылается на объект, который мы и хотим.

Вывод

this это иногда непонятный, но полезный инструмент для разработки приложений JavaScript. Это точно не все, что можно this. Некоторые темы, которые не были рассмотрены:

  • call и apply
  • как this меняется, когда new участвует
  • как this изменения из ES6class

Я призываю вас задавать себе вопрос о том, что вы думаете this должно быть в определенных ситуациях, а затем проверьте себя, запустив этот код в обозревателе. Если вы хотите узнать больше о this смотрите вы не знаете JS: это и прототипы объектов.

А если вы хотите проверить себя, посмотрите упражнения YDKJS: это и прототипы объектов.

6MubkHTI9p32BuBFH5wqv-Sqp2DQBxLPhdDj
Фото Йонаса Якобссона на Unsplash

Добавить комментарий

Ваш адрес email не будет опубликован.