Dunia tanpa kata kunci "baru".
Dan sintaksis "prosa-seperti" yang lebih sederhana dengan Object.create ().
* Contoh ini diperbarui untuk kelas ES6.
Pertama, ingatlah bahwa Javascript adalah bahasa prototipe . Ini bukan berbasis kelas. Oleh karena itu, menulis dalam bentuk prototipe memperlihatkan sifat aslinya, dan bisa sangat sederhana, seperti prosa, dan kuat.
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'
Tidak, Anda tidak perlu konstruktor, tidak ada new
Instansiasi ( baca mengapa Anda tidak boleh menggunakannew
), tidak super
, tidak lucu lucu __construct
. Anda cukup membuat Objek dan kemudian memperluas atau mengubah mereka.
( Jika Anda tahu tentang getter dan setter, lihat bagian "Bacaan lebih lanjut" untuk melihat bagaimana pola ini memberi Anda getter dan setter gratis dengan cara yang awalnya dimaksudkan untuk Javascript , dan seberapa kuat mereka .)
Sintaks yang mirip prosa: Protoype dasar
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!
Sekilas, terlihat sangat enak dibaca.
Extension, menciptakan keturunan Person
* Istilah yang benar adalah prototypes
, dan mereka descendants
. Tidak ada classes
, dan tidak perlu 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
Salah satu cara untuk menyediakan cara "default" untuk menciptakan descendant
, adalah dengan melampirkan #create
metode:
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')
Cara di bawah ini memiliki keterbacaan yang lebih rendah:
Bandingkan dengan padanan "klasik":
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!
Keterbacaan kode menggunakan gaya "klasik" tidak begitu bagus.
Kelas ES6
Memang, beberapa masalah ini diberantas oleh kelas ES6, tetapi masih:
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
Percabangan prototipe dasar
// create a `Robot` prototype by extending the `Person` prototype:
const Robot = Object.create(Person)
Robot.type = 'robot'
Robot.variant = '' // add properties for Robot prototype
Lampirkan metode yang unik untuk 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
Memeriksa warisan
Person.isPrototypeOf(Robot) // outputs true
Robot.isPrototypeOf(Skywalker) // outputs false
Anda sudah mendapatkan semua yang Anda butuhkan! Tidak ada konstruktor, tidak ada Instansiasi. Prosa bersih, jelas.
Bacaan lebih lanjut
Ketelitian, Dapat Dikonfigurasi, dan Gratis Setter dan Setter!
Untuk getter dan setter gratis, atau konfigurasi tambahan, Anda dapat menggunakan argumen kedua Object.create () alias propertiesObject. Ini juga tersedia di # Object.defineProperty , dan # Object.defineProperties .
Untuk mengilustrasikan betapa kuatnya ini, misalkan kita ingin semua Robot
terbuat dari logam (via writable: false
), dan menstandarkan powerConsumption
nilai-nilai (via getter dan 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
Dan semua prototipe Robot
tidak bisa menjadi madeOf
sesuatu yang lain karena writable: false
.
const polymerRobot = Object.create(Robot)
polymerRobot.madeOf = 'polymer'
console.log(polymerRobot.madeOf) // outputs 'metal'
Mixins (menggunakan # Object.assign) - Anakin Skywalker
Bisakah Anda merasakan ke mana arahnya ...?
const darthVader = Object.create(anakin)
// for brevity, property assignments are skipped because you get the point by now.
Object.assign(darthVader, Robot)
Darth Vader mendapatkan metode Robot
:
darthVader.greet() // inherited from `Person`, outputs "Hi, my name is Darth Vader..."
darthVader.machineGreet() // inherited from `Robot`, outputs 001010011010...
Bersama dengan hal-hal aneh lainnya:
console.log(darthVader.type) // outputs robot.
Robot.isPrototypeOf(darthVader) // returns false.
Person.isPrototypeOf(darthVader) // returns true.
Ya, apakah Darth Vader adalah manusia atau mesin memang subjektif:
"Dia lebih mesin sekarang daripada manusia, bengkok dan jahat." - Obi-Wan Kenobi
"Aku tahu ada kebaikan di dalam dirimu." - Luke Skywalker
Extra - Sintaks sedikit lebih pendek dengan # Object.assign
Kemungkinan besar, pola ini mempersingkat sintaksis Anda. Tetapi ES6 # Object.assign dapat mempersingkat lebih banyak (Untuk polyfill untuk digunakan pada browser lama, lihat MDN pada 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.
})