一、基本的创建对象方法
1、new一个object的实例,然后再添加属性和方法
这样创建的属性和方法都会暴露出来
var cat = new Object();
cat.color = "brown";
cat.say = function() {
alert(this.color)
}
2、对象字面量
var cat = {
color: "brown",
say: function() {
alert(this.color)
}
};
采用这两种方式,如果想创建‘dog‘时,又得重新写一遍,非常不便
二、使用工厂模式创建对象
以函数方式来封装创建对象的细节,解决了创建多个相似对象的问题(也就相似与其他语言“类“的概念)
function createPet(kind, color) {
var o = new Object();
o.kind = kind
o.color = color;
o.say = function() {
alert('There is a ' + this.color + ' ' + this.kind);
};
return o;
}
var cat = createPet("cat", "yellow");
var dog = createPet("dog", "black");
cat.say();
dog.say();
但是没有解决对象识别问题。可以注意到,它是在函数内部创建了一个对象o,然后将其返回给了相应的cat,dog(即两者都是Object)。如果全是这么做,那就有很多的Object对象了
三、构造函数模式
function Pet(kind, color) {
this.kind = kind;
this.color = color;
this.say = function() {
alert('There is a ' + this.color + ' ' + this.kind);
};
}
var cat = new Pet("cat", "yellow");
var dog = new Pet("dog", "black");
这种方式类似于其他语言的类的构造方法,使用this关键字,通过new创建一个新的对象;cat,dog就有了明确的归属了,即“Pet“,当然它们也属于Object。
alert(cat instanceof Object); //true
alert(cat instanceof Pet); //true
同时他们有了一个consturctor属性
alert(cat.constructor == Pet); //true
然而,构造函数也有它的问题:
alert(cat.say == dog.say) //false
发现这个“false“的存在,ECMAScript中因为函数即对象,所以定义函数都相当于实例化了一个对象,占用了空间。当然可以像下面这样:
function Pet(kind, color) {
this.kind = kind;
this.color = color;
this.say = say;
}
function say() {
alert('There is a ' + this.color + ' ' + this.kind);
}
var cat = new Pet("cat", "yellow");
var dog = new Pet("dog", "black");
这样做可以解决以上的问题,但是当添加的方法多了,封装性就得不到保证了。
四、原型模式
创建的每一个函数都有一个prototype属性,即原型属性。这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以有特定类型的所有实例共享的属性和方法。意思是:prototype就是通过调用构造函数而创建的那个对象实例的原型对象。可以让所有对象实例共享它所包含的属性和方法
function Pet() {
}
//绑定到函数的原型上
Pet.prototype.kind = "cat";
Pet.prototype.color = "yellow";
Pet.prototype.say = function() {
alert('There is a ' + this.color + ' ' + this.kind);
};
var pet1 = new Pet();
pet1.say(); //"There is a yellow cat"
var pet2 = new Pet();
pet2.say(); //There is a yellow cat
alert(pet1.say == pet2.say) //true
这样解决了构造函数的弊端,但是不能通过对象实例重写原型中的值
此外,当包含有引用类型值的属性时,会带来不必要的麻烦。
function Pet(){}
Pet.prototype = {
constructor: Pet, //重新指向Pet对象
tag: "pet",
kind: ["cat", "dog"],
say: function() {
alert(this.tag);
}
};//等于字面量形式创建了新对象
var pet1 = new Pet();
var pet2 = new Pet();
pet1.kind.push("rabbit");
alert(pet1.kind); //"cat,dog,rabbit"
alert(pet2.kind); //"cat,dog,rabbit"
alert(pet1.kind == pet2.kind); //true
实际生活中不同需求的宠物店pet1与pet2,在pet1中加入兔子,组成了“猫,狗,兔子“的组合,可是无意中让pet2中也加入兔子,而pet2地区没人要兔子,这造成了浪费。
五、组合使用构造函数和原型模式
function Pet(kind, color) {
this.kind = kind;
this.colors = ["black", "yellow"];
}
Pet.prototype = {
constructor: Pet,
say: function() {
alert("I'm a " + this.kind);
}
};
var cat = new Pet("cat");
var dog = new Pet("dog");
cat.colors.push("brown");
alert(cat.colors); //"black,yellow,brown"
alert(dog.colors); //"black,yellow"
alert(cat.colors == dog.colors); //false
alert(cat.say == dog.say); //true
所有实例共享的属性和方法在原型中定义,实例属性在构造函数中定义。
参考《javascript高级程序设计》所做整理与笔记