原生 Function 类:
参考手册并没有过多介绍 JS 内建的 Function 对象,只是介绍了如何用 new Function(name,arg…); 这样的方法来创建一个新的函数。
另外,通过其它资料我们还可以了解到,每个被定义的 function 都有 length 属性,它描述这个 function 定义时的实际参数个数。另一个属性就是原型: prototype ,当一个 function 被调用的时候, js 引擎负责创建 function 的 prototype 属性。
有可能内建的 Function 类本身没有什么其它实际可用的属性或方法;另一种可能是 js 不想暴露关于 Function 类的过多细节。因为如果这样做可能带来过度的复杂性。
Ext 对 Function 的扩展
Ext 为 Function 扩展了五个实例方法: createCallback() 、 createDelegate() 、 defer() 、 createSequence() 、 createInterceptor() 。
也就是说,在使用 Ext 框架时,对于每个自定义的函数,都可以直接调用这些方法。
下面来看源码(注释都已翻译成中文)。
Js代码/*** @ Function类*这些方法对于每个Function对象都有效(任何一个js函数)。*/ Ext.apply(Function.prototype, {/*该方法用来创建一个可以传递参数的回调函数。 *该方法可以直接在任何一个function上调用。 *例如:<code&ateCallback(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: Body(),handler: ateCallback('Fred') }); </code></pre>*@返回值 {Function} 新的回调函数。*/ createCallback : function(/**/){ //使得传递进来的参数在下面的function中可用。(译者注:这里实际上是返回了//一个闭包函数,然后使用window来调用原来的函数,并把需要的参数传递进去。)var args = arguments;var method = this;return function() {return method.apply(window, args);};},/*创建一个代理(回调)函数,把作用域设置到参数obj上。*该函数可以直接再任何函数上调用。例如:<code&ateDelegate(this, *[arg1, arg2])</code>,这将创建一函数并自动把作用域设置到obj上,这样的话,新的*callback函数中的this属性指向obj对象。*用法示例:* <pre><code> var sayHi = function(name){ //注意这里的用法。这个函数期望在一个包含text属性的作用域中//执行。在这个例子中,这个’this’属性指向了下面传入createDelegate方法//的btn对象。alert('Hi, ' + name + '. You clicked the "' + + '" button.'); }var btn = new Ext.Button({text: 'Say Hi',renderTo: Body() });//回调函数将在button实例的作用域中执行。 //点击按钮,将弹出"Hi, Fred. You clicked the ‘Say Hi’ button.('click', ateDelegate(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 = at(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 = ateDelegate(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 = ateSequence(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 = ateInterceptor(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 = hod = 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.0 的 CHM 文档,没有翻译,好多东西还有错误在里面,后期有时间我想来尝试一遍阅读 2.2 的源码一边翻译这份 API 。练练 E 文,也算帮助一下他人 ^_^ )。
如你觉得自己 E 文还行,愿意提供帮助,可加 qun : 88403922 参与
本文发布于:2024-01-30 13:14:13,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170659165420258.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |