日期:2014-05-16 浏览次数:20365 次
???? 在JS中,为了改进语言熟悉程度,也引入了“构造函数”这样的机制,但是在JS中构造函数本身也是函数,只是可以用来创建对象。在JS中创建对象,也需要用到new操作符,它的实际过程是这样的:
????
1:创建一个对象(这一过程完全在new操作符之前) 2:将构造函数的作用域赋给该对象(因此this也就指向了该对象) 3:执行构造函数(创建属性和方法) 4:返回新对象
?
??? 值得一提的是在Python的“构造方法”中,机制与这几乎一致(只是Python将这个方法移到类中,并称之为__init__方法)
?
????创建对象有以下几种方式,包括工厂模式、构造函数模式、原型模式等等。
??? 首先来看工厂模式:
???
function createPerson(person,age){ var o=new Object(); o.person=person; o.age=age; o.sayName=function(){ alert(o.person); } return o; } var ted=createPerson("ted",15); var marry=createPerson("mary",11); ted.sayName();//输出ted marry.sayName();//输出mary
??
?? 工厂模式的缺点就是创建了一个对象,但创建了两个引用。同样它并没有解决对象识别的问题,即怎么样知道一个对象的类型。
?? 再来看构造函数模式:
???
function Person(person,age){ this.person=person; this.age=age; this.sayName=function(){ alert(this.person); } } var ted=new Person("ted",15); var marry=new Person("marry",13); ted.sayName();//输出ted marry.sayName();//输出marry //按照惯例,这里构造函数首字母采用大写
??
? ?注意在这种模式下,这两个对象都具有一个构造函数属性,称为constructor(实质上是Person构造函数的原型对象的属性,稍后将介绍原型对象),例如:
??
alert(ted.constructor); alert(marry.constructor); //不出意外的话,会打印整个Person函数
?? 而在第一种方式下,尝试打印它们的constructor属性话,会直接打印object的构造函数。
?
?? 但是这样随之而来,也引出了一个问题,ted和marry都创建了一个具有完全功能的函数对象,但这两个函数对象却不是同一个。可以像下面这样稍微改造下:
??
function Person(person,age){ this.person=person; this.age=age; this.sayName=sayName; } function sayName(){ alert(this.person); }
?
?? 这样不用创建重复的函数对象了,但还是引来另外一个问题。如果这个对象拥有很多函数,那么这样一一声明全局函数,但这个全局函数实质上只能供Person使用,所以这丝毫没有体会到封装性的好处。
?? 是时候到重量级“人物”出场了--原型模式!
?? 先来了解下什么是原型。我们创建的每一个函数都有一个Prototype属性,这一个属性是一个对象,它包括一个constructor属性,这个属性又重新指向了拥有该Prototype属性的对象。是不是感觉晕掉了?来看看它们的图。
?? 假如我们这样创建了2个对象,其实际内部运行机制是什么样的?先看下下面代码:
??
function Person(){ } Person.prototype.name="ted"; Person.prototype.age=11; Person.prototype.sayName=function(){ alert(this.name); } var ted=new Person(); var marry=new Person(); ted.sayName();//输出ted marry.sayName();//输出ted
?
?? 它的基本运行流程是这样的:
???
???
?
?
?
?
?? 上面图是我自己手绘的,呵呵,请诸兄不要笑话。
?? 要检测对象和原型之间的关系的方法是:
??
alert(Person.prototype.isPrototypeOf(ted));//弹出true alert(Person.prototype.isPrototypeOf(marry));//弹出true
??
? 每当代码读取对象的一个属性的时候,都会执行一次搜索。目标是具有给定名字的属性。方法是:先查看对应实例里,是否有该名字,如果没有,则查看对应的原型对象里,是否有该属性。例如:
???
??
ted.name="haha"; alert(ted.name);//输出haha alert(marry.name);//输出ted
?
?? 从以上代码可以看出,的确实例属性遮盖了原型对象的属性。
?? 但是可以利用delete操作符删除这个实例属性,例如:
??
delete ted.name; alert(ted.name);//输出ted
?
??? 后来的Python,也借鉴了这种做法,不过Python采用的是del操作符,它们的思想都是类似的,都是让对象的引用次数减一。
??? 要验证一个属性是否是实例属性还是系统属性,请查看:
???
alert(ted.hasOwnProperty("name"));//输出true alert(marry.hasOwnProperty("name"));//输出false
?
??? 在Python中则对应hasAttribute()方法,这是顶层类object的方法。
??? 在JS中可以利用in操作符和hasOwnProperty()两个操作结合起来,来判断一个属性究竟是存在于对象还是实例中。
???
alert(checkNameInClass(ted,"name"));//输出true alert(checkNameInPrototype(ted,"name"));//输出false alert(checkNameInClass(marry,"name"));//输出false alert(checkNameInPrototype(marry,"name"));//输出true function checkNameInClass(obj,name){ return obj.hasOwnProperty(name); } function checkNameInPrototype(obj,name){ return !obj.hasOwnProperty(name)&&(name in obj); }
?
?但是上面的原型方法略显复杂,可以改用对象字面量方法来定