JavaScript创造对象
创建对象的方式有很多,下面来具体介绍:
1、 使用Object来创建一个对象。
创建一个Object实例,为它添加属性和方法。
//利用Object创建实例
var person = new Object();
person.name = "xiaoming";
person.sayName = function(){
alert(this.name);
};
person.sayName(); //xiaoming
2、 使用字面量创建对象。
//利用字面量创建对象
var person = {
name : "xiaoming",
sayName:function(){
alert(this.name);
}
};
person.sayName(); //xiaoming
虽然用Object和字面量都可以用来创建对象,但是有明显的缺点:使用同一个借口创建很多对象,会产生大量的重复代码。为解决这一问题,人们开始使用工厂模式的一种变体。
3、 用工厂模式创建对象
工厂模式抽象了创造具体对象的过程,用函数来封装以特定接口创建函数的细节。返回带有属性和方法的对象。
//利用工厂模式创建对象
function createPerson(name,age){
var o = new Object();
o.name = name;
o.age =age;
o.sayName = function(){
alert(this.name);
};
return o;
}
var person1 = createPerson("xiaoming",20);
var person2 = createPerson("xiaohong",19);
person1.sayName(); //xiaoming
person2.sayName(); //xiaohong
工厂模型通过多次条用函数,并返回属性和方法的对象,解决了创建多个对象时会产生重复代码的问题,但是它没能解决对象识别的问题,即怎样知道一个对象的类型问题。这时出现了构造函数模式。
4、 构造函数模式
使用自定义构造函数模式创建对象。
//利用构造函数模型创建对象
function Person(name,age){
this.name = name;
this.age = age;
this.sayName = function(){
alert(this.name);
};
}
var person1 = new Person("xiaoming",20);
var person2 = new Person("xiaohong",19);
person1.sayName(); //xiaoming
person2.sayName(); //xiaohong
alert(person1.constructor == Person); //true
alert(person2.constructor == Person); //true
alert(person1 instanceof Object); //true
alert(person1 instanceof Person); //true
alert(person2 instanceof Object); //true
alert(person2 instanceof Person); //true
注意:函数名应用大写字母开头。
要创建Person 的新实例,必须使用new 操作符。以这种方式调用构造函数实际上会经历以下4个步骤:
(1) 创建一个新对象;
(2) 将构造函数的作用域赋给新对象(因此this 就指向了这个新对象);
(3) 执行构造函数中的代码(为这个新对象添加属性);
(4) 返回新对象。
创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型;而这正是构造函数模式胜过工厂模式的地方。我们可以用对象的constructor 属性来标注随想的类型,也可以用instanceof来检测对象的类型,instanceof操作符要更可靠一些。
构造函数虽然好用,但是每个方法都要在每个实例上运行一遍,,以这种方式构造函数,会导致不同的作用链和标识符解析,不同实例上的同名函数是不相等的。若把函数定义在构造函数外部来解决,代码如下:
function Person(name, age){
this.name = name;
this.age = age;
this.sayName = sayName;
}
function sayName(){
alert(this.name);
}
虽然解决了两个函数做同一件事的问题,但是,在全局作用域中定义的函数实际上只能被某个对象调用,使得全局变量有点名不副实,并且,如果对象定义很多方法,这将要定义很多全局变量,自定义的引用类型就不存在封装性了。为了解决这一问题,我们开始考虑原型模式。
5、 原型模式
我们创造的每一个函数都有一个prototype(原型)属性,这属性是一个指针指向一个对象,而这个对象的用途是包含可以有特定类型的所有实例共享的属性和方法。好处:可以让所有实例共享它所有的属性和方法。
//构造函数
function Person(){
}
Person.prototype.name = "xiaoming";
Person.prototype.age = 20;
Person.prototype.sayName = function (){
alert(this.name);
};
var person1 = new Person();
var person2 = new Person();
person1.sayName();//xiaoming
person2.sayName();//xiaoming
alert(person1.sayName == person2.sayName);//true
虽然在所有实现中都无法访问到[[Prototype]],但可以通过isPrototypeOf()方法来确定对象之间是否存在这种关系。当读取代码的某个属性时,会从代码本身开始搜索,在实例中找到属性,并返回属性;如没有找到,则会向上搜索,指向原型对象,在原型对象中查找该属性,若找到该属性,返回属性值。
原型模式构造对象,虽然可以通过对象访问保存在原型中的值,但不能通过对象重写原型中的值。可以在实例中添加同名属性来屏蔽原型属性,通过delete删除。
但是,原型模式也有不足之处,第一,省略构造函数初始化,是的所有实例具有相同的属性值;第二,也是重要的一点,原型中所有属性被很多实例共享,这样,包含应用类型值的属性来说就会存在问题。
具体问题如下:
//字面量写原型模式创建对象
function Person(){
}
Person.prototype = {
constructor: Person,
name : "xiaoming",
age : 20,
friends : ["xiaohong", "xiaogang"],
sayName : function () {
alert(this.name);
}
};
var person1 = new Person();
var person2 = new Person();
person1.friends.push("xiaofang");
alert(person1.friends); //xiaohong,xiaogang,xiaofang
alert(person2.friends); //xiaohong,xiaogang,xiaofang
alert(person1.friends === person2.friends); //true
实例间会互相影响,这时,我们考虑组合使用构造函数模式和原型模式的方法。
6、 组合使用构造函数模式和原型模式
将构造函数模式和原型模式结合起来,利用构造函数模式来定义实例属性,原型模式来定义方法和共享属性,这样,每个实例都会有自己的一份实例属性副本,同时有共享这对方的引用,最大限度的节省了内存。另外,它还支持向构造函数传递参数,结合了两种模式的长处。
//组合使用构造函数模式和原型模式创建对象
function Person(name,age){
this.name =name;
this.age = age;
this.friends =["xiaohong", "xiaogang"];
}
Person.prototype = {
constructor: Person,
sayName : function () {
alert(this.name);
}
};
var person1 = new Person("xiaoming",20);
var person2 = new Person("xiaoliu",19);
person1.friends.push("xiaofang");
alert(person1.friends); //xiaohong,xiaogang,xiaofang
alert(person2.friends); //xiaohong,xiaogang
alert(person1.friends === person2.friends); //false
alert(person1.sayName === person2.sayName); //true
这种模式是最常见的一种创建对象模式。
7、 动态原型模式
function Person(name, age, job){
//属性
this.name = name;
this.age = age;
this.job = job;
//方法
if (typeof this.sayName != "function"){
Person.prototype.sayName = function(){
alert(this.name);
};
}
}
var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName();
这里只在sayName()方法不存在的情况下,才会将它添加到原型中。进行动态添加。
8、 寄生构造函数模式
这种模式的基本思想是创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象;但从表面上看,这个函数又很像是典型的构造函数。
function Person(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
};
return o;
}
var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName(); //"Nicholas"
该模式不能依赖instanceof 操作符来确定对象类型,因此不建议使用。
9、 稳妥构造函数模式
所谓稳妥对象,指的是没有公共属性,而且其方法也不引用this 的对象。稳妥对象最适合在一些安全的环境中(这些环境中会禁止使用this 和new),或者在防止数据被其他应用程序(如Mashup程序)改动时使用。稳妥构造函数遵循与寄生构造函数类似的模式,但有两点不同:一是新创建对象的实例方法不引用this;二是不使用new 操作。
function Person(name, age, job){
//创建要返回的对象
var o = new Object();作符调用构造函数。
//可以在这里定义私有变量和函数
//添加方法
o.sayName = function(){
alert(name);
};
//返回对象
return o;
}
与寄生构造函数模式类似,使用稳妥构造函数模式创建的对象与构造函数之间也没有什么关系,因此instanceof 操作符对这种对象也没有意义。