— Расширение объекта в Javascript
И истинная прототипическая природа Javascript
*Этот пример обновлен для классов ES6 и TypeScript.
Во-первых, Javascript — это прототип языка , а не основанный на классах. Его истинная природа выражена в приведенной ниже прототипной форме, которая, как вы можете увидеть, очень проста, похожа на прозу, но мощна.
TLDR;
Javascript
const Person = { name: 'Anonymous', // у человека есть имя приветствие: function() { console.log(`Привет, я ${this.name}.`) } } const jack = Object.create(Person) // jack — это человек jack.name = 'Джек' // и имеет имя 'Джек' jack.greet() // выводит «Привет, я Джек».
TypeScript
В TypeScript вам потребуется настроить интерфейсы, которые будут расширяться по мере создания потомков прототипа Person
. Мутация subjectGreet
показывает пример присоединения нового метода к потомку jack
.
интерфейс IPerson расширяет объект { имя: строка приветствовать(): недействительно } константа Человек: IPerson = { имя: "Аноним", приветствовать() { console.log(`Привет, я ${this.name}.`) } } интерфейс IPitePerson расширяет IPerson { subjectGreet: (title: 'Сэр' | 'Mdm') =>void } const PolitePerson: IPlitePerson = Object.create(Person) PolitePerson.politeGreet = функция (название: строка) { console.log(`Уважаемый ${title}! Я ${this.name}.`) } константный разъем: IPlitePerson = Object.create(Person) jack.name = 'Джек' jack.politeGreet = функция (название): недействительным { console.log(`Уважаемый ${title}! Я ${this.name}.`) } jack.greet() // "Привет, я Джек." jack.politeGreet('Сэр') // "Уважаемый сэр, я Джек."
#greet()
), которого нет в новом объекте jack
, старый объект Person
предоставит член.
По словам Дугласа Крокфорда: «Объекты наследуются от объектов. Что может быть более объектно-ориентированным, чем это?»
Вам не нужны конструкторы, нет новая реализация
. Вы просто создаете объекты, а затем расширяете или трансформируете их.
Этот шаблон также предлагает неизменяемость (частичную или полную) и геттеры/сеттеры.
Чистый и чистый. Его простота не ставит под угрозу функции. Читай дальше.
Создание потомка/копии Person
прототип
(технически более правильный, чем класса
).*Примечание. Ниже приведены примеры на JS. Чтобы писать на Typescript, просто следуйте приведенному выше примеру, чтобы настроить интерфейсы для набора текста.
const Скайуокер = Object.create(Person) Skywalker.lastName = 'Скайуокер' Скайуокер.firstName = '' Skywalker.type = 'человек' Skywalker.greet = function() { console.log(`Привет, меня зовут ${this.firstName} ${this.lastName}, и я ${this.type}.` const anakin = Object.create(Скайуокер) anakin.firstName = 'Энакин' anakin.birthYear = '442 ДБЯ' anakin.gender = 'мужской' // вы можете добавить новые свойства. anakin.greet() // 'Привет, меня зовут Энакин Скайуокер, и я человек.' Person.isPrototypeOf(Skywalker) // выводит true Person.isPrototypeOf(anakin) // выводит true Skywalker.isPrototypeOf(anakin) // выводит true
Если вы чувствуете себя менее безопасно, отказываясь от конструкторов вместо прямого присваивания, один из распространенных способов — присоединить метод #create
:
пусть скайуокер = Object.create(Скайуокер)
Object.assign(скайуокер, {
имя,
год рождения,
пол,
фамилия: ‘Скайуокер’,
тип: «человек»
})
вернуть скайуокера
}
const anakin = Skywalker.create(‘Энакин’, ‘мужчина’, ‘442 ДБЯ’)
Ветвление прототипа
Person
в Robot
прототип, расширив прототип `Person`: константный робот = Object.create(человек) Robot. type = ‘робот’
Методы прикрепления, уникальные для робота
Robot.machineGreet = function() { /*некоторая функция для преобразования строк в двоичные файлы */ } // Мутация объекта "Робот" не влияет на прототип "Человек" и его потомков anakin.machineGreet() // ошибка Person.isPrototypeOf(Robot) // выводит true Robot.isPrototypeOf(Skywalker) // выводит false
В TypeScript вам также потребуется расширить интерфейс Person
:
interface Robot extends Person { машинаГреет(): недействительно } const Robot: Robot = Object.create(Person) Robot.machineGreet = функция () { console.log (101010) }
И у вас могут быть миксины — Потому что… Дарт Вейдер человек или робот?
const ДартВейдер = Object.create(анакин) // для краткости присвоение свойств пропущено, потому что вы уже поняли суть. Object.assign(ДартВейдер, Робот)
Дарт Вейдер получает методы робота
:
darthVader.greet() // унаследовано от `Person`, выводит "Привет, меня зовут Дарт Вейдер..." darthVader.machineGreet() // наследуется от `Robot`, выводит 001010011010...
Наряду с другими странностями:
console.log(darthVader.type) // выводит robot. Robot.isPrototypeOf(darthVader) // возвращает false. Person.isPrototypeOf(darthVader) // возвращает true.
Что элегантно отражает субъективность «реальной жизни»:
«Теперь он больше машина, чем человек, извращенный и злой.» — Оби-Ван Кеноби
«Я знаю, что в тебе есть хорошее.» — Люк Скайуокер
Сравните с «классическим» эквивалентом до ES6:
function Person (имя, фамилия, год рождения, тип) { this.firstName = имя this.lastName = фамилия this.birthYear = год рождения этот.тип = тип } // присоединение методов Person.prototype.name = function() { return firstName + ' ' + lastName } Person. prototype.greet = function() { ... } Person.prototype.age = function() { ... } функция Скайуокер (имя, год рождения) { Person.apply(это, [имя, 'Скайуокер', год рождения, 'человек']) } // запутанное повторное указание... Скайуокер.прототип = Человек.прототип Skywalker.prototype.constructor = Скайуокер const anakin = новый Скайуокер('Энакин', '442 ДБЯ') // #isPrototypeOf не будет работать Person.isPrototypeOf(anakin) // возвращает ложь Skywalker.isPrototypeOf(anakin) // возвращает false
Классы ES6
Неуклюже по сравнению с использованием объектов, но читабельность кода нормальная:
class Person { конструктор (имя, фамилия, год рождения, тип) { this.firstName = имя this.lastName = фамилия this.birthYear = год рождения этот.тип = тип } name () { вернуть это.firstName + ' ' + this.lastName } greet() { console.log('Привет, меня зовут ' + this.name() + ' и я ' + this.type + '.' ) } } класс Скайуокер расширяет Person { конструктор (имя, год рождения) { супер(имя, 'Скайуокер', год рождения, 'человек') } } const anakin = новый Скайуокер('Энакин', '442 ДБЯ') // проверка наследования цепочки прототипов частично исправлена.Person.isPrototypeOf(anakin) // возвращает ложь! Skywalker.isPrototypeOf(anakin) // возвращает true
Возможность записи, настройки и бесплатные геттеры и сеттеры!
Для бесплатных геттеров и сеттеров или дополнительной настройки вы можете использовать второй аргумент Object.create(), также известный как propertiesObject. Он также доступен в #Object.defineProperty и #Object.defineProperties.
Чтобы проиллюстрировать его полезность, предположим, что мы хотим, чтобы все роботы
были строго сделаны из металла (через записываемый: false
) и стандартизировали значения powerConsumment
(через геттеры и сеттеры).
// Добавляем интерфейс для Typescript, опускаем для Javascript интерфейс Робот расширяет Person { madeOf: 'металл' потребляемая мощность: строка } // добавить `: Robot` для TypeScript, опустить для Javascript. const Robot: Robot = Object.create(Person, { // определяем атрибуты вашего свойства сделано из: { значение: "металл", доступный для записи: false, // по умолчанию false.это назначение является избыточным и предназначено только для многословия. настраиваемый: false, // по умолчанию false. это назначение является избыточным и предназначено только для многословия. enumerable: true // по умолчанию false }, // геттеры и сеттеры потребляемая мощность: { получить () {возвратить this._powerConsumment}, установить (значение) { if (value.indexOf('MWh')) вернуть this._powerConsumption = value.replace('M', ',000k') this._powerConsumment = значение выдать новую ошибку («Формат энергопотребления не распознан».) } } }) // добавить `: Robot` для TypeScript, опустить для Javascript. const newRobot: Robot = Object.create(робот) newRobot.powerConsumment = '5 МВтч' console.log(newRobot.powerConsumment) // выдает 5000 кВтч
И все прототипы Робот
не могут быть сделаныИз
что-то другое:
const полимерныйРобот = Object. create(Robot) полимерRobot.madeOf = 'полимер' console.log(polymerRobot.madeOf) // выводит «металл»
«Супер» и «Расширения» в JavaScript ES6 — понимание сложных частей | Анураг Маджумдар | Руководство для начинающих по мобильной веб-разработке
Фото Clément H на UnsplashES6 сделал JavaScript намного проще с 9Синтаксис 0009 класса и его дополнительные возможности. Сегодня мы собираемся объединить функцию синтаксиса класса с концепцией наследования , чтобы получить некоторый код. Да, вы правильно догадались, мы рассмотрим ключевые слова super и extends в JavaScript ES6 . Лучший способ изучить новую функцию — погрузиться в нее на примере. Итак, давайте сделаем это!
Если мы хотим расширить класс в JavaScript, мы можем воспользоваться помощью ключевых слов super и расширяет для этого. Давайте рассмотрим пример использования этих ключевых слов.
Синтаксис классов и подклассов ES6Приведенный выше код содержит 2 класса JavaScript , а именно Animal и Gorilla.
Класс Gorilla является подклассом или дочерним классом класса Animal , и он использует ключевое слово extends , чтобы установить себя как подкласс .
Однако 9Ключевое слово 0009 super использовалось двумя разными способами.
Ребята, вы заметили то же самое? В конструкторе Gorilla (строка 23 кода)
super используется как «функция» . Принимая во внимание, что методы Gorilla showVigour() (строка 35) и dailyRoutine() (строка 39) использовали super в качестве «объекта» .
Ключевое слово super используется двумя способами по следующим причинам:
В строке 23 ключевое слово super используется как «функция» , которая вызывает родительский класс Animal с параметрами, переданными в Gorilla . Это ключевой шаг, который необходимо выполнить, чтобы убедиться, что Gorilla является экземпляром Animal.
В строках с номерами 35 и 39 super используется как «объект» , который относится к экземпляру Animal (родительский класс). Ключевое слово super здесь используется для вызова методов родительского класса Животное явно.
Люди, знакомые с такими языками, как C#, Java, Python, могут понять, как все это работает. Однако JavaScript не был таким простым до появления ES6 , особенно для классов. Так как же люди кодировали без использования синтаксиса class , super и extends ключевых слов? Или они никогда раньше не использовали такие понятия и вдруг решили их добавить? Давай выясним!
Правда в том, что объектно-ориентированный JavaScript существовал и использовался прототип наследования для расширения классов. Давайте посмотрим на тот же самый пример, но с традиционным синтаксисом JavaScript . Возможно, это поможет нам найти скрытую правду.
Объектно-ориентированный JavaScript до ES6Посмотрев на код, вы, ребята, должны подумать, подождите секунду, где слово class ? Где конструктор ? Как вообще возможно использовать наследование в старом коде JavaScript без расширения и супер ключевых слов? Не выглядит ли этот код уродливым?
Да, я знаю, что вы чувствуете, ребята, мы на одной волне. К сожалению, основные функции JavaScript никогда не менялись. Они всегда оставались неизменными, независимо от того, какие функции добавлялись в язык. Использование новых ключевых слов, таких как класс , конструктор , супер , расширяет просто добавляет синтаксическую окраску в код, чтобы сделать его читабельным и удобным для разработчиков.
Позвольте мне объяснить, какие строки кода из 9Пример 0009 ES6 соответствует традиционному примеру JavaScript .
Если вы плохо знакомы с концепцией прототипа и наследования в JavaScript, ознакомьтесь со следующими статьями, прежде чем переходить к разделу сравнения:
Прототипы в JavaScript
Читать на Github
Наследование в JavaScript
Читать на Github
hackernoon.com
Обе эти ссылки помогут вам хорошо понять следующий раздел.
В следующих разделах анализируется и сравнивается код, написанный в ES6 и традиционном стиле JavaScript .
Объявление класса
Объявления классов сравниваются в следующем фрагменте кода.
Сравнение объявлений классовОбъявление классов в ES6 напрямую использует ключевое слово класса , за которым следует определение переменных экземпляра внутри конструктор. В традиционном JavaScript нет такого понятия, как класса . На самом деле класс на самом деле является скрытой функцией в JavaScript (см. строку 11 этого фрагмента).
Функция конструктора в строке 3 точно такая же, как и в строке 14.
Функция Animal на самом деле является здесь функцией конструктора .
Методы как часть класса
Сравнение объявлений методовСтроки кода с 4 по 14 — это методы, существующие в Animal класс для стиля ES6 . Однако традиционно это было невозможно, поскольку не существовало такого понятия, как класс , где методы можно было бы так легко объявить. В традиционном JavaScript добавление методов к прототипу делает методы доступными для класса. Строки с 19 по 29 — это методы для традиционных классов JavaScript .
Отображение расширяется до традиционного JavaScript
Большая разница возникает, когда мы пытаемся расширить родительский класс с подклассом . Обратитесь к следующему фрагменту кода:
Сопоставление расширяет ключевое слово с традиционным подходом. Мы видим, что ключевое слово extends заботится о расширении родительского класса Animal до подкласса способом ES6 , но ключевое слово super равно также используется здесь, чтобы убедиться, что класс Animal вызывается через конструктор Gorilla , чтобы наследовать характеристики и поведение Животное . Здесь ключевое слово super используется как функция для вызова класса Animal для инициализации Gorilla . Здесь super эквивалентно Animal.call(это, …).
Чтобы сделать то же самое традиционным способом, необходимо выполнить несколько дополнительных действий. Функция для подкласса Gorilla должна быть создана в соответствии со строкой 10. Поскольку Gorilla наследует характеристики и поведение Animal необходимо вызвать функцию конструктора Animal внутри конструктора Gorilla , как показано в строке 11, эта строка сравнима со строкой 4 и делает то же самое. Только нам нужно передать «эту» ссылку явно классу Animal , чтобы убедиться, что вызов был сделан из класса Gorilla .
Кроме того, нам нужно установить прототип функции Gorilla как новый объект, созданный из Животное — прототип , как показано в строке 11. При этом мы переопределяем объект Gorilla прототип . Следовательно, в следующей строке 15 нам нужно явно указать конструктор Gorilla . Эти шаги предназначены для установки класса Gorilla в качестве подкласса класса Animal .
Сопоставление super с традиционным JavaScript
Мы уже видели одно сопоставление ключевого слова super , то есть строки 4 и 19.в следующем фрагменте кода super используется как функция .
Сопоставление ключевого слова super с традиционным подходомКлючевое слово super также может использоваться в качестве экземпляра родительского класса согласно строкам 8 и 12 для вызова конкретных сведений о классе Animal .
Для достижения того же в традиционном стиле строки 26 и 30 показывают, как это делается.
Экземпляр super на самом деле ParentClassName.prototype.methodName.call(this, …) .
Следовательно, необходимо написать много кода, чтобы обеспечить явный вызов методов родительского класса.
Я почти уверен, что вы, ребята, начнете использовать функции ES6 класса и наследования сразу же, не моргнув глазом, поскольку теперь вы знаете уровень сложности, который обеспечивает традиционный способ.