JS不是面向对象的语言,又是弱语言类型,有时候需要对一些函数进行重载,在JS机制中可以模拟"重载"
在JS的函数执行上下文中有一个名为arguments的有意思的变量,它以数组的形式存储了函数执行时传递过来的所有参数,即使函数定义没有定义这 么多个形参。还有一个特别之处就是跟Array类型相比,arguments变量有且只有一个length属性,Array的方法,例如push、pop 等,它并不具备,它只是一个“伪数组”:具有length属性,存储的数组能够用数组访问符[]来访问,并且是只读不可写。
一、对于不同个数参数的重载
这里应该很明白,直接用arguments函数的length属性来判断就可以了。
var len = arguments.length;
//传递过来一个参数的时候执行
if(len==1){
alert("Function say:"+msg);
}
//传递过来两个参数的时候执行
else if(len==2){
handler(msg);
}
}
talk("demo");
talk("demo",function(w){alert("Handler say:"+w);});
二、对于不同类型的参数的重载
对于JS这样一种动态类型的语言,这种变量声明的随意性淡化了严格的变量类型在开发人员脑子里的重要性(PS:同样是基于ECMA体系的,AS就引入 了变量声明的强制类型),很多意想不到的BUG其实都是由这种变量类型的自动转换造成的。其实JS提供了很准确的方法让我们来严格检测变量的类型,比较通 用的就是typeof方法和constructor属性。
1、typeof variable 返回变量类型
temp = 1; //number
temp = undefined; //undefined
temp = null; //object
temp = {}; //object
temp = []; //object
temp = true; //boolean
temp = function (){} //function
alert(typeof temp);
通过上面的测试你可以看出来,对于null,Object,Array返回的都是object类型,而使用下面的方法就可以解决这个困扰。
2.constructor属性检测变量类型
JS中每个对象都有constructor属性,它是用来引用构造此对象的函数,通过对这个引用的判断就可以检测变量类型了。
temp.constructor==String; //true
temp= {};
temp.constructor == Object;//true
temp= [];
temp.constructor == Array;//true
通过上面的测试已经很容易的把Array和Object类型的变量区分开了。下面我们来对自定义的对象做个测试看看会发生什么。
function Ball(){}
//实例化一个对象
var basketBall = new Ball();
basketBall.constructor==Ball; //true
这可以说明constructor属性对于自定义的对象一样适用。
在弄清楚了上面两个方法的适用以后再来回到JS函数重载的模拟上来,下面这个例子是根据参数类型来重载。
var t = typeof msg;
if(t=="string"){
alert("It's a string");
}
else if(t=="number"){
alert("It's a number");
}
}
talk(10); //It's a string
talk("demo"); //It's a number
附上一个很巧妙的严格检测参数类型和个数的函数:
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 doFunction(id,name){
//检测参数个数和类型
strict([Number,String],arguments);
..
}
另一篇文章
如果我们把JS中的一些函数也看成是“对象”的话,我们再来看看在JS中如何对它们进行“装饰”。
一、利用JS“重载”方法
突然发现JS有一种很好的机制(实际上是一位JS高人指点我的,嘿嘿),它可以“重载”一些函数,利用这种功能我们可以进行动态“重载”。参考以下代码:
function a(){
window.location='http://localhost/test.htm';
}
然后我们在这个页面上来一个alert(‘abc’)会有什么反应?页面不会弹出提示框,而是转向上边这个URL。也就是说运行alert时是执行函数a,而不是系统默认的函数了。
二、使用JS“重载”为老函数添加新的职责
我们可以在老函数的基础上动态地添加新的功能,同时又保持原来的函数签名。我认为这已经做成了Decorator,参考以下代码:
运行一下会得到什么?在提示消息出来后,页面跳转到 http: //localhost/test.htm
<SCRIPT LANGUAGE="JavaScript">
<!--
var temp=window.alert;
window.alert=a;
function a(alertValue){
temp(alertValue);
window.location='http://localhost/quickstart/';
}
alert(' Microsoft 快速入门教程');
//-->
</SCRIPT>
</BODY>
过程:
通过声明一个temp变量来保存系统原来的alert函数;
把系统的函数赋上新的方法(函数指针?^_^),新的方法意味着新的职责。
在新的方法中再调用temp中保存的原来的系统函数。
且看一个完整的例子代码:重载按钮的单击事件
<HTML>
<HEAD>
<TITLE> New Document </TITLE>
<SCRIPT LANGUAGE="JavaScript">
<!--
function old_fun(){alert("old_fun()");}
//-->
</SCRIPT>
</HEAD>
<BODY>
<button οnclick="old_fun()" id="btn">MOVE</button>
</BODY>
<SCRIPT LANGUAGE="JavaScript">
<!--
var btn=document.getElementById("btn");
var old_handler=btn.onclick;
btn.οnclick=function(){new_fun();old_handler();}
function new_fun(){alert("new_fun()");}
//-->
</SCRIPT>
</HTML>