虽然Object构造函数或对象字符量都可以用来创建单个对象,但这些方式有个明显的缺点:使用同一个接口创建很多对象,会产生大量重复代码。人们开始使用工厂模式的一种变体。
一、工厂模式
function createPerson(name) {
var o = new Object();
o.name = name;
o.say = function() {
alert(this.name);
}
return o;
}
var person1 = createPerson("zx");
缺点:
工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。
二、构造函数模式
function Person(name) {
this.name = name;
this.ability = function(){
alert("speaking")
};
this.say = function() {
alert(this.name)
}
}
var person1 = new Person("zx");
var person2 = new Person("harden");
优点
1.通过constructor或者instanceof可以识别对象实例的类别
在上面的例子中,person1和person2都有一个constructor(构造函数)属性,该属性指向Person
alert(person1.constructor == Person); //true
alert(person2.constructor == Person); //true
对象的constructor属性最初是来表示对象类型的。但是,提到检测对象类型,还是instanceof更加可靠一些
alert(person1 instanceof Object); //true
alert(person1 instanceof Person); //true
2.可通过new关键字来创建对象实例
function Person(name) {
this.name = name;
this.say = function() {
alert(this.name)
};
this.ability = function(){
alert("speaking")
}
}
var person1 = new Person("zx");//更像oo语言中创建对象实例
var person2 = new Person("zr");
缺点
相同的方法重复创建
多个实例的ability方法都是实现一样的效果,但是却存储了很多次(两个对象实例的ability方法是不同的,因为存放的地址不同)
打印这两个对象的结果,以及这两个对象的函数比较结果
这便是构造函数模式的缺点,表面上好像没有没什么问题,但是实际这样做有个很大的弊端。那就是对于每一个实例对象,ability方法都是一模一样的内容,每次生成一个实例,都必须为重复的内容,多占用了一些内存,这样浪费内存,也缺乏效率。
三、原型模式
Javascript规定,每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。
这意味着,我们可以把那些不变的属性和方法,直接定义在prototype对象上。
function Cat(name,color){
this.name = name;
this.color = color;
}
Cat.prototype.type = "猫科动物";
Cat.prototype.eat = function(){alert("吃老鼠")};
然后生成实例
var cat1 = new Cat("大毛","黄色");
var cat2 = new Cat("二毛","黑色");
alert(cat1.type); // 猫科动物
cat1.eat(); // 吃老鼠
这时所有实例的type属性和eat()方法,其实都是同一个内存地址,指向prototype对象,因此就提高了运行效率。
alert(cat1.eat == cat2.eat); //true
proto
- 每个class都有显示原型prototype
- 每个实例都有隐式原型proto
- 实例的proto指向对应class的prototype
这里如果觉得很抽象的话,不妨直接输出cat1,看看这个实例里面到底有些什么
function Cat(name, color) {
this.name = name;
this.color = color;
}
Cat.prototype.type = "猫科动物";
Cat.prototype.eat = function () { alert("吃老鼠") };
var cat1 = new Cat("大毛","黄色");
console.log(cat1);
在cat1内部有个proto的隐藏属性,不难发现该属性里存放的东西和Cat prototype里的很相似,于是可以试着比较一下看是否真的相等。
console.log(cat1.__proto__==Cat.prototype); //true
发现返回的结果是true,于是可以断定,cat1内的proto属性指向的就是Cat的原型对象!于是可以大概画出如下的结构图。
由图可知,每个实例化的cat内的proto都会指向该类的原型对象,并且这个实例化对象享有原型对象上所有的属性和方法。
而在原型对象内部,有个constructor属性是指向原构造函数,proto属性指向他父类(也就是Object)的原型对象。