• 关于js模拟c#的Delegate(委托)实现


    这是我的第一篇博文,想来讲一讲js的函数。我的标题是js模拟c#的Delegate。

    一、什么是Delegate(委托)

    在jquery中有delegate函数,作用是将某个dom元素的标签的事件委托给一个函数队列,在触发这个事件的时候会触发这个函数队列中的所有函数。而c#中的Delegate对象也是如此,将多个方法添加至一个委托对象,或称将多个方法委托给一个委托对象,当调用委托对象的Invoke方法时会调用所有被委托的方法。由此可以看出Delegate的本质应该是一个函数队列,执行委托对象就是遍历执行函数队列。

    二、实现委托构造函数

    明白了委托对象的本质是一个函数队列,就可以着手建立委托对象了。先写一个简单的委托的构造函数,这个构造函数返回的对象实现以下功能

    1、add();                             添加函数

    2、remove();                        移除函数

    3、();                                  调用委托

     1 function SimpleDelegate() {
     2     var _fnQuene = [];
     3     var delegate=function(){
     4         for(var i=0;i<_fnQuene.length;i++){
     5             _fnQuene[i].apply(this,arguments);
     6         }
     7     }
     8     delegate.add = function (fn) {
     9         //只有函数才能被添加到函数队列
    10         if(Object.prototype.toString.call(fn)==='[object Function]'){
    11             _fnQuene.push(fn);
    12         }
    13     }
    14     delegate.remove = function (fn) {
    15         _fnQuene.splice(_fnQuene.indexOf(fn), 1);
    16     }
    17     return delegate;
    18 }

    这个函数已经能实现基本的添加移除和调用,如果使用规范,这个函数没有任何问题。但如果使用不规范,会造成严重的后果,比如下面的代码。在后面的代码中执行的时候会不断添加函数去调用add函数,这样一来就造成了死循环。

     1 //正确的使用委托
     2 var d1=new SimpleDelegate();
     3 d1.add(function(){
     4     console.log(1);
     5 })
     6 d1.add(function(){
     7     console.log(2);
     8 })
     9 d1();   //打印1,2
    10 
    11 //不规范使用,造成死循环
    12 var d2=new SimpleDelegate();
    13 d2.add(function(){
    14     d2.add(function(){
    15         console.log(1);
    16     })
    17 })
    18 d2();

    我们需要修改这个函数,首先我们发现在调用委托的时候会获取_fnQuene的length值,而这个length是不断在变化的,每添加一个函数长度就会增加,于是就产生了循环调用add造成死循环的问题,如果我们在调用前获取length的值并缓存起来就不用担心添加的问题了

    1 var delegate = function () {
    2         var len = _fnQuene.length;
    3         for (var i = 0; i < len; i++) {
    4             _fnQuene[i].apply(this, arguments);
    5         }
    6     }

    执行结果变成如下,不再死循环

    var d2 = new SimpleDelegate();
    d2.add(function () {
        console.log(1);
        d2.add(function () {
            console.log(2);
        })
    })
    d2();   //第一次执行打印1
    d2();   //第二次执行打印1,2
    d2();   //第三次执行打印1,2,2

    但这样还是不够,我们只解决了add的问题,如果在函数中调用remove呢,这样好像缓存length也不行

     1 var d=new SimpleDelegate();
     2 function f1(){
     3     console.log(1);
     4     d.remove(f2);
     5 }
     6 function f2(){
     7     console.log(2);
     8 }
     9 d.add(f1);
    10 d.add(f2)
    11 d();        //执行到索引为1的函数,函数已经被删除,报错

    add和remove函数并不靠谱,我们应该修改的是Delegate的机制,之前,我们调用add和remove会直接将操作直接作用在fnQuen上,但当我们开始执行委托后就不应该对fnQuene进行操作。如果添加的函数对fnQuene进行操作,应当把操作缓存下来,并在下一次调用委托之前执行这些操作,并在执行后立刻删除这些操作。如此一想,为什么我们不把每一次操作都缓存下来呢?每次委托执行前添加和移除,委托一旦开始执行,调用的add和remove函数又会缓存相应的操作。

     1 function SimpleDelegate() {
     2     var _fnQuene = [];
     3     var _waitingQuene = [];
     4 
     5     var delegate = function () {
     6         var len = _waitingQuene.length; //缓存本次执行委托时已有的操作数量
     7         var i;
     8         //首先调用所有缓存的操作
     9         for (i = 0; i < len; i++) {
    10             _waitingQuene[i]();
    11         }
    12 
    13         len = _fnQuene.length;    //缓存当前函数队列的长度
    14         for (i = 0; i < len; i++) {
    15             _fnQuene[i].apply(this, arguments);
    16         }
    17     }
    18 
    19     delegate.add = function (fn) {
    20         _waitingQuene.push(function(){
    21             _fnQuene.push(fn);
    22         })
    23     }
    24 
    25     delegate.remove = function (fn) {
    26         _waitingQuene.push(function(){
    27             _fnQuene.splice(_fnQuene.indexOf(fn),1);
    28         })
    29     }
    30     return delegate;
    31 }

    最后为了这个函数使用更加方便,添加链式编程并支持一些重载,以及为对象添加一些属性

     1 function cloneArray(arr) {
     2     var ret = [];
     3     //这里不用map是因为arr可以是类数组对象
     4     for (var i = 0; i < arr.length; i++) {
     5         ret[i] = arr[i];
     6     }
     7     return ret;
     8 }
     9 
    10 function Delegate() {
    11     var _fnQuene = [];
    12     var _waitingQuene = [];
    13 
    14     var delegate = function () {
    15         var len = _waitingQuene.length; //缓存本次执行委托时已有的操作数量
    16         var i;
    17         //首先调用所有缓存的操作
    18         for (i = 0; i < len; i++) {
    19             _waitingQuene[i]();
    20         }
    21         _waitingQuene.length = 0;
    22 
    23         len = _fnQuene.length;    //缓存当前函数队列的长度
    24         for (i = 0; i < len; i++) {
    25             _fnQuene[i].apply(this, delegate._argIntercept ?
    26                 cloneArray(arguments) :
    27                 arguments
    28             );
    29         }
    30     }
    31 
    32     delegate.add = function (fn) {
    33         var args = arguments;
    34         var arg;
    35         var type;
    36         var self = this;
    37         function add() {
    38             for (var i = 0; i < args.length; i++) {
    39                 arg = args[i];
    40                 type = Object.prototype.toString.call(arg);
    41                 if (type === '[object Array]') {
    42                     add.apply(self, arg);
    43                 }
    44                 else if (type === '[object Function]') {
    45                     _fnQuene.push(arg);
    46                 }
    47             }
    48         }
    49         _waitingQuene.push(add);
    50         return this;
    51     }
    52 
    53     delegate.remove = function (fn) {
    54         var args = arguments;
    55         var arg;
    56         var type;
    57         var self = this;
    58         function remove() {
    59             for (var i = 0; i < args.length; i++) {
    60                 arg = args[i];
    61                 type = Object.prototype.toString.call(arg);
    62                 if (type === '[object Array]') {
    63                     remove.apply(self, arg);
    64                 }
    65                 else if (type === '[object Function]') {
    66                     var idx = _fnQuene.indexOf(arg);
    67                     if (idx === -1) {
    68                         continue;
    69                     }
    70                     _fnQuene.splice(idx, 1);
    71                 }
    72             }
    73         }
    74         _waitingQuene.push(remove);
    75         return this;
    76     }
    77 
    78     //检查某个函数是否委托给了当前委托对象
    79     delegate.has = function (fn) {
    80         return _fnQuene.indexOf(fn) !== -1;
    81     }
    82 
    83     Object.defineProperties(delegate, {
    84         //委托中函数的数量
    85         _length: {
    86             get: function () {
    87                 return _fnQuene.length;
    88             }
    89         },
    90 
    91         //是否拦截参数,如果为true,则委托被调用时传给函数的参数为副本
    92         _argIntercept: {
    93             value: false,
    94         }
    95     })
    96 
    97     delegate.constructor = Delegate;
    98     return delegate;
    99 }
    
    
  • 相关阅读:
    细节问题
    慕课 python 操作数据库
    转 Python爬虫入门七之正则表达式
    转 python面试题
    转 Perl函数返回值用法指导
    慕课爬虫实战 爬取百度百科Python词条相关1000个页面数据
    慕课爬虫
    转 Python爬虫入门五之URLError异常处理
    转 廖雪峰 urllib
    转 Python爬虫入门四之Urllib库的高级用法
  • 原文地址:https://www.cnblogs.com/kazetotori/p/5842379.html
Copyright © 2020-2023  润新知