EXT JS 内核剖析一,Javascript基础

Javascript 的继承机制

 

1.         Javascript对象

首先看看Javascript中的对象是什么。从概念上看,Javascript中对象与其他强类型的语言,如C++ / Java,基本类似,由一系列属性和方法构成。不过Javascript是弱类型的,对象的创建形式更加灵活。主要有Object literal(文本对象)的方式,如

var rect = {width:100, height:200};

这行代码创建了一个对象,这个对象有两个属性,并设置了初始值。

另外一种创建对象的方式是用关键字new,具体见第二小节

Javascript中的类型大致可分为两大类:

l  原子类型,包括number, Boolean, null, string undefined

l  对象类型,就是这里要讨论的对象

关于对象比较简单,就说这么多,可以参考<Javascript – The Definitive Gide>或者其他网上的文章。

 

2.         Javascript

接着再来看看类,严格来说Javascript并没有类,是用对象来表示类的。下面让我们来通过一个简单的具体例子来看看Javascript是如何通过对象来表示类的。这里借用<Javascript – The Definitive Gide>一书中的例子Rectangle,代码如下:

 

function Rectangle(w, h) {

    this.width = w;

    this.height = h;

}

这里实际上仅仅是定义了一个函数,这个函数将参数值赋值给this的作用范围中的两个变量(作用范围请参考后续文章,或者其他网上的资源或者书籍),如下面的代码示例:

<html>

<head>

         <title>Javascript inheritance testing</title> 

</head>

<body>

         <h2>Javascript inheritance testing</h2>

        

         <script>

                   var width;

                   var Height;

                   function Rectangle(w,h){

                            this.width = w;

                            this.height = h;

                   }

                   document.write("width = " + width + ", height = " + height + "<br>");

                   Rectangle(100,200);       

                   document.write("width = " + width + ", height = " + height + "<br>");

         </script>

 

</body>

</html>

在调用了函数Rectangle(100,200)后,全局范围的两个变量width, Height被赋予了函数的参数的值,当没有与其他对象关联时,this默认指全局。但是当这样的一个函数与Javascript关键字new 配合使用,就有了新的含义,看代码

var rect = new Rectangle(100, 200);

这行代码做了以下几件事:

l  new创建了一个Object对象,这是Javascript最简单的对象,

l  将这个对象传递给函数调用Rectangle(100, 200),也就是相当于限定这个函数中this指向这个新建的对象,于是这个对象就有了widhtheight属性,并分别被赋值为100200.

l  为这个对象增加一个内置的constructor属性,指向这个函数,后文有详述

l  然后将这个对象赋值给rect变量。

 

通过这几件事分析,我们可以发现,这似乎与Java或者C++创建某个类的对象的方式是一样的。于是我们可以将这样的代码模式定义为Javascript的类机制,即:

通过定义一个含有this的特殊的函数,来初始化一个对象的属性,这个函数被称为constructor—构造函数,通常也把这个函数的定义看做是类定义。按Javascript的规范,这样的函数通常用名词来命名,表示一种类型,如本文中的例子Rectangle。我们知道Javascript中的函数,其实也是对象,这点通过 Rectangle instanceof Object == true可以证明。所以如前面提到的,Javascript的类机制实际上是用对象来实现的。

 

3.         Prototype

Javacript的函数都有个prototype属性,定义函数的时候,Javascript就为这个函数(注意,一个函数就是一个对象)自动设置了prototype为一个对象,这个对象有个属性constructor指向这个函数。

而用构造函数创建对象的时候,这个对象会有个内置的属性constructor指向构造函数,所以通过由某个构造函数创建的多个对象,都指向同一个构造函数(函数也是对象),而这个构造函数有个prototype属性。

例,Rectangle.prototype.constructor 等于 Rectangle.

var rect1 = new Rectangle(1,1);

var rect2 = new Rectangle(1,2);

var rect3 = new Rectangle(1,3);…

这样创建的rect1, rect2, rect3都有constrcutor属性,而且

rect1.constructor, rect3.constructor, rect3.constructor都等于Rectangle

 

有了这样的机制,我们就可以通过扩充构造函数的prototype来定义该类的公共的东西了,比如方法。例,我们可以为Rectangle加个area方法

Rectangle.prototype.area = function() {

return this.wdith * this.height;

};

这样我们的对象rect1就可以这样调用area方法来计算面积了:

rect1.constructor.prototype.area();

不过这样调用写法是很麻烦的,我们可以直接rect1.area()来调用。Javascript内置了属性和方法的查找机制,首先看rect1对象本身是否有area方法,这里是没有的,然后Javascript会看这个对象的constructor.prototype对象是否有这个area方法,于是找到了方法定义,就可以执行调用了,如果在这个对象的constructor.prototype中没有,Javascript会看constructor.prototype是否还有constructor.prototype属性,如果有的话,会继续这样的查找,直到找到,或者达到Object.prototype,这就是所谓的prototype chain

 

4.         继承

Javascript中,每个函数(函数也是对象)都有两个方法,callapply。这两个方法功能是一样的,就是函数本省的功能,所不同的是用传入的对象作为函数中的this的指向的对象。

