工厂模式
工厂模式是一种使用较为广泛的常见对象的模式,使用工厂模式封装了拆关键创建对象的方法,当需要创建多个类似类似项目的时候,工厂模式可以大大的提高效率,如下代码所示:
function createObj(name, age, job) {
const o = new Object()
o.name = name
o.age = age
o.job = job
o.func = function () {
console.log(this.name)
}
return o
}
const c1 = createObj('张三', '20', '程序员')
c1.func() // 张三
构造函数模式
众所周知,在Object和Array中的原生构造函数会在运行过程中自动出现在执行环境当中;也可以通过自定义构造函数来自定义对象的属性和方法;如下代码所示:
function Person (name, age, job) {
this.name = name
this.age = age
this.job = job
this.func = function () {
console.log(this.name)
}
const person1 = new Person('张三', '20', '打工人')
const person2 = new Person('李四', '21', '老板')
如上代码所示,与工厂模式相比,自定义构造函数的写法与工厂模式的区别在于:
1、构造函数内部没有重新创建一个对象;
2、构造函数直接将属性和方法赋值给了this对象;
3、构造函数不需要使用return;
如上面代码所示创建构造函数的实例必须使用new操作符,使用这种方法创建实例的流程:
1、创建一个新对象;
2、将构造函数的作用域赋给新对象;
3、执行构造函数中的代码,给新对象添加属性和方法;
4、返回新对象
console.log(person1.constructor === Person) // true
console.log(person2.constructor === Person) // true
console.log(person1 instanceof Person) // true
console.log(person2 instanceof Person) // true
如上代码可以看出,Person构造函数的两个实例的构造函数均指向了Person构造函数,并且我们可以使用构造函数为自己的实例标识为自定义的类型;
原型模式
承接上面的构造函数,观察下面代码:
console.log(person1.func === person2.func) // false
上面代码展示了使用构造函数时每个实例中所包含的func函数是不相同的,这样我们就相当与同样功能的函数在每个实例中都会被创建一次,这样显然是不必要的。
每个函数被创建的同时都会有一个指向一个对象的属性prototype,通过这个对象包含可以被所有实例共享的属性和方法,prototype可以被称作是函数的原型对象。
function Proto () {
}
Proto.prototype.name = '关云长'
Proto.prototype.age = '20'
Proto.prototype.func = function () {
console.log(this.name)
}
const p1 = new Proto()
const p2 = new Proto()
console.log(p1.func === p2.func) // true
console.log(p1.name, p2.name,p1.age,p2.age) // 关云长 关云长 20 20
如上面代码所示,通过所有实例可以共享原型对象中的属性和方法,并且每个实例访问的都是同一组属性和方法。
组合使用构造函数模式和原型模式
构造函数用于自定义实例属性,原型对象可用于定义实例共享的属性和方法,在某些场景下需要让每个实例都具备各自独有的属性的副本和相同功能的方法,这个时候就需要结合构造函数模式和原型模式各自的特点来进行设计:
function Persons (name, age) {
this.name = name
this.age = age
this.ps = ['赵云', '马超']
}
Persons.prototype = {
constructor: Persons,
func: function () {
console.log(this.name, this.age)
}
}
const p1 = new Persons('关羽', '39')
const p2 = new Persons('刘备', '43')
p1.ps.push('诸葛亮')
console.log(p1.name, p2.name) // 关羽 刘备
console.log(p1.ps, p2.ps) // ['赵云', '马超', '诸葛亮'] ['赵云', '马超']
p1.func() // 关羽 39
p2.func() // 刘备 43
由上面代码所示,p1 和 p2两个实例的name属性是不相同的,但是p1和p2的func函数的功能是相同的,都打印出了name和age属性;值得注意的一点是,此混合模式下在p1和p2的ps进行操作的时候是互相不干扰的;
动态原型模式
通过动态原型模式,将原型对象的初始化封装在一个构造函数中,这样可以通过条件判断进行有选择性的对原型对象的属性或者方法进行初始化:
function Obj (name, type) {
this.name = name
if (typeof this.func !== 'function') {
Obj.prototype.func = function () {
console.log(this.name)
}
}
}
const o1 = new Obj('诸葛亮', 0)
const o2 = new Obj('周瑜', 1)
o1.func() // 诸葛亮
o2.func() // 周瑜
由上面的代码所示,在构造函数中判断func是否已经初始化为函数了,如果没有就初始化Obj原型对象的func函数,注意,当在Obj中初始化以此函数之后,会立即在其他实例中生效,如上代码的o1和o2实例都会输出name属性;
寄生构造函数模式
寄生构造函数模式的写法类似与工厂模式,我个人认为这个模式是工厂模式的扩展,当我们需要对一些数组类型添加自定义方法的时候,我们无法直接直接修改Array的构造函数,此时就可已使用寄生构造函数模式:
function CreatObj () {
const arrs = new Array()
arrs.push.apply(arrs, arguments)
arrs.joinToStr = function () {
return this.join('/')
}
}
const o1 = new CreateObj('蔡文姬', '张飞', '孙悟空')
console.log(o1.joinToStr()) // 蔡文姬/张飞/孙悟空
以上代码展示了我们可以通过寄生构造函数模式为函数创建自定义构造函数。