Javascript面向对象编程基本原理
讲解Javascript面向对象的基础原理。
李青原(liqingyuan1986@aliyun.com)创作于博客园个人博客(http://www.cnblogs.com/liqingyuan/),转载请标明出处。
1.Javascript中的对象:
A.对象类型和JSON格式的关系:
JSON(Javascript object natation)原本是JS内部对Object数据类型的描述方式,也就是字面值(literal)语法。
只是JSON这种语法设计的太成功,人们认识到了JSON的优势,才把这种原本javascript独有的数据语法拿出来,作为了网络环境中常用的一种通用数据格式。
所以对于javascript而言,以下两种写法其实是等同的:
1 var a = new Object(); 2 3 var a = {};
B.对象类型的内置[[prototype]]属性:
在javascript的对象模型中,存在一个特殊概念:原型链。javascript的不同对象之间的继承关系,正是依靠原型链这个设计模型来实现的。
原型链的原理如下:
每个Object类型的变量都内置了一个私有的[[prototype]]属性,IE下该属性无法访问,但是Firefox和Chrome封装了公共属性__proto__来访问它。
(注:为了便于区别函数的prototype属性,下文中使用__proto__属性来指代[[prototype]]属性)
__proto__属性通常指定的是另一个Object类型的变量。
当JS引擎查找某个Object类型变量的属性时,如果在变量本身找不到该属性,就会去__proto__属性指向的__proto__对象上去寻找;如果还是找不到,会继续在__proto__对象的__proto__属性指向的下一级__proto__对象上寻找,以此类推。
因此,如果某个Object类型变量A的__proto__属性指向了对象B,A就会拥有B的所有属性和方法,也就模拟了继承关系。
这种经典的“顺藤摸瓜”式的调用方式,被形象的称为原型链。原型链的终端是javascript引擎内置的一个对象,该对象的__proto__属性会是undefined,JS引擎查找到此就会结束。
2.javascript的函数:
A.函数对象的prototype属性:
在javascript中,函数(Function)是一种特殊的Object类型。
当在Object基本类型的属性方法之外,javascript会给所有函数对象指定一个共有属性:prototype属性。 (函数类型的prototype属性不等于Object类型内置的__proto__属性,此属性为函数特有的公共属性)
如前文所述,__proto__属性虽然在FF下提供了公共访问方式,但是javascript默认该属性是不可访问的,为了实现面向对象编程,函数对象和它的prototype属性将成为关键。
3.利用函数和原型链实现javascript的面向对象:
A.利用函数+关键字new实现对象创建:
1 function Human(){ 2 3 this.name = "liqingyuan"; 4 5 this.gender = "male"; 6 7 } 8 9 var human = new Human();//行1 10 11 alert(human.name +"@@"+human.gender);//liqingyuan@@male 12 13 alert(human.constructor);
前文所述,javascript函数的prototype属性指向一个由javascript引擎创建的对象,该对象还会具有一个默认的属性constructor。
constructor属性起到了构造方法的作用,此属性默认指向函数体本身,从而让JS能够利用函数来替代构造方法,模拟对象的创建过程。
因此可以分析在行1的对象创建过程:
javascript首先创建一个空的Object对象,交给指针human。
而new关键字,起到的作用就是让新建的这个object执行Human函数的prottotype对象的constructor属性(默认即Human函数体本身)。
而javascript中,函数中的this默认指代函数的调用者,所以此时Human函数体内的this指代的是新建的空object对象,也就是指针human。所以走完整个Human函数体后,human也就拥有了name和gender属性。
与此同时,new出来的新对象的内置__proto__属性会被指引到Human函数的prototype对象上,也就是说新对象不仅拥有了函数体内部通过this指定的属性,同时拥有了函数的prototype对象的属性。这是最关键的一步,导致javascript继承体系的成立。
根据上面分析的构造函数原理,上面代码其实还可以写成:
function Human(){ this.name = "liqingyuan"; this.gender = "male"; } var human = new Human.prototype.constructor();//行1 alert(human.name +"@@"+human.gender); alert(human.__proto__ == Human.prototype);//行2
行1中不再使用类似JAVA的方式,而是直接调用构造函数,同样能够获得新的human对象。
Firefox和Chrome下,行2中则会弹出true,显示了上面分析过程最后一步的正确性。 (必须在Firefox和Chrome,属性为双下划线)
B.prototype对象的属性共享关系:
通过前文的分析过程,我们可以知道同一个函数new出来的对象,其内置__proto__属性都指向了函数本身的prototype对象,因此它们必然都能访问prototype对象的属性。此时,函数体内部通过this指定的属性都是每个新对象独有的,而通过prototype对象获得的属性则是共有的。
因此通常对prototype对象的修改,都会反映到新对象上。
但是也有例外:
javascript在处理原型链时,也遵循类似JAVA的规则,优先在对象中查找属性,找不到则去对象的prototype对象中找,找不到再去prototype对象的prototype对象中找,以此类推,直到prototype属性指向undefined。
1 function Human(name,gender){ 2 3 this.name = name; 4 5 this.gender = gender; 6 7 } 8 9 Human.prototype.age = 25; 10 11 12 13 var liqingyuan = new Human("liqingyuan","male"); 14 15 var zhangjun = new Human("zhangjun","male"); 16 17 alert(liqingyuan.age +"@@"+ zhangjun.age);//行1,25 25 18 19 20 21 liqingyuan.age = 28; 22 23 alert(liqingyuan.age +"@@"+ zhangjun.age);//行2,28 25 24 25 26 27 Human.prototype.age = 30; 28 29 alert(liqingyuan.age +"@@"+ zhangjun.age);//行3,28 30
行1中,由于Human对象的prototype被指定了age属性,所以2个Human对象的age都为25;
行2中,liqingyuan被单独指定了age属性,所以JS引擎优先查找它自己的age属性28,;
行3中,由于liqingyuan自己有了age属性,所以虽然重新指定了prototype的属性age为30,JS引擎依然优先查找liqingyuan自己的属性28,而非30。
C.指定对象间的继承关系:
根据前文分析,完全可以通过指定prototype对象,来获得类似继承的效果。
1 function NewObject(){ 2 this.test = 123; 3 } 4 5 function SubObject(){ 6 7 } 8 9 SubObject.prototype = new NewObject();//行1指定继承关系 10 11 12 13 var object = new SubObject(); 14 15 alert(object.test);//子类对象拥有父类的属性
行1中,通过将NewObject对象指定为SubObject的prototype属性,使得2者联系到一条原型链上,成为了事实上的父子关系。因此,新建的子类对象才能使用父类属性。
- 上一篇 »python 面向对象详解
- 下一篇 »php笔试面试题大全