Prototype的bind方法常常把许多学习它的人弄得糊糊涂涂,google和baidu一番后还是一塌胡涂!本人也如此;本人觉得它是个从“谜人”到“迷人”的方法。此文将发表个人对此方法的理解,希望能帮助大家成功渡“谜”,到达“迷人”的彼岸!
<html>
<head>
<script src="prototype.js"></script> //@7
<script type="text/javascript">
/**********************************
*
* 实现渲染一个带有"上一页"和"下一页"的组件,点击"上一页",当前页减1,点击"下一页"当前页加1
*
*/
function PagesSystem(container){
this.currentPage = 10; //当前页
this.pageSysDiv = document.getElementById(container); //容器
this.init = function() { //将组件画出来
this.createPrePage();
this.createNextPage();
};
this.changePage = function(evtObj) { //根据点击后传过来的参数决定是加1或是减1
if(evtObj == "next") {
this.currentPage += 1;
alert("你已执行将当前页加1,现在当前页是:" + this.currentPage);
}else if(evtObj == "pre"){
//此处不作if(this.currentPage ==1) return;限制为了体现当传入的参数为"pre"时,下面的alert()一定会执行
this.currentPage -= 1;
alert("你已执行将当前页减1,现在当前页是:" + this.currentPage);
}
};
this.createPrePage = function() {//创建上一页组件
var _span = document.createElement("SPAN");
_span.style.cssText = "margin-left:16px";
var _a = document.createElement("A");
_a.href = "#";
_a.onclick = this.changePage; //@1 当点击此("上一页")铵钮时执行
_a.innerText = "上一页";
_span.appendChild(_a);
this.pageSysDiv.appendChild(_span);
};
this.createNextPage = function() {//创建下一页组件
var _span = document.createElement("SPAN");
_span.style.cssText = "margin-left:16px";
var _a = document.createElement("A");
_a.href = "#";
_a.onclick = this.changePage; //@2 当点击此("下一页")铵钮时执行
_a.innerText = "下一页";
_span.appendChild(_a);
this.pageSysDiv.appendChild(_span);
};
this.init(); //执行初始化
}
function testUse(msg){//@3在提出问题环节用到
alert(msg);
}
window.onload = function() {
var ps = new PagesSystem("pageDiv");
}
</script>
</head>
<body>
<div id="pageDiv"></div>
</body>
</html>
二、分析代码,提出问题,解决问题
1、无法传递参数问题。
你细看@1和@2处,当前的代码实现是无法把"pre"和"next"参数传递过去,于是,当你运行例子,点击上一页或下一页,都是没信息alert出来的!
这种情况,是很常见的。那么,如何实现将参数传过去。
将@1处代码修改如下:
_a.onclick = function(){ //@1 当点击此("上一页")铵钮时执行
testUse("pre"); //参看@3
this.changePage("pre");
}
这样创建一个匿名函数赋予_a.onclick,也就是当_a对象的onclick事件触发后将执行此匿名函数,而匿名函数将帮忙调用testUse("pre")和this.changePage("pre")两个方法,
从而达成传递参数。
修改代码,运行例子后点击上一页后会显示如下两个信息,一个是testUse中输出的信息,证明了实现参数传递,另一个却是运行错误提示。
这是执行this.changePage("pre")方法抛出来的。它并没像我们预期想的运行。
从提示获到的信息是,对象不支持此属性或方法(如果浏览器报的是中文提示就可以看到“对象不支持此方法或属性”的提示)
回头看this.changePage("pre")方法,很明显this是错误提示中所指的对象,在本应用中指PagesSystem对象指针的引用,在应用中确实是声明了this.changePage("pre")方法,但为什么说没此方法呢????
2、在问题1中,我们已成功解决传递参数,但PagesSystem对象的changePage方法被谁偷了???
再将刚才的代码修改如:
_a.onclick = function(){ //@1 当点击此("上一页")铵钮时执行
testUse("pre"); //参看@3
alert(this.tagName);
this.changePage("pre");
}
再运行例子,你会发现输出this.tagName的值为 A, 它就是_a对象。噢,我的天啊。怎么会这样???
哈哈..._a对象就是<a href=""/></a>这个html 元素对象,这里是“上一页”铵钮对象,原型中哪来changePage方法啊,所以报错!!!
你可以这样理解,看如下代码:
function PagesSystem(container){//此应用中的PagesSystem对象,changePage方法的上下文对象,也可以称为归属者。
//...省略其它代码
this.changePage = function(evtObj) { //根据点击后传过来的参数决定是加1或是减1
if(evtObj == "next") {
this.currentPage += 1;
alert("你已执行将当前页加1,现在当前页是:" + this.currentPage);
}else if(evtObj == "pre"){
//此处不作if(this.currentPage ==1) return;限制为了体现当传入的参数为"pre"时,下面的alert()一定会执行
this.currentPage -= 1;
alert("你已执行将当前页减1,现在当前页是:" + this.currentPage);
}
};
//...省略其它代码
}
这样的代码,你很容易看出this是指PagesSystem, 那么我们继续往下看
假设 A对象的原型如下:
function A() {//@4
//...
this.onclick;
this.doClick = function() { //点击
this.onclick();//执行
}
//...
}
当你在PagesSystem方法中
_a.onclick = function(){ //@1 当点击此("上一页")铵钮时执行
testUse("pre"); //参看@3
alert(this.tagName);
this.changePage("pre");
}
写上这样的代码后,你可以离谱认为@4的代码的模样是如下:
function A() {//@4
//...
this.onclick = function(){ //@1 当点击此("上一页")铵钮时执行
testUse("pre"); //参看@3
alert(this.tagName); //@5
this.changePage("pre"); //@6
};
this.doClick = function() { //点击
this.onclick();//执行
}
//...
}
呵呵。。如果这样看的话,@5,@6中的this当然是指a对象,没异义。那当然是没changePage方法。
3、那么如何解决这问题呢??
很幸运,prototype.js中的bind方法就可以解决这样的问题,它还解决我们上面提的传参数问题。
看bind大侠帅样:
bind: function() {
if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
var __method = this, args = $A(arguments), object = args.shift();
return function() {
return __method.apply(object, args.concat($A(arguments)));
}
}
bind方法中的this就是bind方法的所属者(上下文)如: f.bind(),f是一个声明了的方法,那么bind 方法里的this就是f
再细看,bind方法其实做的工作是返回一个匿名函数,此匿名函数帮忙执行this所指的方法(bind方法的所属者),如果你有如下代码
function f(msg) {
this.functionName = "f method";
alert(msg);
alert(this.functionName);
}
button.onclick = f.bind(this, msg); //这里的this指f, 在bind方法中用object = args.shift()获得,这样的话,当点击button后执行f方法, f方法中的this就不会无故被 button代替。^_^不然,会报错的啊,button哪来functionName,呵呵...
它既解决将msg参数传过去,同时将f绑定到button环境下,bind方法得名可能就是这意义吧。至于如何实现将f绑定,靠的就是apply方法。
apply谜人色彩就由你们自行去揭开啦!
介绍了bind大侠给大家,我的例子就麻烦你们自己调通它啦。谢了。。
欢迎交流指正。
备注: 如需转载本文,请注明出处
posted on 2008-05-08 22:37
Sonny Li 阅读(1925)
评论(6) 编辑 收藏 所属分类:
Prototype学习志