没有“ new”关键字的世界。
使用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
。您只需创建对象,然后对其进行扩展或变形。
(如果您了解getter和setter,请参阅“更多阅读”部分,以了解此模式如何以Javascript 最初打算的方式为您提供免费的getter和setter ,以及它们的功能强大。)
类似于散文的语法:基本原型
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
您已经拥有了所需的一切!没有构造函数,没有实例化。干净,清晰的散文。
进一步阅读
可写性,可配置性和免费的Getter和Setter!
对于免费的getter和setter或其他配置,可以使用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'
Mixins(使用#Object.assign)-阿纳金·天行者
你能感觉到这要去哪里吗?
const darthVader = Object.create(anakin)
// for brevity, property assignments are skipped because you get the point by now.
Object.assign(darthVader, Robot)
Darth Vader获得以下方法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.
好吧,达斯·维达(Darth Vader)是人还是机器确实是主观的:
“他现在比人更是机器,扭曲而邪恶。” -Obi-Wan Kenobi
“我知道你有好处。” - 卢克·天行者
额外-#Object.assign的语法略短
这种模式很可能会缩短语法。但是ES6#Object.assign可以缩短一些时间(要在旧版浏览器上使用polyfill,请参阅ES6上的MDN)。
//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.
})