《仔仔细细分析Ext》 Ext对Function类的扩展

2024-01-09 14:10

本文主要是介绍《仔仔细细分析Ext》 Ext对Function类的扩展,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

原生 Function 类:

         参考手册并没有过多介绍 JS 内建的 Function 对象,只是介绍了如何用 new Function(name,arg…); 这样的方法来创建一个新的函数。

另外,通过其它资料我们还可以了解到,每个被定义的 function 都有 length 属性,它描述这个 function 定义时的实际参数个数。另一个属性就是原型: prototype ,当一个 function 被调用的时候, js 引擎负责创建 functionprototype 属性。

有可能内建的 Function 类本身没有什么其它实际可用的属性或方法;另一种可能是 js 不想暴露关于 Function 类的过多细节。因为如果这样做可能带来过度的复杂性。

 

Ext Function 的扩展

         ExtFunction 扩展了五个实例方法: createCallback()createDelegate()defer()createSequence()createInterceptor()

         也就是说,在使用 Ext 框架时,对于每个自定义的函数,都可以直接调用这些方法。

         下面来看源码(注释都已翻译成中文)。

Js代码 复制代码
  1. <SPAN style="FONT-SIZE: medium">/**  
  2.     * @ Function类  
  3.     *这些方法对于每个Function对象都有效(任何一个js函数)。  
  4.     */  
  5. Ext.apply(Function.prototype, {   
  6.     /*该方法用来创建一个可以传递参数的回调函数。  
  7. *该方法可以直接在任何一个function上调用。  
  8. *例如:<code>myFunction.createCallback(arg1, arg2)</code>,这样就创建了一个绑定了  
  9. *两个参数的方法。  
  10.     *如果回调函数需要特定的作用域,请用createDelegate()方法替代。  
  11.     *createCallback()方法返回的函数总是在window作用域(顶级作用域)中执行。  
  12. *如果你想给一个回调方法传递参数,则需要使用该方法。如果不需要参数,你可以直  
  13. *接简单地传递一个函数的引用给需要回调的地方就可以了(例如callback: myFn)。(译者*注:这句话的意思就是说,如果你不需要向回调函数传递参数,就没有必要使用*createCallback()这个方法,直接按照常规的方式写就可以了)。  
  14. *然而,(译者注:按照常规的写法的话),如果你尝试向回调函数传递参数,(例如callback: *myFn(arg1, arg2))。那么函数在解析的时候就会被简单地执行。(译者注:而不是你期  
  15. *望的在发生某件事情之后再来回调。)  
  16.     *createCallback()的示例用法如下:  
  17.     <pre><code>  
  18. var sayHi = function(name){  
  19.     alert('Hi, ' + name);  
  20. }  
  21.  
  22. // clicking the button alerts "Hi, Fred"  
  23. new Ext.Button({  
  24.     text: 'Say Hi',  
  25.     renderTo: Ext.getBody(),  
  26.     handler: sayHi.createCallback('Fred')  
  27. });  
  28. </code></pre>  
  29.      *@返回值 {Function} 新的回调函数。  
  30.      */  
  31. createCallback : function(/*args...*/){   
  32. //使得传递进来的参数在下面的function中可用。(译者注:这里实际上是返回了//一个闭包函数,然后使用window来调用原来的函数,并把需要的参数传递进去。)   
  33.         var args = arguments;   
  34.         var method = this;   
  35.         return function() {   
  36.             return method.apply(window, args);   
  37.         };   
  38.     },   
  39.   
  40. /*创建一个代理(回调)函数,把作用域设置到参数obj上。  
  41.     *该函数可以直接再任何函数上调用。例如:<code>this.myFunction.createDelegate(this, *[arg1, arg2])</code>,这将创建一函数并自动把作用域设置到obj上,这样的话,新的*callback函数中的this属性指向obj对象。  
  42.     *用法示例:  
  43.     * <pre><code>  
  44. var sayHi = function(name){  
  45. //注意这里的this.text用法。这个函数期望在一个包含text属性的作用域中//执行。在这个例子中,这个’this’属性指向了下面传入createDelegate方法//的btn对象。  
  46.     alert('Hi, ' + name + '. You clicked the "' + this.text + '" button.');  
  47. }  
  48.  
  49. var btn = new Ext.Button({  
  50.     text: 'Say Hi',  
  51.     renderTo: Ext.getBody()  
  52. });  
  53.  
  54. //回调函数将在button实例的作用域中执行。  
  55. //点击按钮,将弹出"Hi, Fred. You clicked the ‘Say Hi’ button."  
  56.  
  57. btn.on('click', sayHi.createDelegate(btn, ['Fred']));  
  58. </code></pre>  
  59. * @参数1 {Object} obj (可选) 需要设置的目标作用域对象。  
  60.       * @参数2 {Array} args (可选) 覆盖默认的调用参数。(默认为调用者传递进来的参数。)  
  61.       * @参数3 {Boolean/Number} appendArgs (可选) 如果为true,(第二个参数)args被  
  62. *添加到调用参数中而不是覆盖。如果为数字,(第二个参数)args将被插入到指定的*位置。  
  63.      * @返回值 {Function} 新的函数  
  64.      */  
  65.     createDelegate : function(obj, args, appendArgs){   
  66.         var method = this;   
  67.         return function() {   
  68.             var callArgs = args || arguments;   
  69.             if(appendArgs === true){   
  70.                 callArgs = Array.prototype.slice.call(arguments, 0);   
  71.                 callArgs = callArgs.concat(args);   
  72.             }else if(typeof appendArgs == "number"){   
  73.                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first   
  74.                 var applyArgs = [appendArgs, 0].concat(args); // create method call params   
  75.                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in   
  76.             }   
  77.             return method.apply(obj || window, callArgs);   
  78.         };   
  79.     },   
  80.   
  81.      /*在指定的毫秒数之后调用函数,同时可以指定特定的作用域。  
  82.       *用法示例:  
  83.       * <pre><code>  
  84. var sayHi = function(name){  
  85.     alert('Hi, ' + name);  
  86. }  
  87.  
  88. // 立即执行:  
  89. sayHi('Fred');  
  90.  
  91. //2秒后执行:  
  92. sayHi.defer(2000, this, ['Fred']);  
  93.  
  94. //有时候这种语法在推迟执行一个匿名的函数的时候非常有用:  
  95. (function(){  
  96.     alert('Anonymous');  
  97. }).defer(100);  
  98. </code></pre>  
  99.      * @参数1 {Number} millis 设置调用setTimeout的毫秒数(如果为0函数立即被调用)。  
  100.      * @参数2 {Object} obj (可选) 需要设置的特定作用域  
  101.      * @参数2 {Array} args (可选) 覆盖默认的调用参数。(默认为调用者传递进来的参数。)  
  102.      * @参数3 {Boolean/Number} appendArgs (可选) 如果为true,(第二个参数)args被  
  103. * 添加到调用参数中而不是覆盖。如果为数字,(第二个参数)args将被插入到指定的* 位置。  
  104.      * @返回值 {Number} 定时器id,可以用来清除定时器。  
  105.      */  
  106.     defer : function(millis, obj, args, appendArgs){   
  107.         var fn = this.createDelegate(obj, args, appendArgs);   
  108.         if(millis){   
  109.             return setTimeout(fn, millis);   
  110.         }   
  111.         fn();   
  112.         return 0;   
  113.     },   
  114.   
  115.     /*使用原始的函数和传递进来的函数来创建一个组合的函数调用顺序(译者注:这是一*种类似管道式的调用效果,使用第一个函数的参数来调用第二个函数)。  
  116.     *最终执行的函数返回原始函数的结果。  
  117.     *使用原始函数的参数来调用传递进来的函数。  
  118.     *示例用法:  
  119.     * <pre><code>  
  120. var sayHi = function(name){  
  121.     alert('Hi, ' + name);  
  122. }  
  123.  
  124. sayHi('Fred'); //弹出 "Hi, Fred"  
  125.  
  126. var sayGoodbye = sayHi.createSequence(function(name){  
  127.     alert('Bye, ' + name);  
  128. });  
  129.  
  130. sayGoodbye('Fred'); //两个alert都会弹出  
  131. </code></pre>  
  132.      * @参数1 {Function} fcn 需要顺序执行的函数  
  133.  *@参数2 {Object} scope (optional) 传递进来函数的作用域 (默认作用域为原始函数的*作用域或window)  
  134.      * @返回值 {Function}  新的函数  
  135. */  
  136.     createSequence : function(fcn, scope){   
  137.         if(typeof fcn != "function"){   
  138.             return this;   
  139.         }   
  140.         var method = this;   
  141.         return function() {   
  142.             var retval = method.apply(this || window, arguments);   
  143.             fcn.apply(scope || this || window, arguments);   
  144.             return retval;   
  145.         };   
  146.     },   
  147.   
  148.        
  149. /*创建一个拦截函数。传入的函数在原始的函数之前执行。如果传入的函数返回false,*就不执行原始的函数。最终函数返回原始函数的执行结果。传递进来的函数使用原始*函数的参数来调用。  
  150.      *用法示例:  
  151.      * <pre><code>  
  152. var sayHi = function(name){  
  153.     alert('Hi, ' + name);  
  154. }  
  155.  
  156. sayHi('Fred'); // 弹出 "Hi, Fred"  
  157.  
  158. //创建一个新的函数来校验输入,而不直接去修改原始函数。  
  159. var sayHiToFriend = sayHi.createInterceptor(function(name){  
  160.     return name == 'Brian';  
  161. });  
  162.  
  163. sayHiToFriend('Fred');   //没有弹出  
  164. sayHiToFriend('Brian');  // 弹出"Hi, Brian"  
  165. </code></pre>  
  166.      * @参数1 {Function} fcn 需要在原始函数调用之前被调用的函数  
  167.      *@参数2 {Object} scope (可选) 拦截器的作用域(默认为原始函数的作用域或者window)  
  168.      * @返回值 {Function} 新的函数  
  169.      */  
  170.     createInterceptor : function(fcn, scope){   
  171.         if(typeof fcn != "function"){   
  172.             return this;   
  173.         }   
  174.         var method = this;   
  175.         return function() {   
  176.             fcn.target = this;   
  177.             fcn.method = method;   
  178.             if(fcn.apply(scope || this || window, arguments) === false){   
  179.                 return;   
  180.             }   
  181.             return method.apply(this || window, arguments);   
  182.         };   
  183.     }   
  184. });   
  185.   
  186. </SPAN>  
