这里用“反射”这个词仅是一种概念上的借用,且不论下面提到的javascript的特性是否可以被称为“反射”,javascript这种获得对象类型(通过typeof运算)、构造函数(通过观察constructor属性)甚至枚举属性和方法的特性确实为我们学习、研究和使用javascript带来了极大的便利。
在javascript中可以利用for...in方法枚举出对象中所有“可列举”的属性和方法,包括通过prototype机制“继承”的属性和方法
例如:
function classA()
{
classA.prototype.a = function(){return "a";};
}
function classB()
{
classB.prototype.b = function(){return "b";};
}classB.prototype = new classA();
var msg = new Array();
var b = new classB();
for (idx in b)
{
msg.push(idx);
}
alert(msg);
上面这个例子列举出了classB的对象b的所有可列举属性和方法(不可列举的属性方法包括大多数javascript内置对象的固有方法,如Object.isPropertyOf等等,但html对象和dom对象的方法都是可列举的),包括a、b两个成员函数。另外由于javascript中通过关联数组引用和通过函数调用引用无参方法等效(即b["a"]和b.a()都返回字符串"a"),所以将上面例子的循环体改成msg.push(b[idx]);就可以遍历执行b中的所有方法并将结果返回给msg。
另外一个有趣的话题是关于instanceof运算的。javascript的instanceof运算足够强大,强大到支持“继承”的判别,如上面那个例子,b instance of classA和b instance of classB的结果都是true。instanceof运算的这个能力正是我们使用“多态”所需要的。
相反地,同强大的instanceof运算相比,javascript的typeof运算则显得有些简陋。它只能识别出string、number等基本类型和object与function类型。这样,要判断对象类型的话,typeof就无能为力。一个比较勉强的解决方案是依靠constructor属性来判断,但是,constructor有个很烦人的问题是,它默认返回的是prototype中的构造函数。结果上面的例子中对象b的constructor竟然返回classA,而不是令人期待的classB,于是,只好在每次继承之时手工修改constructor属性,上面的例子中在classB.prototype=new classA();之后添上classB.constructor = classB。
最后谈到dom对象的问题。在javascript中dom对象和普通对象可以通过判断tagName属性来区别,但是这不是非常好的办法,因为你很难禁止他人在普通对象中定义tagName属性。另外一个比较好的方法是dom对象的constructor属性一般为undefined(同时,显然地,instanceof Object将返回false,这是它们区别其他对象的一个明显特征,当然,前提是必须保证javascript的标准在将来不会扩充到给dom对象赋予确定的constructor和严格的继承机制!最后,我想要说的是,javascript的“反射机制”是强大的,但是也是不完善的,所以在使用的时候需要小心,“常识”往往也容易令人犯错。
结束话题之前发一个稍微封装的“反射”管理类,希望它能够给大家带来一些便利:
function Reflector()
{
Reflector.getType=function(obj)
{
if (obj == null) //null的类型
{
return null;
}
else if (obj instanceof Object) //普通对象
{
return obj.constructor;
}
else if (obj.tagName != null) //dom对象
{
return obj.tagName;
}
else
{
return typeof(obj);
}
}
Reflector.getAttributes=function(obj)
{
var methods = new Array();
for (idx in obj)
{
methods.push(new Type(obj[idx],ClassManager.getType(obj[idx]),idx));
}
return methods;
}
Reflector.getAttributeNames=function(obj)
{
var methods = new Array();
for (idx in obj)
{
methods.push(idx);
}
return methods;
}
}
//描述类型的类,entity是对象实体,type是对象类型,name是对象名称
function Type(entity, type, name)
{
this.Entity = entity;
this.Type = type;
this.Name = name;
}