面向对象的 Javascript 函数重载和类型检查
其它面向对象的语言(比如Java)的一种共有的特性是“重载”函数的能力:传给它们不同数目或类型的参数,函数将执行不同操作。虽然这种能力在JavaScript中不是直接可用的,一些工具的提供使得这种探求完全成为可能。
在JavaScript的每一个函数里存在一个上下文相关的名为arguments的变量,它的行为类似于一个伪数组,包含了传给函数的所有参数。参数不是一真正的数组(意味着你不能修改它,或者调用push()方法增加新的项),但是你可以以数组的形式访问它,而且它也的确有一个length属性。程序2-5中有两个示例。
程序2-5. JavaScript中函数重载的两个示例
//一个简单的用来发送消息的函数
function sendMessage( msg, obj ) {
//如果同时提供了一个消息和一个对象
if ( arguments.length == 2 )
//就将消息发给该对象
obj.handleMsg( msg );
//否则,刚假定只有消息被提供
else
//于是显示该消息
alert( msg );
}
//调用函数,带一个参数 – 用警告框显示消息
sendMessage( "Hello, World!" );
//或者,我们也可以传入我们自己的对象用
//一种不同方式来显示信息
sendMessage( "How are you?", {
handleMsg: function( msg ) {
alert( "This is a custom message: " + msg );
}
});
//一个使用任意数目参数创建一个数组的函数
function makeArray() {
//临时数组
var arr = [];
//遍历提交的每一个参数
for ( var i = 0; i < arguments.length; i++ ) {
arr.push( arguments[i] );
}
//返回结果数组
return arr;
}
另外,存在另一种断定传递给一个函数的参数数目的方法。这种特殊的方法多用了一点点技巧:我们利用了传递过来的任何参数值不可能为undefined这一事实。程序2-6展示一了个简单的函数用来显示一条错误消息,如果没有传给它,则提供一条缺省消息。
程序2-6: 显示错误消息和缺省消息
function displayError( msg ) {
//检查确保msg不是undefined
if ( typeof msg == 'undefined' ) {
//如果是,则设置缺省消息
msg = "An error occurred.";
}
//显示消息
alert( msg );
}
typeof语句的使用引入了类型检查。因为JavaScript(目前)是一种动态类型语言,使得这个话题格外有用而重要的话题。有许多种方法检查变量的类型;我们将探究两种特别有用的。
第一种检查对象类型的方式是使用显式的typeof操作符。这种有用的方法给我们一个字符串名称,代表变量内容的类型。这将是一种完美的方案,除非变量的类型或者数组或自定义的对象如user(这时它总返回"ojbect",导致各种对象难以区分)。
这种方法的示例见程序2-7
程序2-7. 使用typeof决定对象类型的示例
//检查我们的数字是否其实是一个字符串
if ( typeof num == "string" )
//如果是,则将它解析成数字
num = parseInt( num );
//检查我们的数组是否其实是一个字符串
if ( typeof arr == "string" )
//如果是,则用逗号分割该字符串,构造出一个数组
arr = arr.split(",");
检查对象类型的第二种方式是参考所有JavaScript对象所共有的一个称为constructor的属性。该属性是对一个最初用来构造此对象的函数的引用。该方法的示例见程序2-8。
程序2-8. 使用constructor属性决定对象类型的示例
//检查我们的数字是否其实是一个字符串
if ( num.constructor == String )
//如果是,则将它解析成数字
num = parseInt( num );
//检查我们的字符串是否其实是一个数组
if ( str.constructor == Array )
//如果是,则用逗号连接该数组,得到一个字符串
str = str.join(',');
表2-1显示了对不同类型对象分别使用我所介绍的两种方法进行类型检查的结果。表格的第一列显示了我们试图找到其类型的对象。每二列是运行typeof Variable(Variable为第一列所示的值)。此列中的所有结果都是字符串。最后,第三列显示了对第一列包含的对象运行Variable.constructor所得的结果。些列中的所有结果都是对象。
表2-1. 变量类型检查
———————————————————————————————
Variable typeof Variable Variable.constructor
———————————————————————————————
{an:"object"} object Object
["an","array"] object Array
function(){} function Function
"a string" string String
55 number Number
true boolean Boolean
new User() object User
——————————————————————————————————
使用表2-1的信息你现在可以创建一个通用的函数用来在函数内进行类型检查。可能到现在已经明显,使用一个变量的constructor作为对象类型的引用可能是最简单的类型检查方式。当你想要确定精确吻合的参数数目的类型传进了你的函数时,严格的类型检查在这种可能会大有帮助。在程序2-9中我们可以看到实际中的一例。
程序2-9. 一个可用来严格维护全部传入函数的参数的函数
//依据参数列表来严格地检查一个变量列表的类型
function strict( types, args ) {
//确保参数的数目和类型核匹配
if ( types.length != args.length ) {
//如果长度不匹配,则抛出异常
throw "Invalid number of arguments. Expected " + types.length +
", received " + args.length + " instead.";
}
//遍历每一个参数,检查基类型
for ( var i = 0; i < args.length; i++ ) {
//如JavaScript某一项类型不匹配,则抛出异常
if ( args[i].constructor != types[i] ) {
throw "Invalid argument type. Expected " +
types[i].name +", received " +
args[i].constructor.name + " instead.";
}
}
}
//用来打印出用户列表的一个简单函数
function userList( prefix, num, users ) {
//确保prefix是一个字符串,num是一个数字,
//且user是一个数组
strict( [ String, Number, Array ], arguments );
//循环处理num个用户
for ( var i = 0; i < num; i++ ) {
//显示一个用户的信息
print( prefix + ": " + users[i] );
变量类型检查和参数长度校验本身是很简单的概念,但是可用来实现复杂的方法,给开发者和你的代码的使用者提供更好的体验。接下来,我们将探讨JavaScript中的作用域以及怎么更好的控制它。