还是看Rectangle例子

var width =50;

         var Height = 60;

         //定义Rectangle

         function Rectangle(w,h){

                   this.width = w;

                   this.Height = h;

         }

         //创建一个Rectangle对象,width = 100, height = 200

         var rect = new Rectangle(100,200);

         //rect传入,设定Rectanglethis指向rect

         Rectangle.call(rect, 150, 250);

         //通过打印rectwidth, Height,我们发现值改变了

         document.write("width = " + rect.width + ", height = " + rect.Height + "<br>");

        

         //将全局this传入,设定Rectanglethis指向全局

         Rectangle.call(this, 155, 255);

         //通过打印rectwidth, Height,我们全局的两个变量值改变了

         document.write("width = " + width + ", height = " + Height + "<br>");

 

有了Rectangle类之后,又有需要有颜色的Rectangle,用面向对象的思想,当然是从Rectangle继承一个新的类是自然的方式。这行借用函数对象的call方法来实现类似的调用父类构造函数,代码如下

function ColoredRectangle(w,h,c){

//this传入作为Rectanglethis指向的对象,与Java中调用父类的构造函数很相似

         Rectangle.call(this, w, h);

         //设置ColoredRectangle自有属性

         this.color = c;           

}

通过这种复用代码的方式,我们实现了继承父类的属性了,如何继承父类的公用方法呢?还记得前面讲的prototype吗?

ColoredRectangle.prototype = Rectangle.prototype

这样不就是可以共享父类中公共的资源了吗?可以测试,确实ColoredRectangle有了Area方法,似乎我们实现了继承方法的目标。

我们再来为ColoredRectangle扩充一个方法getColor

         ColoredRectangle.prototype.getColor = function(){

         return this.color;

     }

 

         一切都似乎很完美,但是这个时候我们看看Rectangle,我们会发现Rectangle也有了getColor方法了,这可不是我们想要的,测试代码如下

//定义Rectangle

     function Rectangle(w,h){

         this.width = w;

         this.Height = h;

     }

     //为类增加Area方法

     Rectangle.prototype.Area = function(){

         return this.width * this.Height;

     }

     var rect = new Rectangle();

    

     function ColoredRectangle(w,h,c){

         //this传入作为Rectanglethis指向的对象,与Java中调用父类的构造函数很相似

         Rectangle.call(this, w, h);

         //设置ColoredRectangle自有属性

         this.color = c;       

     }

     ColoredRectangle.prototype = Rectangle.prototype;

     ColoredRectangle.prototype.getColor = function(){

         return this.color;

     }

    

     document.write(("getColor" in rect) + "<br>");

 

     为什么呢?问题出在这里ColoredRectangle.prototype = Rectangle.prototype这行代码将父类的prototype赋值给子类了,也就是说子类和父类的prototype指向了同一个对象,所以子类增加方法,父类也就有了。还记得前面说的prototype.constructor.prototype吧,我们可以再子类和父类的prototype中间在加一个对象,这样就实现了父类和子类prototype的分离了。

         ColoredRectangle.prototype = Rectangle.prototype改为

ColoredRectangle.prototype = new Rectangle();

这样ColoredRectangle.prototype.constructor.prototype指向了Rectangle.prototype,通过Javascriptprototype chain的查找机制,就可以共享父类的公共资源了。

但是这种方式有带来了点小问题,ColoredRectangle.prototype指向一个Rectangle对象,那么ColoredRectangle.prototype就有了widthHeight属性,而ColoredRectangle本身已经有了这两个属性,虽然不会有啥逻辑问题,但是带来了内存的浪费。

可以用Javascriptdelete来删除这两多余的属性

delete ColoredRectangle.prototype.width;

delete ColoredRectangle.prototype.Height;

到这里我们的继承目标就基本实现了,但是如果父类有很多属性的话,要一个一个删除似乎也是比较阀门的事情,这里可以通过一个小技巧来解决,用一个没有属性的对象来代替

ColoredRectangle.prototype = new Rectangle();中的new Rectangle, 并把Rectangle.prototype赋给这个对象,这样就不用删除属性了,代码如下

         //定义Rectangle

     function Rectangle(w,h){

         this.width = w;

         this.Height = h;

     }

     //为类增加Area方法

     Rectangle.prototype.Area = function(){

         return this.width * this.Height;

     }

     var rect = new Rectangle();

    

     function ColoredRectangle(w,h,c){

         //this传入作为Rectanglethis指向的对象,与Java中调用父类的构造函数很相似

         Rectangle.call(this, w, h);

         //设置ColoredRectangle自有属性

         this.color = c;       

     }

     function Empty(){};

     Empty.prototype = Rectangle.prototype;

     ColoredRectangle.prototype = new Empty();

    

     ColoredRectangle.prototype.getColor = function(){

         return this.color;

     }

     var crect = new ColoredRectangle(2,3,"red");

     document.write(("getColor" in rect) + "<br>");

     document.write(("getColor" in crect) + "<br>");

     到这里我们的继承目标才算是比较完美的实现了。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值