目录
描述
创建对象
工厂模式
构造函数模式
原型模式
构造函数模式+原型模式
寄生构造模式
稳妥构造函数模式
描述
面向对象的语言有一个标志,就是都有类的概念,而通过类可以创建很多具有相同属性和方法的对象。
对象可以定义为无序属性的集合,其属性可以包含基本值,对象或者函数
创建对象
创建自定义对象的最简单的方式就是创建一个Object的实例。
let cat = new Object()
cat.name = 'Tom'
cat.age = 2
cat.eat = function(){
alert('吃鱼')
}
//当然用对象字面量表示法更方便
let cat = {
name: 'Tom',
age: 2,
eat: function() {
alert('吃鱼')
}
}
但是这样会产生大量重复的代码
工厂模式
在es5中,没有类的概念,于是提出了用函数来封装以特定接口创建对象的细节:
function cat(name, age, color){
let cat = new Object()
cat.name = name;
cat.age = age;
cat.color = '黄色'
cat.eat = function() {
alert('吃肉')
}
return cat
}
let cat1 = new Cat('Tom', 2, '黄色')
let cat2 = new Cat('jack', 1, '棕色')
这样就能够一个函数创建多个实例了,就相当于Cat这个工厂,专门用来生产cat实例,只需要给工厂传递想要的参数即可。
在es6中 提出了class的概念 ,class可以看做是一个语法糖,新的class
写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已
class Cat {
constructor(age, name, color='白色') { //默认白色
this.age = age;
this.name = name;
this.color = color;
}
eat() {
alert(`${this.name}爱吃鱼`)
}
}
let cat1 = new Cat(12, 'tom', 'yellow')
let cat2 = new Cat(12,, 'jack')
简单工厂模式的缺点也很突出:没有解决对象识别的问题(即怎样知道一个对象的类型)
构造函数模式
构造函数与工厂模式主要有几个不同:
1.没有显示的创建对象
2.直接将属性和方法赋值给this对象
3.没有return语句
4.函数大写开头,主要是为了区别其他函数,构造函数也是函数,只不过能用来创建对象罢了。
function Cat(name, age, color){
this.name = name;
this.age = age;
this.color = '黄色'
this.eat = function() {
alert(`${this.name}爱吃鱼`)
}
}
let cat1 = new Cat('Tom', 2, '黄色')
let cat2 = new Cat('jack', 1, '棕色')
缺点:构造函数中每个方法都要在实例上重新创建一遍,在上面例子中,虽然eat方法都可以弹出信息,但是其实cat1.eat和cat2.eat这两个方法指向地址是不同的
console.log(cat1.eat === cat2.eat) //false
我们可以在外部创建一个函数来解决共享问题
function Cat(name, age, color){
this.name = name;
this.age = age;
this.color = '黄色'
this.eat = eat
}
function eat() {
alert(`${this.name}爱吃鱼`)
}
let cat1 = new Cat('Tom', 2, '黄色')
let cat2 = new Cat('jack', 1, '棕色')
这样一来eat就是指向eat函数地址的一个指针,就可以共享eat方法了。
可是新问题又来了,这样eat方法只能被某个对象调用,让全局作用域名不副实啊,关键是,如果对象需要很多方法,那再全局作用域中岂不全是全局函数?这样我们自定义的引用类型就完全没有封装性可言了,好在可以用原型模式来解决
原型模式
我们创建的每一个函数都有一个prototyp属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法
function Cat(){
}
Cat.prototype.name = 'tom'
Cat.prototype.age = 12
Cat.prototype.eat = function() {
alert(`${this.name}爱吃鱼`)
}
let cat1 = new Cat('Tom', 2, '黄色')
let cat2 = new Cat('jack', 1, '棕色')
//或者,注意这样其实是重写整个原型对象,也就是将prototype指向一个新对象
,如果是在重写原型对象之前创建实例,那么原来的实例指向的还是未重写之前的原型对象。
Cat.prototype = {
name: 'tom',
age: 12,
eat: fuction() {
alert(`${this.name}爱吃鱼`)
}
}
缺点:首先是忽略了为构造函数传递参数,其次在原型中所有属性和方法都是共享的,但是每个实例都应该有各自的属性,比如cat1名字为tom,cat2名字为jack,而不是每个实例都是tom.
构造函数模式+原型模式
function Cat(name, age){
this.name = name;
this.age = age;
}
Cat.prototype.eat = function() {
alert(`${this.name}爱吃鱼`)
}
let cat1 = new Cat('Tom', 2, '黄色')
let cat2 = new Cat('jack', 1, '棕色')
//或者,注意这样其实是重写整个原型对象,也就是将prototype指向一个新对象
,如果是在重写原型对象之前创建实例,那么原来的实例指向的还是未重写之前的原型对象。
Cat.prototype = {
name: 'tom',
age: 12,
eat: fuction() {
alert(`${this.name}爱吃鱼`)
}
}
寄生构造模式
可以为对象创建构造函数,例:用来创建一个具有额外方法的数组。由于不能直接修改Array构造函数,可以用该模式
function SpecialArray() {
let arr = new Array()
arr.push.apply(arr,arguments)
arr.toSpecialString = function() {
return this.join('|')
}
return arr
}
let special = new SpecialArray(1,2,3,4)
special.toSpecialString() //1|2|3|4
稳妥构造函数模式
稳妥构造函数模式使用稳妥对象,稳妥对象指没有公共属性,而且其方法不引用this的对象,适合用在安全的环境中(这些环境禁止使用this和new)。
稳妥构造函数模式和寄生构造函数模式有两点不同:1.新创建的实例方法不引用this;2.不使用new操作符调用构造函数。
function Cat(name, age) {
let cat = new Cart()
cat.name = name;
cat.age = age
cat.eat = function() {
alert(`${name}爱吃鱼`)
}
return cat
}
let cat = Cat('tom', 12)
cat.eat() //tom爱吃鱼
参考文章:《javascript高级程序设计》