JS界面框架的思考——控件的继承、组合和可见性

  JS框架的另一个重要任务,就是控件的访问控制,即通过怎样的方式访问一个控件,如何保证不丢失引用,以及如何确保控件可见性的合理和安全。

  控件的继承是一个额外的问题,因为控件公共的属性和方法太多是需要使用继承特性的一个重要原因,而另一个重要原因则是为了使用多态性带来的便利。JavaScript的语法决定了,在JavaScript中实现多重继承是可行的,多重继承写法可以有意识地写成类似接口继承的形式,避免混淆的对象引用。

  例如,一个Form控件,要如何控制允许出现在其中的控件类型呢?比较合理的做法就是,将文本框、单选框、提交按钮等抽象FormItem的基类即可。某种控件里,应该限定允许存在的控件类型。

  一般情况下理想情况是,组合关系不可以形成环,但是有时形成环是无法避免的,这时候框架必须小心避免实际控件的循环包含关系,否则将导致无穷递归包含的现象发生。因此,要由框架来限制环的形成(毕竟无穷递归的广义表只是理论上研究的数据结构)。

  站在安全可控的角度,我们不应该让使用者用new关键字来建立一个控件,使用new建立的控件意味着框架无法得知控件的引用情况,这对于面向对象的界面控制是一种灾难,因为既无法保证安全的可见性,也无法保证不发生引用丢失。不安全的可见性就意味着,框架将无法感知控件的更改,而丢失引用意味着一些DOM没有逻辑控件对象可以操控。

  因此,我们往往通过父对象的方法来创建一个子对象,这个子对象的可见性自然处于父对象之中,由框架感知、掌控其所有操作和引用计数。这时候程序员的设计方法就变成,先创建父对象,然后向其中添加子对象,与使用new关键字方式创建控件的常见实现正好相反。

  不过这自然会导致代码复用性的问题,因为基于这种方式就意味着无法直接使用已经创建好的控件。对于这个问题的解决方案是,重用用于创建这个控件的选项参数,而不是重用这个控件本身。

  站在消息传递的角度,控件产生的消息只能由其创建者捕获,父控件捕获消息后,自行处理或将其转发到合适的子控件的处理队列中。这种消息传递机制非常严格,类似于树形网络的消息传递,是非常严谨可靠的,但是不可避免对于开发效率有一定的影响。这种写法是有长远的好处的,因为如果程序员无法明确控件的父子关系,那么很有可能无法预料异常结果的发生。

JavaScript继承特性的一种实现

  上次讲到了JavaScript实现封装的特性,那么JavaScript能否模拟继承的特性呢?

var User = function() {
	this.username = 'username';
	this.password = 'password';
}

User.prototype.toString = function() {	
	return this.username + ':' + this.password;
};

  这个是上次所写的User的定义。现在我们要新建一个Student,使其具有User的所有属性,和就类似与继承特性。

var extend = function(derived, base) {
	baseObj = new base();
 	for (property in baseObj) {
 		derived.prototype[property] = baseObj[property];
 	}
}

var Student = function() {
	this.studentNumber = '11111';
}

extend(Student, User);

var student = new Student();

document.write(student);

  extend方法是为了模拟继承特性,函数体很简单,首先新建一个“基类”的对象,然后将“基类”对象中的所有属性,放到“派生类”的prototype中,就使得derived的对象可以访问到所有base对象具有的属性。

  实际操作时,我们首先创建一个Student,并且定义其相对与User不同的部分。随后调用extend函数进行“继承”,使其具有User构造的对象的所有属性。

  然后尝试使用Student来构造一个student对象。