Prototype源码浅析——Function.prototype部分(二)
接着上一部分:Prototype源码浅析——Function.prototype部分(一)
粗略回顾,前面的
一个例子,两条道路:
function handler(greet){
console.log(greet,this.name);
}
var obj = {
name : 'xesam'
}
第一、用call或者apply来改变handler的作用域,handler.call(obj,'hello'),这种情况下需要另一个函数来包装一下。
第二、将handler变成obj的一个方法,让其能这么调用obj.handler()
第一部分说的是第一条道路,现在就说第二条道路。
既然要将handler变成obj的一个方法,当然直接抢是不可能的,obj.handler的handler只是我定义的一个同名方法(要定义成其他的如xx都可以)而已,跟例子定义的function handler还是有本质区别的。
我们暂且把这种操作叫做“方法化”(非官方),取个名称比如methodize(Prototype里面的真实方法名)。
参考bind的说明,具体实现应该是这样的:
Function.prototype.methodize = function(){
var _this = handler;//在例子中代表handler
return function(){
var caller = obj;//在例子中代表obj
//arguments实际参数
var args = [caller].concat(Array.prototype.slice.call(arguments));
_this.apply(obj,args);
}
}
换成通用的形式后调用就是:
function handler(){
console.log(this.name);
}
var obj = {
name : 'xesam'
}
Function.prototype.methodize = function(){
var _this = this;//在例子中代表handler
return function(){
var caller = this;//在例子中代表obj
//arguments实际参数
var args = [this].concat(Array.prototype.slice.call(arguments));
_this.apply(this,args);
}
}
obj.handler = handler.methodize();
obj.handler();
不过真生的Prototype中的实现方式有一点点的细微区别:
function methodize() {
if (this._methodized) return this._methodized;
var __method = this;
return this._methodized = function() {
var a = update([this], arguments);
return __method.apply(null, a);//这里apply的第一个参数并不是this,而是null
};
}
因此原版中更明显的调用的方式是:
function handler_1(obj){ //这样之后和hangdler_1(obj_1)的效果一样了,不过obj_1.handler_()显得更面向对象而已,也用在了面向对象上面
console.log(obj.name);
}
var obj_1 = {
name : 'xesam'
}
obj_1.handler = handler_1.methodize();
obj_1.handler();
其实如果理解了bind,这一个操作也好理解,整个Function的扩展这一部分,就是利用匿名函数和作用域的变换来实现的,灵活的同时也不怎么好理解。
把一个简单的事情复杂化,这样做的主要意图单看Function这一部分根本是匪夷所思,后面的其他部分会再提到的。
另一个容易让人迷糊的方法wrap,号称wrap体现了面向方面的程序设计的本质。通俗来说就是将方法本身作为新方法的第一个参数来包装,主要是可以调用包装前和包装好后的方法,后面在Class部分,可以让你调用父类或者子类的方法。
具体代码和bind差不多,所以我直接给出来了:
Function.prototype.wrap =function(wrapper){
var _this = this;
return function(){
var params = [_this.bind(this)].concat(Array.prototype.slice.call(arguments, 0));
return wrapper.apply(this, params);
}
}
我们还是用实例说话:
Function.prototype.bind = function(context) {
var __method = this, args = Array.prototype.slice.call(arguments, 1);
return function() {
var b = args.concat(Array.prototype.slice.call(arguments, 0));
return __method.apply(context, b);
}
}
Function.prototype.wrap =function(wrapper){
var _this = this;
return function(){
var params = [_this.bind(this)].concat(Array.prototype.slice.call(arguments, 0));
wrapper.apply(this, params);
}
}
function handler(){
console.log('自定义的handler',this.name);
}
var obj = {
name:'小西山子'
}
obj.handler = handler.wrap(function(handlerBinded,content){ //这里可以用上面说的方法化的,不过避免麻烦,我直接用脑对象来处理
if(content){
console.log(this.name); //this的指向
console.log(content);
}else{
console.log(this.name); //this的指向
handlerBinded(); //this的指向
}
});
obj.handler();
obj.handler('test');
具体的还是到Class里面再说吧。
其他的方法里面包括两个关于延时的:delay和defer
说到延时方法有二:
第一:循环XXXXX次,然后再执行,就像sleep()的效果一样,或者死循环,就像die()的效果一样
第二:setTimeout和setInterval。
第一种方法主要是用来模拟sleep()做个测试什么的,不考虑
第二种方法如果只要延时执行,setTimeout就可以了。
还是一个例子:
function handler(){
console.log('执行');
}
想让handler延时1s执行,不多想:
setTimeout(function(){
handler();
},1000)
如果想要方法的形式,也不多想,像bind一样把参数什么的往里面丢就可以了:
Function.prototype.delay = function(timeout){
var _this = this;
var _params = Array.prototype.slice.call(arguments,1);
var timer = setTimeout(function(){
_this.call(_this,_params);
},timeout);
return timer;
}
handler.delay(1000);
至于defer,就是对delay的一个小包装而已。
delay中的timeout是可选的,defer直接给timeout赋值0.01(有点要说明的是我的实现里面timeout单位是ms,Prototype把timeout单位是s);
所以
handler.defer() -->handler.delay(10);//注意单位,这里用的是我实现的单位。
这两个延时函数唯一值得指出的就是defer(10)中的那个10,延迟10ms然后执行(其实际效果就是js解释器一有空闲就立马执行,起到一个防止阻塞当前代码的作用)。
平时见得多的可能是这种形式:
setTimeout(function(){
},0)
关于js定时器的机制可以看些别的文章:
【转载】关于setTimeout,理解JavaScript定时机制
最后一个方法就是获取方法形参:argumentNames。
function handler(x,y){
}
handler.argumentNames();//['x','y']
说这个函数只需要理解handler.toString()就行,handler.toString()会返回handler的字符串形式(还包括注释哦)。
function handler(x,//x_1
y/* y_1 */){
}
console.log(handler.toString());
打印结果:
'function handler(x,//x_1
y/* y_1 */){
}'
因此我们只需要用正则替换注释内容以及空白就行。
Function.prototype.argumentNames = function(){
var tmp = this.toString().match(/^[\s\()]*function[^(]*\(([^)]*)\)/)[1];//获取参数部分
var tmp_1 = tmp.replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g,''); //剔除注释
var tmp_2 = tmp_1.replace(/\s+/g,''); //剔除空白
return tmp_2.split(',')
}
Prototype里面还有一点判断的部分,看看就行了:
Function.prototype.argumentNames = function(){
var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1]
.replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '')
.replace(/\s+/g, '').split(',');
return names.length == 1 && !names[0] ? [] : names;//这里是判断参数是否空
}
到此,函数部分处理update和merge两个没有提到之外,所有的方法都在这里了。
有什么错误之处,还请指出,谢谢
转载请注明来自小西山子【http://www.cnblogs.com/xesam/】
本文地址:http://www.cnblogs.com/xesam/archive/2011/12/18/2292739.html