代码改变世界

从Atlas到Microsoft ASP.NET AJAX(2) - Class、JavaScript Extensions (Client BCL)

2006-10-21 13:06 Jeffrey Zhao 阅读(...) 评论(...) 编辑 收藏
Prototypes and Closures

在CTP release中,“类”和类型是使用closure定义的,在类的构造函数中生效(尽管这些类不能以.NET Framework中的类的方式理解,为了方便,我们在这里还是将它们称之为类)。在RTM中,我们使用了prototype模型,做出这种改变有几个原因,下面的表格比较了这两种模型:

Prototype

Closure

需要构造函数

构造函数里包含成员定义

把“_”作为前缀,作为成员“private”标志

成员被封装在类中,成为了真正的private成员。

类成员被有效的共享,这大大减少了多个对象实例带来的内存消耗。

成员是基于实例的,这增加了每个对象所占内存数量。

Prototype提供了一个在所有的浏览器中都能得到更迅速的加载时间。在Firefox中,使用prototype会使加载时间有显著提高。在任何情况下,用户都不能察觉到脚本的加载时间。

使用prototype带来最主要的性能收益是减少了每个对象的内存使用,并显著加快了对象的实例化速度。

在IE中,closure稍微有着更好的加载时间。

由于能够使用“反射”来获得类型的信息,则不用初始化对象实例就能得到工具的IntelliSense和语句补全功能支持。

支持IntelliSense和代码补全的工具必须通过执行代码来初始化一个类来获得必要的类型信息

我们设法在Visual Studio提供这一功能,但是不是在Visual Studio 2005中。

在调试时,能在调试器中看到private成员。

在调试时,从调试器中无法轻易看到类的私有成员,如果在IDE中查看私有成员的话,必须经过一些操作。


我们从很多方面研究了prototype相对于closure的性能优势——不光是对象实例的内存使用,还包括实例化一个包含各种各样类和成员的典型应用程序。这种性能研究都在多个浏览器中进行,因为我们的目标是找出一个应用于所有浏览器上最合适的模型。

如果您只是编写ASP.NET页面,在自己的脚本或者XML-Script中使用ASP.NET AJAX Framework客户端对象,则不会受到这种变化的影响。

CTP版使用了内置的closure支持,保证了基础方法能够被合适地访问到。这个支持在在RTM中被移出了,您可能会需要为基于prototype的类派生出“子类”,那么请不要在继承链中任何一个使用基于closure的类型。

Note:在beta版中,存在着一些支持closure的概念,这有可能在RTM时被移除。然而,一般来说closure还是能够在子类中使用,即使它派生于一个prototype类。但是开发人员不应该使用closure开发一个可能被继承的类。

  下面的例子展示了RTM设计中最重要的地方,稍后会提供一些更深入的描述和成员模式。
registerNamespace("Custom");

// Prototype: Constructor
Custom.Timer = function() {
    
// Ctor creates private fields and calls base. 
    Custom.Timer.initializeBase(this);
    
this._interval = 1000;
    
this._enabled = false;
    
this._timer = null;
}
// Prototype: members
Custom.Timer.prototype = {
    member1: 
function() { },
    member2: 
function() { },
    ...
}
Custom.Timer.registerClass('Custom.Timer', Sys.Component);

与此相反,在CTP版本中使用了基于closure的定义方式,请看下面的例子:
registerNamespace("Custom");

// Closure: Constructor and members
Custom.Timer = function() {
    Sys.Timer.initializeBase(
this, [true]);    
    
    
var _interval = 1000;
    
var _enabled;
    
var _timer = null;

    
this.member = function() ... 
}
Custom.Timer.registerClass('Custom.Timer', Sys.Component);

Comment  使用Closure是以前Atlas的硬伤,它大大影响了整个应用程序的性能。现在将定义类的方式变成了使用prototype,这样在“表面”看上去有别于类的定义,但是由于各种比如方法的共享,使对象不用保留每一份的方法代码拷贝,从而增强了性能。而且事实上,使用prototype更像是高级语言的编译器处理“类”和“继承”能概念的做法。
在文档里提到了,如果是定义一个不会被派生的类,那么可以使用closure,但是我们还是应该完全使用prototype。这涉及到标准的遵循、性能和扩展性等各个方面。同样,除非真正必要,我们不应该访问一个对象里前缀是“_”的方法,它们被认作是私有成员。“按章法出牌”能够提供更高的可维护性。Microsoft ASP.NET AJAX提供的客户端编程模型,甚至在以前Atlas中使用closure的方式来定义类的原因,不都是为了向服务器端的编程模型靠拢吗?



JavaScript Extensions (Client BCL)

CTP版本包括了对于JavaScript内置对象的一系列扩展,这些会继续保留在RTM版本中。然而,它们被重新设计以避免与其它AJAX框架的潜在冲突。

Array

在RTM版本中去除了对于Array的prototype的扩展,使之变为Array类型的静态成员,它们通过被传入Array对象进行工作。

Error

在CTP版本中,存在一个基本的错误处理方式,不过它被扩展了。RTM版本谨慎地扩展了Error的prototype,从而提供了“强类型”的错误。组件开发人员尤其需要抛出错误(典型的debug脚本)。在RTM版本中,您能捕获异常,为之补充更详细的错误信息。下面的代码提供了最基本的错误处理方式的示例,演示了如果捕获错误并提供更详细的错误信息:
function checkRange(index) {
    
if (index > 10 || index < 0) {
        
throw Error.argumentOutOfRange(“index”, index, 
            
"The index value must be between 0 and 10.");
    }
}

function myCallingFunction(value) {
    
try {
        checkRange(value);
    } 
catch (e) {
        e.message 
= e.name + “:” + e.message + “\nIndex value was: “ + 
            e.actualValue;
        
throw e; // bubbles up to the default error treatment
    }
    ...
}

一个异常被抛出后,包含错误信息的error对象会被浏览器处理。

Comment  可以看出Client FX正努力把客户端的编程模型向服务器端成熟的编程模型中靠拢。这种扩展使用了Javascript的高度灵活性来对开发人员使用的模型进行限制。这应该是件好事吧,我们并没有丧失什么。