Вы можете думать о классе JavaScript как о доме, который содержит переменные и функции. По той же причине, по которой мы используем переменную для хранения чисел, мы используем класс для хранения функций; мы хотим иметь возможность создать шаблон, который позволит нам эффективно повторно использовать информацию.

Но классы особенные, потому что они работают как черновик, который другие люди могут использовать и создавать классные вещи. Например, у меня может быть класс под названием «Воин».

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

let bruceLee = Warrior.create(“Bruce Lee”);
bruceLee.kick();
// Don’t worry. We’ll get to how to make a class after this quick example.

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

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

let chuckNorris = Warrior.create(“Chuck Norris”);
chuckNorris.kick();

Но как мы создаем класс в первую очередь? Есть несколько способов создать класс в JavaScript. Мы можем разделить их на старые пути и новые пути. Некоторые пуристы предпочитают старый путь, но мы перейдем этот мост, когда доберемся до него.

Самый популярный способ создать класс в JavaScript — это использовать ключевое слово function, за которым следует имя функции с заглавной буквы:

function Warrior(){
}

Но мы также можем создать класс, используя литерал объекта:

let Warrior = new Class({
});

Или мы можем создать класс, используя функцию самовызова:

let Warrior = (function(){
})()

Новый способ создания класса в JavaScript заключается в использовании ключевого слова class, как и в любом другом языке:

class Warrior{
 constructor(){
 }
}

Как бы вы ни решили создать класс, компоненты класса обычно одни и те же. В этом уроке мы будем использовать первый метод, но я покажу вам, как сделать то же самое, используя новый метод «класса».

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

Мы помещаем эту информацию в то, что мы называем конструктором:

function User(){
//constructor
 this.name;
 this.height;
 this.age;
 this.weight;
}

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

Давайте сделаем еще один шаг, позволив кому-то присвоить значение классу, добавив в класс один параметр:

function User(object){
//constructor
 this.name = object.name;
 this.height = object.height;
 this.age = object.age;
 this.weight = object.weight;
}

Этот тип построения класса является отличной практикой, потому что он позволяет любому, кто вызывает класс, создавать свой объект, как сборка-медведь, не беспокоясь о том, чтобы что-то сломать или упорядочить что-либо в определенном порядке. Например:

let bob = User({ height:”7'2", age: 30, name:”Mike”, weight: “200 Ibs”})

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

Обратите внимание, что приведенный выше код не будет работать как есть, потому что мы не говорили о следующем аспекте классов JavaScript.

Частное против публичного

Данные внутри классов организованы в частные и общедоступные данные. Есть определенные вещи, к которым вы не хотите, чтобы посторонние имели доступ в классе, поэтому вы держите их в тайне. Например, это может быть алгоритм, вычисляющий ИМТ человека. Это всегда должно оставаться неизменным, поэтому вы не хотите, чтобы люди меняли это. Но мы ведь хотим иметь возможность изменить имя или вес, не так ли? Так что эти данные должны быть обнародованы.

Вот как мы отделяем общедоступные данные от частных данных в JavaScript. Обратите внимание, что в классической конструкции класса нет ключевых слов.

function User(construct){
//constructor
 this.name = construct.name; 
 this.height = construct.height;
 this.age = construct.age;
 this.weight = construct.weight;
//private
 let bmiMultiple = 703;
 let bmi = ()=>{
 return (this.weight / (this.height * this.height)) * bmiMultiple;
 }
//public
 return {
 name,
 height,
 age,
 weight,
 bmi
 }
}

Если бы мы попытались получить доступ к переменной bmiMultiple, мы бы не смогли этого сделать. Обратите внимание, что переменные, перед которыми стоит ключевое слово this, автоматически становятся доступными для получения и установки. В этом смысл конструктора — позволить кому-то построить объект сразу же, установив значения.

Примечание. Я обманываю, перечисляя переменные в операторе return без добавления двоеточия и значения, как в чистом ES5. ES6 допускает это сокращение. Не стесняйтесь смешивать старое с новым

После того, как вы создали свой класс…

let bob = User({ height:72, age: 30, name:”Mike”, weight: 200}) // 72 is in inches and 200 is in pounds

… затем вы можете получить информацию и установить информацию:

bob.name; // Mike
bob.name = “Joe”; // Joe
bob.weight; // 200 
bob.weight = 300;
bob.weight; // 300 Ibs

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

 // private property
 this.#weightCounter= 0;
// private method (can only be called within the class)
 #bmi() {
 this.#weightCounter++;
 }

Функциональность

Если вы изучили мою статью о функциях, вы заметите, что я использовал анонимную функцию, чтобы функция bmi могла плавно вписаться в оператор return. Вы можете использовать обычное объявление функции, но вы должны знать, что выбранная вами структура класса меняет способ вызова функций. По возможности лучше придерживаться анонимных функций, потому что они просто компактнее и читабельнее.

Мы можем вызвать нашу функцию так:

 bob.bmi(); // 27.12

Наследование

Мы должны кратко обсудить наследование. В конце концов, вы обнаружите, что вам нужно создавать больше классов, адаптированных для конкретных людей. Например, вы можете создать класс, посвященный тяжелоатлетам, и еще один класс, посвященный бегунам. Эти классы будут иметь разные черты, но у них все еще будут общие черты с общим классом пользователя, такие как имя, рост и т. д.

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

//constructor
function User(construct){
 this.name = construct.name;
 this.height = construct.height;
 this.age = construct.age;
 this.weight = construct.weight;
}
//prototype
User.prototype.bmi = ()=>{
 return (this.weight / (this.height * this.height)) * 703;
}
 
//inheriting constructor
function CardioUser(object){
 User.call(this, object)
}
//prototypical inheritance
CardioUser.prototype = User.prototype;
let andy = new CardioUser({name:”Joe”, height:72, age: 30, weight: 200});
 
andy.bmi();

Много чего надо разобрать. Я удалил функцию ИМТ из класса и назначил ее прототипу User, который затем можно передать CardioUser. Это наиболее функциональный способ создания классов в JavaScript. Сама функция служит только конструктором, в то время как прототип содержит любую динамическую функцию, которую вы хотите создать.

Затем я унаследовал конструктор, используя функцию вызова для пользователя.

Согласно Mozilla, «с помощью `call()` вы можете написать метод один раз, а затем наследовать его в другом объекте без необходимости переписывать метод для нового объекта». Первый параметр принимает ключевое слово this и дополнительные параметры, используемые для присвоения значения.

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

После этого я переназначил функции от User на CardioUSer и вала! CardioUser теперь унаследовал и конструктор, и функции.

Новый способ ведения дел намного проще:

class User {
 constructor(construct){
 this.name = construct.name;
 this.height = construct.height;
 this.age = construct.age;
 this.weight = construct.weight;
 }
bmi(){
 return (this.weight / (this.height * this.height)) * 703;
 }
}
class CardioUSer extends User {
 constructor(object){
 super(object)
 }
}
let bob = new CardioUSer({name:”Joe”, height:72, age: 30, weight: 200});
bob.bmi();

Вы, вероятно, знаете, что происходит, просто читая код. Я расширил класс User до Cardio. Вместо того, чтобы использовать call, я использовал super, который должен принимать только то, что я хочу присвоить своим унаследованным переменным. В этом случае мы используем литерал объекта.

Геттеры и сеттеры

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

set name (name) { 
this.name = name; 
} 
 
get name () { 
return this.name; 
}

Этот тип кодирования является хорошей практикой, хотя и может показаться избыточным. Старомодным способом сделать это было бы написать…

let setName = (name)=> { 
this.name = name; 
} 
 
let getName = (name)=> { 
return this.name; 
}

Вывод

Мы можем продолжать и продолжать рассказывать о классах, но пока этого должно быть достаточно. Вы знаете, что у вас есть основные строительные блоки, необходимые для создания функциональных приложений. Но какой метод лучше, спросите вы. Старый или новый? Я считаю, что старый метод действительно удобен в использовании только потому, что я не чувствую себя ограниченным параметрами, установленными классами ES6. Тем не менее, я нахожу классы ES6 приятными для просмотра, поскольку они основаны на Ruby, где классы компактны и удобочитаемы.

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

Но теперь, когда JavaScript используется во все большем количестве областей, кажется, что классы ES6 были разработаны, чтобы обеспечить удобство программистам, перешедшим с других языков сценариев. Так уж получилось, что новичкам легче понять новые классы ES6. По сравнению с этим старый способ ведения дел кажется немного хакерским.

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

Понравилась эта статья? Если да, то получите больше похожего контента, подписавшись на Decoded, наш канал на YouTube!