zoukankan      html  css  js  c++  java
  • 读Ext之三(原型扩展)

    续上篇,

    Ext.ns = Ext.namespace;  
    

    有了一个简写的namespace。整个匿名函数执行完了。接下来

    Ext.ns("Ext.util", "Ext.lib", "Ext.data");  
    Ext.elCache = {};  
    

    分别为Ext添加了util,lib,data,elCache属性,默认都是空的对象。

    Ext.apply(Function.prototype, {
    	createInterceptor : function(){
    		// ...
    	},
    	createCallback : function(){
    		// ...
    	},
    	createDelegate : function(){
    		// ...
    	},
    	defer : function(){
    		// ...
    	}
    });
    

    Ext.apply在第一篇已经讲到,这里用来扩展Function,为其增加了四个方法createInterceptor、createCallback、createDelegate、defer。
    众多流行的JS库都不同程度的“污染”了原生JS,最典型的如Prototype ,Mootools 。JQuery则完全例外,一个匿名函数执行后便诞生了集所有API为一身的强大 $ .
    虽然如此,JQuery的事件对象则不是DOM中事件对象,它完全重写了一个兼容IE,DOM3的事件对象。这里的污染加了双引号,不赞同的就理解成扩展吧。

     

    createInterceptor 方法

    createInterceptor : function(fcn, scope){
        var method = this;
        return !Ext.isFunction(fcn) ?
                this :
                function() {
                    var me = this,
                        args = arguments;
                    fcn.target = me;
                    fcn.method = method;
                    return (fcn.apply(scope || me || window, args) !== false) ?
                            method.apply(me || window, args) :
                            null;
                };
    },
    

    Interceptor顾名思义,拦截器。它却不是Strust2中的拦截器,但还是有部分相似之处。这里是利用所传函数fcn拦截,如果fcn返回false,将被拦截,true才执行。

    var method = this,因为是给Function.prototype扩展,因此其prototype上挂的所有的函数,函数内的this都是Function,即函数自身。示例可能更容易理解

    Function.prototype.test = function(){
    	alert(this);
    };
    function fn(){return 'test';}
    fn.test();
    

    给Function.prototype添加了一个test方法,定义函数fn,fn会自动继承test方法,fn.test()时弹出的this可以看到就是fn自身。

    !Ext.isFunction(fcn),这个条件表明如果所传参数fcn不是一个function,那么将直接返回this,this即函数自身。或者说这时没有进行任何拦截,原样返回了该函数自身。

    当fcn为一个function时,将执行 “:”后的分支,此时给fcn添加了两个属性target,method。target是me,me是this。


    此时的this是什么呢?多数情况下是window,但整个function如果作为对象属性存在时,this则是该对象。谁调用了createInterceptor,method就是谁。如:

    function oldFn(){
    	alert('test');
    }
    function ret(){
    	return false;
    }
    var newFn = oldFn.createInterceptor(ret);
    newFn();
    

    oldFn继承了createInterceptor方法,且调用了它,参数是ret。这时createInterceptor内部的method, fcn.method就是oldFn;me,fcn.target则为window对象。改成如下,me,fcn.target则为obj了。

     

    function oldFn(){
    	alert('test');
    }
    function ret(){
    	return false;
    }
    var obj = {name:'jack'};
    obj.method = oldFn.createInterceptor(ret);
    obj.method();
    

     

    再往下看
    (fcn.apply(scope || me || window, args) !== false),所传参数fcn被执行,执行上下文优先是scope,其次是me,最后是window。返回结果不等于false才执行method。method执行上下文先为me,me不存在则是window。

    整个createInterceptor方法就看完了,下面是一个具体例子:

    function sayName(name){
    	alert('hello, ' + name);
    }
    function rule(name){
    	return name == 'snandy';
    }
    var sayName2 = sayName.createInterceptor(rule);
    sayName2('zhangsan'); // -> no alert
    sayName2('snandy');   // -> "hello, snandy"
    

    createCallback 方法

    createCallback : function(/*args...*/){
        // make args available, in function below
        var args = arguments,
            method = this;
        return function() {
            return method.apply(window, args);
        };
    },

    这个方法非常有用,实现简单。返回一个新函数,新函数内执行method(method是谁就不用再提了吧),会把外面的参数给传进来。
    初学者经常 纠结于给事件handler传参数 。createCallback 解决了给DOM事件handler(监听器)传参问题。
    createCallback 不仅用在DOM事件handler上你完全可以自定义事件,设计一套属于自己的 观察者模式 API。即每个类有自己的事件,如 LTMaps ,除了拥有属性,方法还有许多事件,如移动地图(move),缩放地图(zoom)。Ext的众多UI组件也都是这种模式,Ext.Panel 具有afterlayout,close等事件。

    在给这些事件添加hanlder,又想传参或许也会用到 createCallback。

    再看createDelegate方法,创建一个代理函数

    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 (Ext.isNumber(appendArgs)){
                callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
                var applyArgs = [appendArgs, 0].concat(args); // create method call params
                Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
            }
            return method.apply(obj || window, callArgs);
        };
    },
    

    createDelegate 比 createCallback 更强大,除了能解决回调函数传参问题。还能控制:
    1, 自定义参数是否覆盖默认参数(如 DOM事件对象 作为handler第一个参数)
    2, 自定义参数的位置

    内部实现:取自身(var method=this),返回一个新function,该function调用自身(method.apply),同时指定上下文(obj||window)及参数(callArgs)。就这么简单,细节之处在于对参数的控制。

    1,只传obj和args时将覆盖回调函数默认参数(DOM事件对象)

    <a href="#" id="aa">SINA</a>
    <script type="text/javascript">
    	var aa = document.getElementById('aa');
    	function sayName(name){
    		alert('hello, ' + name);
    	}
    	var sayName2 = sayName.createDelegate(aa,['jack']);
    	aa.onclick = sayName2;
    </script>
    

    2,三个参数都传,appendArgs为true时,默认参数(DOM事件对象)位置不变(第一个),自定义参数args在最后

    <a href="#" id="aa">SINA</a>
    <script type="text/javascript">
    	var aa = document.getElementById('aa');
    	function sayName(){		
    		alert('实际参数长度:' + arguments.length);
    		alert('hello, ' + arguments[0]);
    		alert('hi, ' + arguments[1]);
    	}
    	var sayName2 = sayName.createDelegate(aa,['jack'],true);
    	aa.onclick = sayName2;
    </script>
    

    3, 三个参数都传,appendArgs为数字时将指定自定义参数的位置

    <a href="#" id="aa">SINA</a>
    <script type="text/javascript">
    	var aa = document.getElementById('aa');
    	function sayName(name){		
    		alert('实际参数长度:' + arguments.length);
    		alert('hello, ' + arguments[0]);
    		alert('hi, '+ arguments[1]);
    		alert('hi, '+ arguments[2]);
    	}
    	var sayName2 = sayName.createDelegate(aa,['jack','lily'],0);
    	aa.onclick = sayName2;
    </script>
    

    此外,method的执行上下文应该增加this,这样实用一些了。

    return method.apply(obj || this || window, callArgs);
    

    接着看defer,指定函数在多少毫秒后执行

    defer : function(millis, obj, args, appendArgs){
        var fn = this.createDelegate(obj, args, appendArgs);
        if(millis > 0){
            return setTimeout(fn, millis);
        }
        fn();
        return 0;
    }

    内部实用了刚刚提到的createDelegate方法,指定函数的执行上下文及参数设定。如果millis是正数则延迟执行返回setTimeout的返回值(一个数字),有必要可用clearTimeout终止。否则立即执行,返回0。

    好了,以上是对Function.prototype的扩展。接下来是对String,Array的扩展。使用Ext.applyIf,第一篇 已提到Ext.applyIf不会覆盖已有的方法。

    Ext.applyIf(String, {
        format : function(format){
            var args = Ext.toArray(arguments, 1);
            return format.replace(/\{(\d+)\}/g, function(m, i){
                return args[i];
            });
        }
    });
    

     

    给String添加静态方法(类方法)format,可以把字符串中特殊写法({0},{1})用指定的变量替换。如

    var href = 'http://www.sina.com.cn', text = '新浪';
    var s = String.format('<a href="{0}">{1}</a>', href, text);
    alert(s); // --> <a href="http://www.sina.com.cn">新浪</a>
    

    有点类似于JSP的EL表达式。

     

    Array扩展,

    Ext.applyIf(Array.prototype, {
        indexOf : function(o, from){
            var len = this.length;
            from = from || 0;
            from += (from < 0) ? len : 0;
            for (; from < len; ++from){
                if(this[from] === o){
                    return from;
                }
            }
            return -1;
        },
        remove : function(o){
            var index = this.indexOf(o);
            if(index != -1){
                this.splice(index, 1);
            }
            return this;
        }
    });
    

    indexOf 再熟悉不过了吧,String早有了。用来查找元素是否在数组中,如果有则返回该元素在数组中的索引,否则返回-1。该方法在 ECMAScript 5 中已经引入,各浏览器的新版本都实现了。

    reomve 删除指定元素,如果存在该元素则删除,返回数组自身。

    好了,以上3篇是整个Ext-core的Ext.js。

     

  • 相关阅读:
    持续集成之Jenkins+sonar自动化部署04
    自动化部署03
    自动化部署02
    自动化部署01
    持续集成之代码质量管理-Sonar
    gitlab-ce安装01
    在Centos7.3安装Zabbix3.4.4服务端
    自动化运维之日志系统上线规范(十)
    自动化运维之日志系统Logstash实践(九)
    自动化运维之日志系统Logstash解耦实践(八)
  • 原文地址:https://www.cnblogs.com/snandy/p/2468750.html
Copyright © 2011-2022 走看看