面向对象都有类的概念,而通过类可以创建多个具有相同属性和方法的对象。对于Java来说,就是一门彻底面向对象的语言,有着官方的类封装。
但在ES6之前,JavaScript是没有官方提供的类库。
下面我们来创建一个对象
let animal = {
name: "animal",
// 注意不能使用箭头函数,它的this指向的window
// eat: () => console.log(this.name + " is eating"),
// sleep: () => console.log(this.name + " is sleeping")
eat: function() {
console.log(this.name + " is eating!")
},
sleep: function() {
console.log(this.name + " is sleeping!")
}
}
animal.eat()
这就是一个简单的“类”。
但对于这个对象,里面的属性都是暴露,会带给我们很多麻烦。这时可以通过ES5的Objecet.defineProperty()方法来属性的特性,详情请阅读JavaScript高级程序设计第三版P139。
没有类怎么实现继承?
let cat = {
name: "cat",
__proto__: animal
}
let rabbit = {
name: "rabbit",
__proto__: animal
}
cat.sleep()
rabbit.eat()
是通过原型链继承,_proto_(两个下划线)是实例指向构造对象的原型(别忽略原型两个字哦)。所以sleep()和eat()会从animal中继承过来。
有人问:每次这样构造实例好像很麻烦啊?有什么简单的方法?
那我就来简单介绍两个设计模式(大家可以阅读JavaScript高级程序设计第三版P144).
工厂模式
function createPerson(name, age, job) {
let o = new Object()
o.name = name
o.job = job
o.age = age
o.sayName = function() {
console.log(this.name)
}
// 别忘了返回
return o
}
let jack = createPerson('jack', 21, 'designer')
这个模式可以方便地创建大量对象,但存在一个 问题没法解决对象识别,就是怎样分辨是哪一个构造函数的实例?
那就有请我们下一个主角登场
构造函数模式
function Person(name, age, job) {
this.name = name
this.age = age
this.job = job
this.sayName = function() {
console.log(this.name)
}
}
let jack = new Person('jack', 21, 'designer')
let amy = new Person('amy', 23, 'dancer')
这应该是平常使用最多的设计模式(构造函数名首字母默认大写)吧。注意:没有return,创建实例时使用new操作符。
现在我们可以检测对象类型了
console.log(jack instanceof Person) // true
我们可以使用instanceof来检查实例是否来自构造函数。
那构造函数模式是否完美了?当然不会。请看下面代码
console.log(jack.sayName == amy.sayName)
我们可以看到,每个方法都在每个实例重新创建一遍,就是说不同实例上的同名函数是不相等的。这就有点费内存了。
组合使用原型模式和构造函数模式
function Person(name, age, job) {
this.name = name
this.age = age
this.job = job
}
Person.prototype = {
sayName: function() {
console.log(this.name)
}
}
let jack = new Person('jack', 21, 'designer')
let amy = new Person('amy', 23, 'dancer')
console.log(jack.sayName == amy.sayName) // true
在构造函数原型中定义我们公有属性和方法,这就是组合使用原型模式和构造函数模式。
题外话
还有很多设计模式,就目前自身的知识水平来说,还不足以说明白这些内容。留待下回分解。下一篇文章内容关于ES6的原生类class。