Функция

— Расширение объекта в 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 на Unsplash

ES6 сделал 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 класса и наследования сразу же, не моргнув глазом, поскольку теперь вы знаете уровень сложности, который обеспечивает традиционный способ.