Welt ohne das "neue" Schlüsselwort.
Und einfachere "prosaähnliche" Syntax mit Object.create ().
* Dieses Beispiel wurde für ES6-Klassen aktualisiert.
Denken Sie zunächst daran, dass Javascript eine prototypische Sprache ist . Es ist nicht klassenbasiert. Daher enthüllt das Schreiben in prototypischer Form seine wahre Natur und kann sehr einfach, prosaartig und kraftvoll sein.
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'
Nein, Sie brauchen keine Konstruktoren, keine new
Instanziierung ( lesen Sie, warum Sie sie nicht verwenden solltennew
), nein super
, kein lustiger Witz __construct
. Sie erstellen einfach Objekte und erweitern oder verwandeln sie dann.
( Wenn Sie sich mit Gettern und Setzern auskennen, lesen Sie den Abschnitt "Weiterführende Literatur", um zu erfahren, wie Sie mit diesem Muster kostenlose Getter und Setter erhalten, wie Javascript es ursprünglich beabsichtigt hatte, und wie leistungsfähig sie sind .)
Prosaähnliche Syntax: Basisprototyp
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!
Sieht auf einen Blick gut lesbar aus.
Erweiterung, die einen Nachkommen von erstellt Person
* Die richtigen Begriffe sind prototypes
und ihre descendants
. Es gibt keine classes
und keine Notwendigkeit für 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
Eine Möglichkeit, eine "Standard" -Methode zum Erstellen eines zu erstellen descendant
, besteht darin, eine #create
Methode anzuhängen :
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')
Die folgenden Möglichkeiten haben eine geringere Lesbarkeit:
Vergleichen Sie mit dem "klassischen" Äquivalent:
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!
Die Lesbarkeit des Codes im "klassischen" Stil ist nicht so gut.
ES6-Klassen
Zugegeben, einige dieser Probleme werden von den ES6-Klassen beseitigt, aber dennoch:
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
Verzweigung des Basisprototyps
// create a `Robot` prototype by extending the `Person` prototype:
const Robot = Object.create(Person)
Robot.type = 'robot'
Robot.variant = '' // add properties for Robot prototype
Fügen Sie Methoden hinzu, die für 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
Vererbung überprüfen
Person.isPrototypeOf(Robot) // outputs true
Robot.isPrototypeOf(Skywalker) // outputs false
Sie haben schon alles, was Sie brauchen! Keine Konstruktoren, keine Instanziierung. Saubere, klare Prosa.
Weiterführende Literatur
Beschreibbarkeit, Konfigurierbarkeit und kostenlose Getter und Setter!
Für kostenlose Getter und Setter oder zusätzliche Konfigurationen können Sie das zweite Argument von Object.create () verwenden, auch bekannt als propertiesObject. Es ist auch in # Object.defineProperty und # Object.defineProperties verfügbar .
Um zu veranschaulichen, wie mächtig dies ist, möchten wir, dass alle Robot
streng aus Metall (via writable: false
) bestehen und powerConsumption
Werte standardisieren (via Getter und Setter).
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
Und alle Prototypen von Robot
können nicht madeOf
etwas anderes sein, weil writable: false
.
const polymerRobot = Object.create(Robot)
polymerRobot.madeOf = 'polymer'
console.log(polymerRobot.madeOf) // outputs 'metal'
Mixins (mit # Object.assign) - Anakin Skywalker
Kannst du spüren, wohin das führt ...?
const darthVader = Object.create(anakin)
// for brevity, property assignments are skipped because you get the point by now.
Object.assign(darthVader, Robot)
Darth Vader erhält die Methoden von Robot
:
darthVader.greet() // inherited from `Person`, outputs "Hi, my name is Darth Vader..."
darthVader.machineGreet() // inherited from `Robot`, outputs 001010011010...
Zusammen mit anderen seltsamen Dingen:
console.log(darthVader.type) // outputs robot.
Robot.isPrototypeOf(darthVader) // returns false.
Person.isPrototypeOf(darthVader) // returns true.
Nun, ob Darth Vader Mensch oder Maschine ist, ist in der Tat subjektiv:
"Er ist jetzt mehr Maschine als Mensch, verdreht und böse." - Obi Wan Kenobi
"Ich weiß, dass es gut in dir ist." - Luke Skywalker
Extra - Etwas kürzere Syntax mit # Object.assign
Höchstwahrscheinlich verkürzt dieses Muster Ihre Syntax. ES6 # Object.assign kann sich jedoch noch etwas verkürzen (Informationen zur Verwendung von Polyfill in älteren Browsern finden Sie unter MDN in 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.
})