Світ без "нового" ключового слова.
І простіший "прозовий" синтаксис з Object.create ().
* Цей приклад оновлений для класів ES6.
Спочатку пам’ятайте, що Javascript - це прототипна мова . Це не на основі класу. Отже, написання у прототипіальній формі розкриває його справжню природу і може бути дуже простим, прозовим і потужним.
TLDR;
const Person = { name: 'Anonymous' } // person has a name
const jack = Object.create(Person) // jack is a person
jack.name = 'Jack' // and has a name 'Jack'
Ні, вам не потрібні конструктори, жодна new
інстанція ( читайте, чому ви не повинні використовуватиnew
), ні super
, не смішно смішно __construct
. Ви просто створюєте Об'єкти, а потім розширюєте чи перетворюєте їх.
( Якщо ви знаєте про геттери та сетери, перегляньте розділ "Подальше читання", щоб побачити, як ця схема дає вам безкоштовні геттери та сетери таким чином, якими раніше був призначений Javascript , і наскільки вони потужні .)
Прозовий синтаксис: базовий прототип
const Person = {
//attributes
firstName : 'Anonymous',
lastName: 'Anonymous',
birthYear : 0,
type : 'human',
//methods
name() { return this.firstName + ' ' + this.lastName },
greet() {
console.log('Hi, my name is ' + this.name() + ' and I am a ' + this.type + '.' )
},
age() {
// age is a function of birth time.
}
}
const person = Object.create(Person). // that's it!
На перший погляд, виглядає дуже читабельно.
Розширення, створення нащадка Person
* Правильні умови є prototypes
, і їхні descendants
. Тут немає classes
і не потрібно instances
.
const Skywalker = Object.create(Person)
Skywalker.lastName = 'Skywalker'
const anakin = Object.create(Skywalker)
anakin.firstName = 'Anakin'
anakin.birthYear = '442 BBY'
anakin.gender = 'male' // you can attach new properties.
anakin.greet() // 'Hi, my name is Anakin Skywalker and I am a human.'
Person.isPrototypeOf(Skywalker) // outputs true
Person.isPrototypeOf(anakin) // outputs true
Skywalker.isPrototypeOf(anakin) // outputs true
Один із способів створення "за замовчуванням" способу створення "" - descendant
це додавання #create
методу:
Skywalker.create = function(firstName, gender, birthYear) {
let skywalker = Object.create(Skywalker)
Object.assign(skywalker, {
firstName,
birthYear,
gender,
lastName: 'Skywalker',
type: 'human'
})
return skywalker
}
const anakin = Skywalker.create('Anakin', 'male', '442 BBY')
Нижче наведені способи нижчої читабельності:
Порівняйте з "класичним" еквівалентом:
function Person (firstName, lastName, birthYear, type) {
this.firstName = firstName
this.lastName = lastName
this.birthYear = birthYear
this.type = type
}
// attaching methods
Person.prototype.name = function() { return firstName + ' ' + lastName }
Person.prototype.greet = function() { ... }
Person.prototype.age = function() { ... }
function Skywalker(firstName, birthYear) {
Person.apply(this, [firstName, 'Skywalker', birthYear, 'human'])
}
// confusing re-pointing...
Skywalker.prototype = Person.prototype
Skywalker.prototype.constructor = Skywalker
const anakin = new Skywalker('Anakin', '442 BBY')
Person.isPrototypeOf(anakin) // returns false!
Skywalker.isPrototypeOf(anakin) // returns false!
Читання коду з використанням "класичного" стилю не так добре.
Класи ES6
Щоправда, деякі з цих проблем усуваються класами ES6, але все ж:
class Person {
constructor(firstName, lastName, birthYear, type) {
this.firstName = firstName
this.lastName = lastName
this.birthYear = birthYear
this.type = type
}
name() { return this.firstName + ' ' + this.lastName }
greet() { console.log('Hi, my name is ' + this.name() + ' and I am a ' + this.type + '.' ) }
}
class Skywalker extends Person {
constructor(firstName, birthYear) {
super(firstName, 'Skywalker', birthYear, 'human')
}
}
const anakin = new Skywalker('Anakin', '442 BBY')
// prototype chain inheritance checking is partially fixed.
Person.isPrototypeOf(anakin) // returns false!
Skywalker.isPrototypeOf(anakin) // returns true
Розгалуження базового прототипу
// create a `Robot` prototype by extending the `Person` prototype:
const Robot = Object.create(Person)
Robot.type = 'robot'
Robot.variant = '' // add properties for Robot prototype
Додайте унікальні методи Robot
// Robots speak in binaries, so we need a different greet function:
Robot.machineGreet = function() { /*some function to convert strings to binary */ }
// morphing the `Robot` object doesn't affect `Person` prototypes
anakin.greet() // 'Hi, my name is Anakin Skywalker and I am a human.'
anakin.machineGreet() // error
Перевірка спадщини
Person.isPrototypeOf(Robot) // outputs true
Robot.isPrototypeOf(Skywalker) // outputs false
У вас вже є все необхідне! Ніяких конструкторів, жодної інстанції. Чиста прозора проза.
Подальше читання
Чутливість, конфігурація та безкоштовні розпорядники та налаштування!
Для безкоштовних геттерів та сетерів або додаткової конфігурації ви можете скористатися другим аргументом Object.create (), який називається PropertiesObject. Він також доступний у # Object.defineProperty та # Object.defineProperties .
Для того, щоб проілюструвати, наскільки це потужно, припустимо, що ми хочемо, щоб усе Robot
було суворо виготовлено з металу (через writable: false
), і стандартизувати powerConsumption
значення (за допомогою геттерів та сетерів).
const Robot = Object.create(Person, {
// define your property attributes
madeOf: {
value: "metal",
writable: false,
configurable: false,
enumerable: true
},
// getters and setters, how javascript had (naturally) intended.
powerConsumption: {
get() { return this._powerConsumption },
set(value) {
if (value.indexOf('MWh')) return this._powerConsumption = value.replace('M', ',000k')
this._powerConsumption = value
throw new Error('Power consumption format not recognised.')
}
}
})
const newRobot = Object.create(Robot)
newRobot.powerConsumption = '5MWh'
console.log(newRobot.powerConsumption) // outputs 5,000kWh
І всі прототипи Robot
не можуть бути madeOf
чимось іншим, тому що writable: false
.
const polymerRobot = Object.create(Robot)
polymerRobot.madeOf = 'polymer'
console.log(polymerRobot.madeOf) // outputs 'metal'
Мікінс (за допомогою # Object.assign) - Анакін Скайуокер
Ви можете відчути, куди це йде ...?
const darthVader = Object.create(anakin)
// for brevity, property assignments are skipped because you get the point by now.
Object.assign(darthVader, Robot)
Дарт Вейдер отримує методи Robot
:
darthVader.greet() // inherited from `Person`, outputs "Hi, my name is Darth Vader..."
darthVader.machineGreet() // inherited from `Robot`, outputs 001010011010...
Поряд з іншими дивними речами:
console.log(darthVader.type) // outputs robot.
Robot.isPrototypeOf(darthVader) // returns false.
Person.isPrototypeOf(darthVader) // returns true.
Що ж, чи Дарт Вейдер людина чи машина - це справді суб'єктивно:
"Він зараз більш машинний, ніж людина, скручений і злий". - Оби-Ван Кенобі
"Я знаю, що в тебе добре". - Люк Скайуокер
Додатково - трохи коротший синтаксис з # Object.assign
Ймовірно, цей візерунок скорочує ваш синтаксис. Але ES6 # Object.assign може скоротити ще трохи (Для використання полів заповнення у старих браузерах див. MDN на ES6 ).
//instead of this
const Robot = Object.create(Person)
Robot.name = "Robot"
Robot.madeOf = "metal"
//you can do this
const Robot = Object.create(Person)
Object.assign(Robot, {
name: "Robot",
madeOf: "metal"
// for brevity, you can imagine a long list will save more code.
})