一、如何创建对象
1、new关键字
var obj = new Object()
2、字面量
var obj = {}
二、工厂模式
上面创建对象的方式缺点:使用一个接口创建很多对象,就会产生大量重复代码,于是就有了工厂模式。
function createPerson(name, age, job) {
var o = new Object()
o.name = name
o.age = age
o.job = job
o.sayName = function () {
console.log(this.name)
}
return o
}
var person1 = createPerson('zhangsan', 18, '教师')
var person2 = createPerson('lisi', 20, '司机')
优点:能够根据接受参数不同来构建一个包含所有信息的Person对象,可以无数次的调用,返回一个方法对象。
缺点:返回的对象只能包含已经定义好的属性;没有解决对象识别问题(不知道一个对象的类型)。
三、构造函数模式
function Person(name, age, job) {
this.name = name
this.age = age
this.job = job
this.sayName = function(){
console.log(this.name)
}
}
var person1 = new Person('wangwu', 22, '工程师')
var person2 = new Person('zhangsan', 25, '测试师')
和工厂模式的不同之处:
1、没有显示的创建对象
2、直接将属性和方法赋值给this对象
3、没有return语句
优点:可以用constructor来标识对象类型
缺点:每个方法都要在每个实例上重新创建一遍。(也就是sayName要被多次创建,浪费内存)
过渡方法:可以将方法写在外面
function Person(name, age, job) {
this.name = name
this.age = age
this.job = job
this.sayName = sayName
}
function sayName(){
console.log(this.name)
}
var person1 = new Person('wangwu', 22, '工程师')
var person2 = new Person('zhangsan', 25, '测试师')
虽然可以解决函数重复创建,但是函数如果很多,全部放在全局作用域下,就起不到封装的作用。
四、原型模式
function Person(){}
Person.prototype.name = 'lisi'
Person.prototype.age = 21
Person.prototype.job = '当兵'
Person.prototype.sayName = function(){
console.log(this.name)
}
var person1 = new Person()
person1.sayName() // lisi
var person2 = new Person()
person2.sayName() // lisi
console.log(person1.sayName == person1.sayName) // true
与构造函数不同的是,新对象的这些属性和方法都是由所有实例共享的
优点:可以让所有实例共享它所包含的属性和方法。(不必在构造函数中定义对象实例的信息,而是可以将这些信息 直接添加到原型对象中)
缺点:由其共享的本性所导致的。省略了为构造函数传递参数。私有属性可能都会一起改变。
function Person(){}
Person.prototype = {
name: 'lisi',
age: 12,
job: '',
firends: ['1', '2'],
sayName: function(){
console.log(this.name)
}
}
var person1 = new Person()
var person2 = new Person()
person1.firends.push('3')
console.log(person1.firends) // ['1', '2', '3']
console.log(person2.firends) // ['1', '2', '3']
五、构造函数和原型组合模式
function Person(name, age, job){
this.name = name
this.age = age
this.job = job
}
Person.prototype = {
constructor: Person,
sayName: function(){
console.log(this.name)
}
}
优点:构造函数用于定义实例属性,原型模式用于定义方法和共享属性。
缺点:其他OO语言开发人员看到这样写法会有些困惑
六、动态原型模式
function Person(name, age, job){
this.name = name
this.age = age
this.job = job
if(typeof this.sayName != 'function') {
Person.prototype.sayName = function(){
console.log(this.name)
}
}
}
var p1 = new Person('zhangsan', 12, 'wuye')
p1.sayName()
优点:把所有信息都封装在构造函数中,通过构造函数初始化原型,判断原型中是否有sayName方法,如果没有救添加。
缺点:不能使用对象字面量重写原型。(因为如果在已经创建了实例的情况下重写原型,那么就会切断现有实例与新原型之间的联系)
七、寄生构造函数模式
如果前面几种模式都不适用,可以使用寄生构造函数模式
function SpecialArray(){
var values = new Array() // 创建数组
values.push.apply(values, arguments) // 添加值
values.toPipedString = function(){ // 添加方法
return this.join('|')
}
return values // 返回数组
}
var colors = new SpecialArray('red', 'blue', 'green')
console.log(colors.toPipedString()) // 'red|blue|green'
优点:可以创建额外方法
缺点:返回的对象与构造函数或者与构造函数的原型属性之间没有关系。
八、稳妥构造函数模式
所谓稳妥,指的是没有公共属性,而且其方法也不引用this的对象。
function Person(name, age, job) {
var obj = new Object()
// 可以在这里定义私有变量和函数
// 添加方法
obj.sayName = function(){
console.log(name)
}
return obj
}
// 除了sayName方法之外,没有其他途径访问其数据成员
优点:保护数据不被更改
缺点:只能通过对象中的方法,才能访问对象中的属性
九、总结
工厂模式 ——-> 构造函数模式 ——-> 原型模式 ——-> 构造函数和原型组合模式
工厂模式 | 构造函数模式 | 原型模式 | 构造函数和原型组合模式 | 动态原型模式 | 寄生构造函数模式 | 稳妥构造函数模式 | |
---|---|---|---|---|---|---|---|
优点 | 能够根据接受参数不同来构建一个包含所有信息的Person对象,可以无数次的调用,返回一个方法对象 | 可以用constructor来标识对象类型 | 可以让所有实例共享它所包含的属性和方法。(不必在构造函数中定义对象实例的信息,而是可以将这些信息 直接添加到原型对象中) | 构造函数用于定义实例属性,原型模式用于定义方法和共享属性。 | 把所有信息都封装在构造函数中,通过构造函数初始化原型,判断原型中是否有sayName方法,如果没有救添加。 | 可以创建额外方法 | 保护数据不被更改 |
缺点 | 返回的对象只能包含已经定义好的属性;没有解决对象识别问题(不知道一个对象的类型) | 每个方法都要在每个实例上重新创建一遍。(也就是sayName要被多次创建,浪费内存) | 由其共享的本性所导致的。省略了为构造函数传递参数。私有属性可能都会一起改变。 | 其他OO语言开发人员看到这样写法会有些困惑 | 不能使用对象字面量重写原型 | 返回的对象与构造函数或者与构造函数的原型属性之间没有关系。 | 只能通过对象中的方法,才能访问对象中的属性 |
过渡 | 将方法放在全局作用域,这样做不能很好的起到封装的作用 | ||||||
备注 |