Автор оригинала: FreeCodeCamp Community Member.
Андреа Куутифарис
Во многих языках программирования классы являются четко определенной концепцией. В JavaScript это не так. Или, по крайней мере, это было не так. Если вы ищете O.O.P. И JavaScript вы столкнетесь со многими статьями с большим количеством различных рецептов о том, как вы можете эмулировать Класс
в JavaScript.
Есть ли простой, к.И. Способ определить класс в JavaScript? И если да, то почему так много разных рецептов определить класс?
Прежде чем ответить на эти вопросы, давайте лучше понять, какой JavaScript Объект
является.
Объекты в JavaScript
Начнем с очень простого примера:
const a = {}; a.foo = 'bar';
В приведенном выше фрагменте кода объект создан и улучшен с помощью свойства Foo
Отказ Возможность добавления вещей в существующий объект – это то, что делает JavaScript отличаться от классических языков, таких как Java.
Подробнее о том, что объект может быть усилен, позволяет создать экземпляр «неявный» класс без необходимости на самом деле создать класс. Давайте уточним эту концепцию на примере:
function distance(p1, p2) { return Math.sqrt( (p1.x - p2.x) ** 2 + (p1.y - p2.y) ** 2 ); } distance({x:1,y:1},{x:2,y:2});
В приведенном выше примере мне не нужен класс точки для создания точки, я только что расширил экземпляр Объект
Добавление х
и y
характеристики. Дистанция функции не заботится, если аргументы являются экземпляром класса Точка
или нет. Пока вы не позвоните Расстояние
Функция с двумя объектами, которые имеют х
и y
Свойство типа Номер
это будет работать просто хорошо. Эта концепция иногда называется Утка печатает Отказ
До настоящего времени я использовал только объект данных: объект, содержащий только данные и без функций. Но в JavaScript можно добавлять функции на объект:
const point1 = { x: 1, y: 1, toString() { return `(${this.x},${this.y})`; } }; const point2 = { x: 2, y: 2, toString() { return `(${this.x},${this.y})`; } };
На этот раз объекты, представляющие 2D-точку, имеют TOSTRING ()
метод. В примере выше, TOSTRING
Код был продублирован, и это не хорошо.
Есть много способов избежать этого дублирования, и, по сути, в разных статьях об объектах и классах в JS вы найдете разные решения. Вы когда-нибудь слышали о «раскрывающемся модуле»? Он содержит слова «шаблон» и «раскрытие», звучит круто, а «модуль» является обязательным. Поэтому он должен быть правильным способом создавать объекты … за исключением того, что это не так. Выявление шаблона модуля может быть правильным выбором в некоторых случаях, но это определенно не является способом создания объектов с поведением.
Теперь мы готовы ввести классы.
Классы в JavaScript
Что такое класс? Из словаря: класс – это «набор или категория вещей, имеющих некоторые свойства или атрибут общего и дифференцированного от других по доброй, тип или качество».
На языках программирования мы часто говорим, что «объект является экземпляром класса». Это означает, что, используя класс, я могу создать много объектов, и все они обмениваются методами и свойствами.
Поскольку объекты могут быть усилены, как мы видели ранее, есть могут способы создания методов обмена объектами и свойствами. Но мы хотим простейший.
К счастью ECMAScript 6 обеспечивает ключевое слово Класс
Создание очень легко создать класс:
class Point { constructor(x, y) { this.x = x; this.y = y; } toString() { return `(${this.x},${this.y})`; } }
Итак, на мой взгляд, это лучший способ объявления классов в JavaScript. Классы часто связаны с наследством:
class Point extends HasXY { constructor(x, y) { super(x, y); } toString() { return `(${this.x},${this.y})`; } }
Как вы можете видеть в примере выше, чтобы продлить другой класс достаточно использовать ключевое слово расширяется
Отказ
Вы можете создать объект из класса, используя Новый
Оператор:
const p = new Point(1,1); console.log(p instanceof Point); // prints true
Хороший объектно-ориентированный способ определения классов должен предоставить:
- простой синтаксис, чтобы объявить класс
- Простой способ доступа к текущему экземпляру A.K.A.
это
- простой синтаксис для продления класса
- Простой способ получить доступ к экземпляру Super Class, A.K.a.
супер
- Возможно, простой способ сказать, если объект является экземпляром определенного класса.
OBJ Instance Oclass
должен вернутьсяправда
Если этот объект является экземпляром этого класса.
Новый Класс
Синтаксис предоставляет все точки выше.
До внедрения Класс
Ключевое слово, какой способ определить класс в JavaScript?
Кроме того, что действительно класс в JavaScript? Почему мы часто говорим о Прототипы ?
Классы в JavaScript 5
От Mozilla MDN Страница о классах :
Ключевая концепция здесь Наследование на основе прототипа Отказ Поскольку есть много недоразумений на том, что такое наследование, я продолжу шаг за шагом, переходя от Класс
ключевое слово в Функция
ключевое слово.
class Shape {} console.log(typeof Shape); // prints function
Похоже, что Класс
и Функция
относятся к. Это Класс
просто псевдоним для Функция
? Нет, это не так.
Shape(2); // Uncaught TypeError: Class constructor Shape cannot be invoked without 'new'
Итак, кажется, что люди, которые ввели Класс
Ключевое слово хотел сказать нам, что класс – это функция, которая должна называться, используя Новый
оператор.
var Shape = function Shape() {} // Or just function Shape(){} var aShape = new Shape(); console.log(aShape instanceof Shape); // prints true
Пример выше показывает, что мы можем использовать Функция
объявить класс. Однако мы не можем заставить пользователю позвонить в функцию, используя Новый
оператор. Можно выбрасывать исключение, если Новый
Оператор не использовался для вызова функции.
Во всяком случае, я предлагаю вам не ставить эту проверку в каждой функции, которая действует как класс. Вместо этого используйте эту конвенцию: любая функция, имя которой начинается с заглавной буквы, является классом и должна называться «|» Новый оператор.
Давайте будем двигаться дальше и узнать, что a Прототип является:
class Shape { getName() { return 'Shape'; } } console.log(Shape.prototype.getName); // prints function getName() ...
Каждый раз, когда вы объявляете метод в классе, вы фактически добавляете этот метод для прототипа соответствующей функции. Эквивалент в JS 5:
function Shape() {} Shape.prototype.getName = function getName() { return 'Shape'; }; console.log(new Shape().getName()); // prints Shape
Иногда класс-функции называются Конструкторы Потому что они действуют как конструкторы в обычном классе.
Вы можете задаться вопросом, что произойдет, если вы объявите статический метод:
class Point { static distance(p1, p2) { // ... } } console.log(Point.distance); // prints function distance console.log(Point.prototype.distance); // prints undefined
Поскольку статические методы находятся в от 1 до 1 отношения с классами, статическая функция добавляется в конструктор-функцию, а не к прототипу.
Давайте повторим все эти концепции в простом примере:
function Point(x, y) { this.x = x; this.y = y; } Point.prototype.toString = function toString() { return '(' + this.x + ',' + this.y + ')'; }; Point.distance = function distance() { // ... } console.log(new Point(1,2).toString()); // prints (1,2) console.log(new Point(1,2) instanceof Point); // prints true
До настоящего времени мы нашли простой способ:
- объявить функцию, которая действует как класс
- Доступ к экземпляру класса, используя
это
ключевое слово - Создание объектов, которые на самом деле являются экземпляром этого класса (
Новая точка (1,2) точка экземпляра
возвращаетTrue
)
Но как насчет наследства? Как насчет доступа к супер классу?
class Hello { constructor(greeting) { this._greeting = greeting; } greeting() { return this._greeting; } } class World extends Hello { constructor() { super('hello'); } worldGreeting() { return super.greeting() + ' world'; } } console.log(new World().greeting()); // Prints hello console.log(new World().worldGreeting()); // Prints hello world
Выше представляет собой простой пример наследования, используя ECMAScript 6, ниже того же примера, используя так называемый Прототип наследования :
function Hello(greeting) { this._greeting = greeting; } Hello.prototype.greeting = function () { return this._greeting; }; function World() { Hello.call(this, 'hello'); } // Copies the super prototype World.prototype = Object.create(Hello.prototype); // Makes constructor property reference the sub class World.prototype.constructor = World; World.prototype.worldGreeting = function () { const hello = Hello.prototype.greeting.call(this); return hello + ' world'; }; console.log(new World().greeting()); // Prints hello console.log(new World().worldGreeting()); // Prints hello world
Этот способ объявления классов также предлагается в примере Mozilla MDN здесь Отказ
Используя Класс
Синтаксис, мы вывели, что создание классов включает в себя изменение прототипа функции. Но почему так? Чтобы ответить на этот вопрос, мы должны понимать, что Новый
Оператор на самом деле делает.
Новый оператор в JavaScript
Новый
Оператор объясняется довольно хорошо на странице Mozilla MDN здесь Отказ Но я могу предоставить вам относительно простой пример, который эмулирует, что Новый
Оператор делает:
function customNew(constructor, ...args) { const obj = Object.create(constructor.prototype); const result = constructor.call(obj, ...args); return result instanceof Object ? result : obj; } function Point() {} console.log(customNew(Point) instanceof Point); // prints true
Обратите внимание, что реальный Новый
Алгоритм сложнее. Целью приведенного выше примера является просто объяснить, что происходит, когда вы используете Новый
оператор.
Когда вы пишете Новая точка (1,2)
Что происходит:
-
Точка
Прототип используется для создания объекта. - Вызывается конструктор функции, и только созданный объект пропускается как контекст (A.k.a.
это
) вместе с другими аргументами. - Если конструктор возвращает объект, то этот объект является результатом нового, в противном случае объект, созданный из прототипа, является результатом.
Итак, что делает Прототип наследования иметь в виду? Это означает, что вы можете создавать объекты, которые наследуют все свойства, определенные в прототипе функции, называемую с Новый
оператор.
Если вы думаете об этом, на классическом языке происходит тот же процесс: когда вы создаете экземпляр класса, этот экземпляр может использовать это
Ключевое слово для доступа ко всем функциям и свойствам (общедоступным), определенным в классе (и предках). В зависимости от свойств все экземпляры класса, скорее всего, будут делиться теми же ссылками на методы класса, поскольку нет необходимости дублировать двоичный код метода.
Функциональное программирование
Иногда люди говорят, что JavaScript не очень подходит для объектно-ориентированного программирования, и вместо этого следует использовать функциональное программирование.
Пока я не согласен, что JS не подходит для O.O.P, я думаю, что функциональное программирование – это очень хороший способ программирования. В функциях JavaScript есть Первый класс граждан (например, вы можете передавать функцию другую функцию), и она обеспечивает такие функции, как привязывать
, Позвоните
или Применить
которые являются базовыми конструкциями, используемыми в функциональном программировании.
Кроме того, программирование RX можно рассматривать как эволюцию (или специализацию) функционального программирования. Посмотрите на RXJS здесь Отказ
Заключение
Используйте, когда это возможно, Ecmascript 6 Класс
синтаксис:
class Point { toString() { //... } }
Или используйте функциональные прототипы для определения классов в Ecmascript 5:
function Point() {} Point.prototype.toString = function toString() { // ... }
Надеюсь, вам понравилось чтение!