— Расширение объекта в 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 и расширяет для этого. Давайте рассмотрим пример использования этих ключевых слов.
Приведенный выше код содержит 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 . Возможно, это поможет нам найти скрытую правду.
Посмотрев на код, вы, ребята, должны подумать, подождите секунду, где слово 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 класса и наследования сразу же, не моргнув глазом, поскольку теперь вы знаете уровень сложности, который обеспечивает традиционный способ.


prototype.greet = function() { ... }
Person.prototype.age = function() { ... }
функция Скайуокер (имя, год рождения) {
Person.apply(это, [имя, 'Скайуокер', год рождения, 'человек'])
}
// запутанное повторное указание...
Скайуокер.прототип = Человек.прототип
Skywalker.prototype.constructor = Скайуокер
const anakin = новый Скайуокер('Энакин', '442 ДБЯ')
// #isPrototypeOf не будет работать
Person.isPrototypeOf(anakin) // возвращает ложь
Skywalker.isPrototypeOf(anakin) // возвращает false


create(Robot)
полимерRobot.madeOf = 'полимер'
console.log(polymerRobot.madeOf) // выводит «металл»