dinghao

记录成长点滴

 

Ajax-Javascript-继承

 Prototype-based继承

继承只是在用OO的术语描述Js,不能用OO继承来理解Js的继承。Js的继承根据实现方式不同,结果也不同。如Ma的继承打破了prototype chain,和常用的继承实现方式不同。这里只谈基于prototype的继承。

Prototype

由于继承,prototype会形成链。链从当前的prototype开始,到Object结束。Prototypechain是许多Js方法的基础,如变量定位机制:先查找构造函数内的变量,即obj自身,没有会顺着prototype chain寻找,并一直到Object。如果没有找到则undefined

因此实现继承最好不破坏这条链,但是MA的实现破坏了他,除了这点MA实现是很完美的。

下面的例子会形成PositionedRectangleRectangleObjectprototype chain,但是Ma实现没有中间的Rectangle,这是Ma实现的缺点。

Prototype

Prototype是对象(文中(以前文也是)和Js关联的对象都是指objects,不同于Object或者c#中的对象,定义在ecma有)。

但是他和其他对象的区别是,Js会为其指定一个默认constructor,他指向和Prototype关联的构造函数。没有继承的情况下,一般都是默认值(除非手动修改),但继承的实现方式可能会改变Prototype类型。

如例子中:

加上:PositionedRectangle.prototype.constructor = PositionedRectangle;

pr.constructor.prototype instanceof Rectangle//ture

去掉:PositionedRectangle.prototype.constructor = PositionedRectangle;

pr.constructor.prototype instanceof Rectangle//false

注:但是我认为不要依赖prototype的类型做任何判断,象上面的判断应该都要避免,Js没有说明prototype类型,只保证是objct 就合法。这也是prototype和其他对象不同之处,别的对象可以依赖于instanceof

继承的属性&重新生成的属性

继承在这有两种含义:prototype 继承和通常意义继承(在Definitive guide上称为子类化和超类化,没有称为继承)。

Callapply的使用,转换了上下文,所以他们的属性已经不再属于基类,也就是父类成员被拷贝到了子类,这不是继承。

pr.hasOwnProperty('width')//true

被继承下来的只是prototype中的属性,但是prototype的第一个值是constructor,这也导致基类私有变量被继承。也就是说,基类私有变量在子类有两份,一份是拷贝过来的,另一份是继承下来的,虽然实际上copy过来的属性会隐藏继承下来的属性,我们一般不会访问到但是不是不能访问,这可能会是安全漏洞。例子中继承下来的变量可以这样访问:

PositionedRectangle.prototype.width;//undifiend

注:Ma实现只存在拷贝,不存在继承私有变量。

属性隐藏

本质就是通过prototypechain

第一种实现的副作用

我想要的只是prototype,结果一些无用的变量被继承,一些默认实现被修改,详细看代码注释。

Ma实现的副作用

破坏prototypechain,强制准确类型判断必须用反射。Prototypechain的改变使依赖于它的Js系统函数不能正确工作,如:instanceof

My.PositionedRectangle.prototype instanceof My.Rectangle//false

pr instanceof My.PositionedRectangle//false 不能正确判断基类

可以看到实现方式不同,继承会有不同的结果。如果不看源码,而用通常实现来理解Ma的继承,会产生误解。

Instanceof

我不知道是如何实现Instanceof函数的,但是可以推测他的实现,那就是可能用prototypechain

实现的类型识别,因为这是惟一可能回溯到基类的地方。这也是Ma的类型不能正确工作的原因。

类型辨别功能的缺失,是MA必须提供其他方式来判断来型,那就是“反射”,这里的反射也和C#没有一点关系,在Ma中应该优先用反射判断类型,这是唯一准确的方式。

反射

MA极大的改进了Js的类型识别,他也必须这样做,否则没有别的办法来识别类型。用的也很巧,只是在注册namespace或者类时做个记录,却实现了这么强的功能,Ms称为 Reflector

如果不这样,用下面的实现检测类型,会不准确,也很笨拙:

function getType(x) {
    
// If x is null, return "null"
    if (x == nullreturn "null";
 
    
// Next try the typeof operator
    var t = typeof x;
    
// If the result is not vague, return it
    if (t != "object")  return t;
 
    
// Otherwise, x is an object. Use the default toString( ) method to
    // get the class value of the object.
    var c = Object.prototype.toString.apply(x);  // Returns "[object class]"
    c = c.substring(8, c.length-1);              // Strip off "[object" and "]"
 
    
// If the class is not a vague one, return it.
    if (c != "Object"return c;
 
    
// If we get here, c is "Object".  Check to see if
    // the value x is really just a generic object.
    if (x.constructor == Object) return c;  // Okay the type really is "Object"
    // For user-defined classes, look for a string-valued property named
    // classname, that is inherited from the object's prototype
    if ("classname" in x.constructor.prototype &&  // inherits classname
        typeof x.constructor.prototype.classname == "string"// its a string
        return x.constructor.prototype.classname;
 
    
// If we really can't figure it out, say so.
    return "<unknown type>";
}

可以把上面的代码看作Ma中反射原形,虽然和MA实现不同,但都是基于给obj增加classname属性实现,MAregisterNamespaceregisterclass解析了类型,是其他方法能工作的基础。

通常实现

摘自Definitive

// Here is a simple Rectangle class.

// It has a width and height and can compute its own area

function Rectangle(w, h) {

    
this.width = w;

    
this.height = h;

}


Rectangle.prototype.area 
= function( ) return this.width * this.height; }

 

// Here is how we might subclass it

function PositionedRectangle(x, y, w, h) {

    
// First, invoke the superclass constructor on the new object

    
// so that it can initialize the width and height.

    
// We use the call method so that we invoke the constructor as a

    
// method of the object to be initialized.

    
// This is called constructor chaining.

    Rectangle.call(
this, w, h);

 

    
// Now store the position of the upper-left corner of the rectangle

    
this.x = x;

    
this.y = y;

}


 

// If we use the default prototype object that is created when we

// define the PositionedRectangle( ) constructor, we get a subclass of Object.

// To subclass Rectangle, we must explicitly create our prototype object.

PositionedRectangle.prototype 
= new Rectangle( );

 

// We create this prototype object for inheritance purposes, but we

// don't actually want to inherit the width and height properties that

// each Rectangle object has, so delete them from the prototype.

delete PositionedRectangle.prototype.width;

delete PositionedRectangle.prototype.height;

 

// Since the prototype object was created with the Rectangle( ) constructor,

// it has a constructor property that refers to that constructor.  But

// we want PositionedRectangle objects to have a different constructor

// property, so we've got to reassign this default constructor property.

 

PositionedRectangle.prototype.constructor 
= PositionedRectangle;

 

// Now that we've configured the prototype object for our subclass,

// we can add instance methods to it.

PositionedRectangle.prototype.contains 
= function(x,y) {

    
return (x > this.x && x < this.x + this.width &&

            y 
> this.y && y < this.y + this.height);

}


我加注释的版本,注释不是上面的翻译:

function Rectangle(w, h) {

    
this.width = w;

    
this.height = h;

}


Rectangle.prototype.area 
= function( ) return this.width * this.height; }

Rectangle.prototype.constructor
//Rectangle

function PositionedRectangle(x, y, w, h) {

   

    Rectangle.call(
this, w, h);

    
this.x = x;

    
this.y = y;

}


PositionedRectangle.prototype.constructor
//PositionedRectangle,默认回引到prototype关联的构造函数

PositionedRectangle.prototype 
= new Rectangle( );//语句1 这条语句形成prototypechain,这种继承方式副作用很大,我们需要的只是Rectangle的prototype,可得到了许多无用的东西,后面要删除

PositionedRectangle.prototype.constructor
//Rectangle,继承改变了默认方式

PositionedRectangle.prototype.constructor 
= PositionedRectangle;//语句1的副产物,因为语句一修改了默认实现,我们要把它修正过来。

 

delete PositionedRectangle.prototype.width;//删除无用私有变量,否则暴露了基类的私有变量,这些变量都是语句一的副产物,不删除由于Js变量调用机制也不会被调用,他们都被

delete PositionedRectangle.prototype.height;//Rectangle的私有变量隐藏,但是不删除这就是漏洞。

 

PositionedRectangle.prototype.contains 
= function(x,y) {

    
return (x > this.x && x < this.x + this.width &&

            y 
> this.y && y < this.y + this.height);

}


//后面如果定义一个同名变量area,由于prototypechain,会隐藏Rectangle的area,测试prototypechain

PositionedRectangle.prototype.area
=function(){return 'is test'}

var pr=new PositionedRectangle(1,2,3,4);

pr.area();
//is test,隐藏Rectangle

MA库实现的版本:

Type.registerNamespace('My');

// Here is a simple Rectangle class.

// It has a width and height and can compute its own area

My.Rectangle
= function(w, h) {

    
this.width = w;

    
this.height = h;

}


My.Rectangle.prototype.area 
= function( ) return this.width * this.height; }

My.Rectangle.registerClass('My.Rectangle');

 

// Here is how we might subclass it

My.PositionedRectangle
=function(x, y, w, h) {

 

     My.PositionedRectangle.initializeBase(
this,[w,h]);

    
// Now store the position of the upper-left corner of the rectangle

    
this.x = x;

    
this.y = y;

}


 

My.PositionedRectangle.prototype.contains 
= function(x,y) {

    
return (x > this.x && x < this.x + this.width &&

            y 
> this.y && y < this.y + this.height);

}


My.PositionedRectangle.registerClass('My.PositionedRectangle',My.Rectangle);

var pr=new My.PositionedRectangle(1,2,3,4);

MicrosoftAjax.js
实现

继承分成了两部分:registerClassinitialbase

代码执行过程不再分析,几个要点:

 

this.prototype.constructor = this//更改默认构造函数。

this.__baseType.apply(instance);

//拷贝prototype,同时也破坏了chain

for (var memberName in baseType.prototype) {

            
var memberValue = baseType.prototype[memberName];

            
if (!this.prototype[memberName]) {

                
this.prototype[memberName] = memberValue;

            }


        }

接下来会写关于scope chaincallobject和闭包的,这些是Js中最头痛的概念,我用了一周才明白大概,可能是最后一篇,因为MA.js余下的东西都没有什么可说了

posted on 2007-07-19 13:36 思无邪 阅读(...) 评论(...) 编辑 收藏

导航

统计

公告