对象的概念
在ECMAScript中,对象的被定义为:无序属性的集合,其属性可以包含基本值、对象或者函数。
简单的说对象就是由无序名值对组成的散列表,其中值可以是数据或者函数。
创建对象方法
在JavaScript中创建对象有多种方法:
- Object构造函数
- 对象字面量
- 工厂模式
- 构造函数模式
- 原型模式
- 组合构造函数和原型模式
- 动态原型模式
- 寄生构造函数模式
- 稳妥构造函数模式
(1)Object 构造函数
var person=new Object();
person.name="John";
person.age=20;
......
person.Hi=function(){
alert("Welcome"+person.name);
}
上述例子中创建一个名为person的对象并添加了两个属性(name,age)和一个方法(Hi())。
(2)对象字面量
var person={
name:"John";
age:20;
Hi:function(){
alert(this.name);
}
}
虽然上述创建对象方法比较简单,但是确定也相当明显:创建多个对象时产生大量重复代码。为了提高效率,提高代码复用,人们开始使用工厂模式的变体———
(3)工厂模式
由于在ES中无法创建类,开发人员使用一种函数,用来封装创建类的细节:
function person(name,age){
var o=new Object();
o.name=name;
o.age=age;
0.Hi=function(){
alert("Welcome "+o.name);
};
return o;
}
var someone=person("John",20);
someone.Hi(); //"Welcome John"
在每次需要创建person对象时只要传入参数就可以得到不同的对象实例,但是这种方法却无法知道一个对象的类型。
(4)构造函数模式
这种方法是创建一个类似于Object和Array的构造函数,我们把Object()和Array()叫做原生构造函数,同样我们也可以自定义构造函数,从而自定义属性和方法。
function person(name,age){
this.name=name;
this.age=age;
this.Hi=function(){
alert("Welcome "+this.name);
}
}
var someone=new person("John",20);
someone.Hi(); //"welcome John"
在上述例子中,someone是Object类型的对象实例,也是person类型的对象实例。使用这种方法创建的对象也是存在问题的:构造函数中的方法在每个对象实例上都要重新创建一遍,也就是说,用这种方法创建的对象,会导致不同的作用域链和标识符解析
alert(person1.Hi==person2.Hi); //"false"
最直观的解决办法就是把函数定义移动到构造函数外面来解决这个问题。
function person(name,age){
this.name=name;
this.age=age;
this.Hi=Hi;
}
function Hi(){
alert("Welcome "+this.name);
}
(5)原型模式
我们创建的每个函数都有一个prototype(原型),这个属性是一个指向对象的指针,这个对象包含特定类型的所有属性和方法。使用这个属性,就不必再构造函数中定义对象实例的信息,而是可以将这些信息直接添加到原型对象中。
function person(){}
person.prototype.name="John";
person.prototype.age=20;
person.prototype.Hi=function(){
alert("Welcome"+this.name);
}
var person1=new person();
var person2=new person();
alert(person1.Hi == person2.Hi); //true
原型对象的缺点:
(1)忽略构造函数参数传递,导致所有实例默认属性值相同。
(2)由于属性被多个实例共享,当某个实例引用类型属性值被修改时,会导致原型中属性值被修改,导致所有实例属性值被修改。
(6)组合使用构造函数和原型模式
组合方法就是属性值采用构造函数,方法采用原型模式,这样可以同时解决实例的默认属性值相同的情况,也不会导致一个实例属性值修改导致所有实例属性值被修改。
function person(name,age){
this.name=name;
this.age=age;
this.friend=["a","b"];
}
person.prototype={
constructor:person;
Hi:function(){
alert("Welcome "+this.name);
}
}
这种由构造函数和原型模式混合,是目前ES中使用对广泛,认同最高的创建自定义类型的方法,可以说是定义引用类型的默认方法。
(7)动态原型模式
在这种方法中,我们首先通过检查某个方法是否有效,来决定是否需要初始化原型。
function person(name,age){
this.name=name;
this.age=age;
//方法
if(typeof this.Hi != "function")
{
person.prototype.Hi=function(){
alert("Welcome "+this.name);
}
}
}
上述方法的代码只有在第一次使用构造函数的时候才会调用,此后创建的对象实例都不需要做修改,可以在 if 语句中检查需要检验的属性或者方法。
(8)寄生构造函数模式
这种方法一般在前面的方法都不太好用的情况下才会采取的方法,这种方法的基本思想是创建一个函数封装代码,再返回新创建的对象,从表面上看像是一个典型的构造函数。
我们想要修改一个数组,但是却不能直接修改Array构造函数,因此可以使用这个模式:
function special(){
var value=new Array();
value.push.apply(value,arguments);
value.toPipedString = function(){
return this.join("|");
}
return value;
}
var color=new spcial("red","blue","green");
alert(color.toPipedString()); //"red|blue|green"
这种方法中返回的对象和构造函数的原型属性没有任何关系,也不能用instanceof确定对象类型,所以一般情况下不建议使用。
(9)稳妥构造函数模式
所谓的稳妥对象是指:没有公共属性,方法中也不引用this对象,适合在安全环境中使用。
function person(name,age){
var o=new Object();
//在这添加需要的属性和函数
o.Hi=function(){
alert("Welcome "+name);
}
}
在稳妥对象中,除了已经定义的函数,没有其他任何办法可以给这个对象添加方法或者数据成员,也不能访问到对象内部原始数据,具有较高的安全性。
虽然列出而定方法比较多,但是其实后面的几种方法只是前面几种方法的组合或者扩展,提出这些方法只是为了解决单独使用某种方法时无法解决的一些缺点或者麻烦。
文章内容整理自《JavaScript高级程序设计》