一、如何创建对象
1、new关键字
var obj = new Object()
2、字面量
var obj = {}
二、工厂模式
上面创建对象的方式缺点:使用一个接口创建很多对象,就会产生大量重复代码,于是就有了工厂模式。
function createPerson(name, age, job) {var o = new Object()o.name = nameo.age = ageo.job = jobo.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 = namethis.age = agethis.job = jobthis.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 = namethis.age = agethis.job = jobthis.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 = 21Person.prototype.job = '当兵'Person.prototype.sayName = function(){console.log(this.name)}var person1 = new Person()person1.sayName() // lisivar person2 = new Person()person2.sayName() // lisiconsole.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 = namethis.age = agethis.job = job}Person.prototype = {constructor: Person,sayName: function(){console.log(this.name)}}
优点:构造函数用于定义实例属性,原型模式用于定义方法和共享属性。
缺点:其他OO语言开发人员看到这样写法会有些困惑
六、动态原型模式
function Person(name, age, job){this.name = namethis.age = agethis.job = jobif(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语言开发人员看到这样写法会有些困惑 | 不能使用对象字面量重写原型 | 返回的对象与构造函数或者与构造函数的原型属性之间没有关系。 | 只能通过对象中的方法,才能访问对象中的属性 |
| 过渡 | 将方法放在全局作用域,这样做不能很好的起到封装的作用 | ||||||
| 备注 |
