javascript--创建对象
面向对象(Object-Oriented,OO)的语言有一个特点,那就是它们都有类的概念。而javascript中没有类的概念,因此它的对象也与基于类的语言中的对象有所不同。
ECMA-262把对象定义为:“无序属性的集合,其属性可以包含基本值、对象或函数”,我们可以把对象想象成散列集:无非就是一组名值对,其中值可以是数据或函数。下面介绍几种创建对象的按方法。
一、Object构造函数或对象字面量
var person={ name:"Bob", age:"24", sayName:function(){ alert(this.name); } }
这种方法有明显缺陷:会产生大量重复代码。
二、工厂模式
考虑到ECMAscript中无法创建类,开发人员想到用函数封装以特定接口创建对象的细节。
function createPerson(name,age){ return { name:name, age:age, sayName:function(){ alert(this.name); } } } var person1=createPerson("Bob","24");
工厂模式虽然定义接口创建对象,单却没有解决对象识别的问题(即怎样知道一个对象的类型)。
三、构造函数模式
大家都知道在javascript中用构造函数可创建特定类型的对象,如:Object、Array。此外,也可以创建自定义的构造函数,从而定义自定义对象类型的属性和方法。
function Person(name,age){ this.name=name; this.age=age; this.sayName=function(){ alert(this.name); } } var person1=new Person("Bob","24"); var person2=new Person("Rose","27");
创建Person构造函数的新实例,必须使用new操作符。调用构造函数会经历以下几个步骤:
- 创建一个新对象;
- 将构造函数的作用域赋给新对象--this就指向新对象;
- 执行构造函数中的代码;
- 返回新对象;
每个对象都有个constructor(构造函数)属性:
alert(person1.constructor==Person); //true alert(person1 instanceof Person); //true alert(person1 instanceof Object); //true
构造函数模式虽然好用,但也并非没有缺点。每个实例中的方法都是重新创建的,以构造函数模式穿件实例会导致不同的作用域链和标识符解析。因此,不同实例上的同名函数是不相等的。
alert(person1.sayName==person2.sayName) //false
四、原型模式
每一个创建的函数都有一个prototype属性,指向一个对象,而这个对象的用途是包含了其所有实例共享的属性和方法。因此,我们可以把那些不变的属性和方法直接定义在prototype对象上。
function Person(name,age){ Person.prototype.name=name; Person.prototype.age=age; Person.prototype.sayName=function(){ alert(this.name); } }
var person1=new Person("Bob","24"); var person2=new Person("Rose","27"); person1.sayName(); //Rose person2.sayName(); //Rose alert(person1.sayName==person2.sayName) //true
有原型模式创建的实例的属性和方法都指向prototype对象。且原型的属性、方法都是动态的,我们对原型对象所做的任何修改都能从实例上体现出来,所以person1.sayName()输出Rose。但是,实例一般都是要有属于自己的全部属性的,这也是很少有人单独使用原型模式的原因。
五、组合使用构造函数模式和原型模式
由上可知,构造函数模式可以定义每个实例特有的属性和方法,原型模式定义所有实例共享的动态属性和方法。混成模式可谓是集两家之所长!
function Person(name,age){ this.name=name; this.age=age; this.sayName=function(){ alert(this.name); } } Person.prototype={ job:"teacher", age:"30", sayAge:function(){ alert(this.age); } }
var person1=new Person("Bob","24"); var person2=new Person("Rose","27"); person1.sayAge(); //24 person2.sayAge(); //37 alert(person1.sayName==person2.sayName); //false alert(person1.sayAge==person2.sayAge); //true
在这个例子中,我们可以把共享的属性和方法,定义在prototype
对象上;特有的属性、方法定义在实例对象上。但是上例中,我们在实例和原型中都定义了age属性,实例结果显示实例属性。若我们将实例中的age属性去掉:
function Person(name,age){ this.name=name; this.sayName=function(){ alert(this.name); } } Person.prototype={ job:"teacher", age:"30", sayAge:function(){ alert(this.age); } }
var person1=new Person("Bob","24"); var person2=new Person("Rose","27"); person1.sayAge(); //30 person2.sayAge(); //30
实例结果都返回原型中的属性值。这是由于当代码读取某个对象的某个属性时,搜索首先从对象实例开始,若实例中存在则返回该值且屏蔽掉原型中的同名属性;若没找到,则继续在原型对象中搜索。
构造函数和原型的混成模式,是目前用来创建自定义类型的一种默认模式!