/*** @ Function类*这些方法对于每个Function对象都有效(任何一个js函数)。*/
Ext.apply(Function.prototype, {/*该方法用来创建一个可以传递参数的回调函数。
*该方法可以直接在任何一个function上调用。
*例如:<code>myFunction.createCallback(arg1, arg2)</code>,这样就创建了一个绑定了
*两个参数的方法。*如果回调函数需要特定的作用域,请用createDelegate()方法替代。*createCallback()方法返回的函数总是在window作用域(顶级作用域)中执行。
*如果你想给一个回调方法传递参数,则需要使用该方法。如果不需要参数,你可以直
*接简单地传递一个函数的引用给需要回调的地方就可以了(例如callback: myFn)。(译者*注:这句话的意思就是说,如果你不需要向回调函数传递参数,就没有必要使用*createCallback()这个方法,直接按照常规的方式写就可以了)。
*然而,(译者注:按照常规的写法的话),如果你尝试向回调函数传递参数,(例如callback: *myFn(arg1, arg2))。那么函数在解析的时候就会被简单地执行。(译者注:而不是你期
*望的在发生某件事情之后再来回调。)*createCallback()的示例用法如下:<pre><code>
var sayHi = function(name){alert('Hi, ' + name);
}// clicking the button alerts "Hi, Fred"
new Ext.Button({text: 'Say Hi',renderTo: Ext.getBody(),handler: sayHi.createCallback('Fred')
});
</code></pre>*@返回值 {Function} 新的回调函数。*/
createCallback : function(/*args...*/){
//使得传递进来的参数在下面的function中可用。(译者注:这里实际上是返回了//一个闭包函数,然后使用window来调用原来的函数,并把需要的参数传递进去。)var args = arguments;var method = this;return function() {return method.apply(window, args);};},/*创建一个代理(回调)函数,把作用域设置到参数obj上。*该函数可以直接再任何函数上调用。例如:<code>this.myFunction.createDelegate(this, *[arg1, arg2])</code>,这将创建一函数并自动把作用域设置到obj上,这样的话,新的*callback函数中的this属性指向obj对象。*用法示例:* <pre><code>
var sayHi = function(name){
//注意这里的this.text用法。这个函数期望在一个包含text属性的作用域中//执行。在这个例子中,这个’this’属性指向了下面传入createDelegate方法//的btn对象。alert('Hi, ' + name + '. You clicked the "' + this.text + '" button.');
}var btn = new Ext.Button({text: 'Say Hi',renderTo: Ext.getBody()
});//回调函数将在button实例的作用域中执行。
//点击按钮,将弹出"Hi, Fred. You clicked the ‘Say Hi’ button."btn.on('click', sayHi.createDelegate(btn, ['Fred']));
</code></pre>
* @参数1 {Object} obj (可选) 需要设置的目标作用域对象。* @参数2 {Array} args (可选) 覆盖默认的调用参数。(默认为调用者传递进来的参数。)* @参数3 {Boolean/Number} appendArgs (可选) 如果为true,(第二个参数)args被
*添加到调用参数中而不是覆盖。如果为数字,(第二个参数)args将被插入到指定的*位置。* @返回值 {Function} 新的函数*/createDelegate : function(obj, args, appendArgs){var method = this;return function() {var callArgs = args || arguments;if(appendArgs === true){callArgs = Array.prototype.slice.call(arguments, 0);callArgs = callArgs.concat(args);}else if(typeof appendArgs == "number"){callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments firstvar applyArgs = [appendArgs, 0].concat(args); // create method call paramsArray.prototype.splice.apply(callArgs, applyArgs); // splice them in}return method.apply(obj || window, callArgs);};},/*在指定的毫秒数之后调用函数,同时可以指定特定的作用域。*用法示例:* <pre><code>
var sayHi = function(name){alert('Hi, ' + name);
}// 立即执行:
sayHi('Fred');//2秒后执行:
sayHi.defer(2000, this, ['Fred']);//有时候这种语法在推迟执行一个匿名的函数的时候非常有用:
(function(){alert('Anonymous');
}).defer(100);
</code></pre>* @参数1 {Number} millis 设置调用setTimeout的毫秒数(如果为0函数立即被调用)。* @参数2 {Object} obj (可选) 需要设置的特定作用域* @参数2 {Array} args (可选) 覆盖默认的调用参数。(默认为调用者传递进来的参数。)* @参数3 {Boolean/Number} appendArgs (可选) 如果为true,(第二个参数)args被
* 添加到调用参数中而不是覆盖。如果为数字,(第二个参数)args将被插入到指定的* 位置。* @返回值 {Number} 定时器id,可以用来清除定时器。*/defer : function(millis, obj, args, appendArgs){var fn = this.createDelegate(obj, args, appendArgs);if(millis){return setTimeout(fn, millis);}fn();return 0;},/*使用原始的函数和传递进来的函数来创建一个组合的函数调用顺序(译者注:这是一*种类似管道式的调用效果,使用第一个函数的参数来调用第二个函数)。*最终执行的函数返回原始函数的结果。*使用原始函数的参数来调用传递进来的函数。*示例用法:* <pre><code>
var sayHi = function(name){alert('Hi, ' + name);
}sayHi('Fred'); //弹出 "Hi, Fred"var sayGoodbye = sayHi.createSequence(function(name){alert('Bye, ' + name);
});sayGoodbye('Fred'); //两个alert都会弹出
</code></pre>* @参数1 {Function} fcn 需要顺序执行的函数*@参数2 {Object} scope (optional) 传递进来函数的作用域 (默认作用域为原始函数的*作用域或window)* @返回值 {Function}  新的函数
*/createSequence : function(fcn, scope){if(typeof fcn != "function"){return this;}var method = this;return function() {var retval = method.apply(this || window, arguments);fcn.apply(scope || this || window, arguments);return retval;};},/*创建一个拦截函数。传入的函数在原始的函数之前执行。如果传入的函数返回false,*就不执行原始的函数。最终函数返回原始函数的执行结果。传递进来的函数使用原始*函数的参数来调用。*用法示例:* <pre><code>
var sayHi = function(name){alert('Hi, ' + name);
}sayHi('Fred'); // 弹出 "Hi, Fred"//创建一个新的函数来校验输入,而不直接去修改原始函数。
var sayHiToFriend = sayHi.createInterceptor(function(name){return name == 'Brian';
});sayHiToFriend('Fred');   //没有弹出
sayHiToFriend('Brian');  // 弹出"Hi, Brian"
</code></pre>* @参数1 {Function} fcn 需要在原始函数调用之前被调用的函数*@参数2 {Object} scope (可选) 拦截器的作用域(默认为原始函数的作用域或者window)* @返回值 {Function} 新的函数*/createInterceptor : function(fcn, scope){if(typeof fcn != "function"){return this;}var method = this;return function() {fcn.target = this;fcn.method = method;if(fcn.apply(scope || this || window, arguments) === false){return;}return method.apply(this || window, arguments);};}
});

 


总结 :

         从以上的源码分析可以看出,对原生 Function 类的扩展全部使用了闭包。里面模拟了高级语言里面才有的代理、拦截器 。知道 Java 里面 Spring 的人对这里的代理模式 应该不陌生,知道 Struts2 的人应该很熟悉拦截器 的概念,后面还有模板 (XTemplate) ,相信 C++fans 读到的时候肯定会很兴奋。一点小小的感慨:对于编程,编程的思想和算法 是可以超越语言、甚至超越所谓的架构而存在的。如果我们能够把这些模式、这些思想烂熟于胸,使用起来收放自如,又何必去在乎何种语言,何必去争论所谓的 C/S 还是 B/S

         我相信,通过仔细阅读 Ext 的源代码,必定能对那些耳熟能详的所谓模式和思想有更进一层的体会。

         Ext 对原生对象的扩展到这里结束。

(今天早上找了一个 Easy CHM ,想把翻译的这些东西压缩成帮助文档,结果搞出来样子非常丑,希望哪位善于制作 CHM 的高手能给我一点帮助,能达到以下这种效果就成了,Ext提供的HTML式的效果就是这样的。这个图片是网上流传比较多的一份 Ext2.0CHM 文档,没有翻译,好多东西还有错误在里面,后期有时间我想来尝试一遍阅读 2.2 的源码一边翻译这份 API 。练练 E 文,也算帮助一下他人 ^_^ )。


         如你觉得自己 E 文还行,愿意提供帮助,可加 qun88403922 参与

这篇关于《仔仔细细分析Ext》 Ext对Function类的扩展的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/587389

相关文章

Nginx分布式部署流程分析

《Nginx分布式部署流程分析》文章介绍Nginx在分布式部署中的反向代理和负载均衡作用,用于分发请求、减轻服务器压力及解决session共享问题,涵盖配置方法、策略及Java项目应用,并提及分布式事... 目录分布式部署NginxJava中的代理代理分为正向代理和反向代理正向代理反向代理Nginx应用场景

Redis中的有序集合zset从使用到原理分析

《Redis中的有序集合zset从使用到原理分析》Redis有序集合(zset)是字符串与分值的有序映射,通过跳跃表和哈希表结合实现高效有序性管理,适用于排行榜、延迟队列等场景,其时间复杂度低,内存占... 目录开篇:排行榜背后的秘密一、zset的基本使用1.1 常用命令1.2 Java客户端示例二、zse

Redis中的AOF原理及分析

《Redis中的AOF原理及分析》Redis的AOF通过记录所有写操作命令实现持久化,支持always/everysec/no三种同步策略,重写机制优化文件体积,与RDB结合可平衡数据安全与恢复效率... 目录开篇:从日记本到AOF一、AOF的基本执行流程1. 命令执行与记录2. AOF重写机制二、AOF的

MyBatis Plus大数据量查询慢原因分析及解决

《MyBatisPlus大数据量查询慢原因分析及解决》大数据量查询慢常因全表扫描、分页不当、索引缺失、内存占用高及ORM开销,优化措施包括分页查询、流式读取、SQL优化、批处理、多数据源、结果集二次... 目录大数据量查询慢的常见原因优化方案高级方案配置调优监控与诊断总结大数据量查询慢的常见原因MyBAT

分析 Java Stream 的 peek使用实践与副作用处理方案

《分析JavaStream的peek使用实践与副作用处理方案》StreamAPI的peek操作是中间操作,用于观察元素但不终止流,其副作用风险包括线程安全、顺序混乱及性能问题,合理使用场景有限... 目录一、peek 操作的本质:有状态的中间操作二、副作用的定义与风险场景1. 并行流下的线程安全问题2. 顺

MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决

《MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决》MyBatis默认开启一级缓存,同一事务中循环调用查询方法时会重复使用缓存数据,导致获取的序列主键值均为1,... 目录问题原因解决办法如果是存储过程总结问题myBATis有如下代码获取序列作为主键IdMappe

Java中最全最基础的IO流概述和简介案例分析

《Java中最全最基础的IO流概述和简介案例分析》JavaIO流用于程序与外部设备的数据交互,分为字节流(InputStream/OutputStream)和字符流(Reader/Writer),处理... 目录IO流简介IO是什么应用场景IO流的分类流的超类类型字节文件流应用简介核心API文件输出流应用文

Android 缓存日志Logcat导出与分析最佳实践

《Android缓存日志Logcat导出与分析最佳实践》本文全面介绍AndroidLogcat缓存日志的导出与分析方法,涵盖按进程、缓冲区类型及日志级别过滤,自动化工具使用,常见问题解决方案和最佳实... 目录android 缓存日志(Logcat)导出与分析全攻略为什么要导出缓存日志?按需过滤导出1. 按

Linux中的HTTPS协议原理分析

《Linux中的HTTPS协议原理分析》文章解释了HTTPS的必要性:HTTP明文传输易被篡改和劫持,HTTPS通过非对称加密协商对称密钥、CA证书认证和混合加密机制,有效防范中间人攻击,保障通信安全... 目录一、什么是加密和解密?二、为什么需要加密?三、常见的加密方式3.1 对称加密3.2非对称加密四、

MySQL中读写分离方案对比分析与选型建议

《MySQL中读写分离方案对比分析与选型建议》MySQL读写分离是提升数据库可用性和性能的常见手段,本文将围绕现实生产环境中常见的几种读写分离模式进行系统对比,希望对大家有所帮助... 目录一、问题背景介绍二、多种解决方案对比2.1 原生mysql主从复制2.2 Proxy层中间件:ProxySQL2.3