Open
Description
面向对象
《JavaScript高级程序设计》笔记
创建对象的方式
1. 工厂模式
function createPerson(name, age, job) {
var o = new Object()
o.name = name
o.age = age
o.job = job
o.sayName = function() {
alert(this.name)
}
return o
}
var person = createPerson("faker", 18, "LOL")
缺点:对象无法识别,方法不能共享
2. 构造函数模式
function Person(name, age, job) {
this.name = name
this.age = age
this.job = job
this.sayName = function() {
alert(this.name)
}
}
var person = new Person("faker", 18, "LOL")
缺点:还是方法共享问题
2.1 构造函数模式优化
function Person(name, age, job) {
this.name = name
this.age = age
this.job = job
this.sayName = sayName
}
function sayName() {
alert(this.name)
}
var person = new Person("faker", 18, "LOL")
缺点:方法共享了,但谈不上封装,每增加一个方法就得在外面再定义一个函数
3. 原型模式
function Person() {}
Person.prototype = {
constructor: Person, // 重写原型对象会切断原型与构造函数之间的联系,我们应该纠正它
name: "faker",
age: 18,
job: "LOL",
sayName() {
alert(this.name)
},
}
var person = new Person()
缺点:实例只有共享属性和方法,没有自身的属性,这还不是它最大的问题,最大的问题是它共享本质所导致的;例如,在原型中存在一个引用类型的数组,那么所有实例都会共享该数组,通过实例操作数组会在所有实例中反映出来。
4. 组合模式
function Person(name, age, job) {
this.name = name
this.age = age
this.job = job
}
Person.prototype = {
constructor: Person,
sayName() {
alert(this.name)
},
}
var person = new Person("faker", 18, "LOL")
集构造函数和原型模式二者之长,这是JavaScript中用的最常用的一种模式
5. 动态原型模式
function Person(name, age, job) {
this.name = name
this.age = age
this.job = job
if (typeof this.sayName != "function") {
Person.prototype.sayName = function() {
alert(this.name)
}
}
}
var person = new Person("faker", 18, "LOL")
具有更好的封装性
6. 寄生构造函数模式
function Person(name, age, job) {
var o = new Object()
o.name = name
o.age = age
o.job =job
o.sayNmae = function() {
alert(this.name)
}
return o
}
var person = new Person("faker", 18, "LOL")
不太了解有什么用
7. 稳妥构造函数模式
function Person(name) {
var o = new Object()
o.sayName = function() {
alert(name)
}
return o
}
var person = Person("faker")
在某些安全环境中会用到这种模式,因为只有sayName
方法可以访问到name,它有两个特点:
- 不使用this
- 不使用new创建对象
仔细看看,其实这是闭包的一种应用
继承的方式
1. 原型链
function SuperType() {
this.name = "faker"
}
SuperType.prototype.getName = function() {
return this.name
}
function SubType() {
}
// 继承了SuperType
SubType.prototype = new SuperType()
SubType.prototype.constructor = SubType
var instance = new SubType()
alert(instance.getName()) // faker
缺点:
- 包含引用类型的值会被所有实例共享(原型不就是用来共享的?)
- 子类原型成为了父类的一个实例,父类实例中的属性也变成了原型中的属性(我们想要的是一个纯净的原型)
2. 借用构造函数
function SuperType(name){
this.name = name
}
function SubType(name){
SuperType.call(this, name)
}
var instance = new SubType("faker")
alert(instance.name) // faker
缺点:仅使用借用构造函数无法避免构造函数存在的共享问题
3. 组合继承
function SuperType(name) {
this.name = name
}
SuperType.prototype.getName= function() {
return this.name
}
function SubType(name) {
// 继承了属性
SuperType.call(this, name)
}
// 继承了方法
SubType.prototype = new SuperType()
SubType.prototype.constructor = SubType
var instance = new SubType("faker")
alert(instance.getName()) // faker
缺点:虽然集原型链和构造函数二者之长,但有两个问题:
- 调用了两次父构造函数
- 实例的name会屏蔽掉SubType原型中的name,其实原型中的name的存在是没有意义的
4. 原型式继承
function object(o) {
function F() {}
F.ptototype = o
return new F()
}
var o = {
name: "faker"
}
var obj = object(o)
alert(obj.name) // faker
// 更好的方式
var obj1 = Object.create(o)
alert(obj1.name) // faker
这是道格拉斯提出的一种,以现有对象为原型来创建新对象的方式,ES5中用Object.create()
规范了这种方式
5. 寄生式继承
function createAnother(o) {
var clone = Object.create(o)
clone.sayHi = function() { // 增强对象
alert("Hi!")
}
return clone
}
顾名思义,这是一种寄生在原型式继承之上的一种模式
6. 寄生组合式继承
function inheritPrototype(subType, superType) {
var prototype = Object.create(superType.prototype)
prototype.constructor = subType
subType.prototype = prototype
}
function SuperType(name) {
this.name = name
}
SuperType.prototype.getName = function() {
return this.name
}
function SubType(name, age) {
SuperType.call(this, name)
this.age = age
}
inheritPrototype(SubType, SuperType)
var instance = new SubType("faker", 18)
alert(instance.getName()) // faker
寄生组合式继承是寄生式继承和借用构造函数的组合,相比组合继承,它只调用了一次父构造函数,因此在子类的原型上也没有了多余的属性。这是目前最理想的继承范式。