• JavaScipt 源码解析 回调函数


    函数是第一类对象,这是javascript中的一个重要的概念,意味着函数可以像对象一样按照第一类管理被使用,所以在javascript中的函数:

    能"存储"在变量中,能作为函数的实参被传递,能在函数中被创建,能从函数中返回

    jquery中回调的设计

    异步回调:

    事件句柄回调:

    $(document).ready(callback);

    $(document).on('click',callback);

    Ajax异步请求成功失败回调

    $.ajax({

      url:"",

      context:document

    }).done(function(){

      //成功执行

    }).fail(function(){

      //失败执行

    });

    动画执行完毕回调

    $("#a").click(function(){

      $("#b").animate({

        left:'+=50',

        opacity:0.25,

        height:'toggle'

      },5000,function(){

      这里是回调

    });

    });

    同步回调

    一个同步(阻塞)中使用回调的例子,目的是在test代码执行完成后执行回调callback

    var test1 = function(callback){

      //执行长时间操作

      callback();

    }

    test1(function(){

      //执行回调中的方法

    });

    所以理解回调函数最重要的2点:

    1,一个回调函数作为参数传递给另一个函数时,我们仅仅传递了函数定义,我们并没有在参数中执行函数,我们并不传递我们平时执行函数一样带有一对执行小括号的函数。

    2,回调函数并不会马上被执行,他会在包含它的函数内的某个特定的时间点被"回调"。

    回调的运用

    我们经常会这样使用函数回调:

    时间触发通知,资源加载通知,定时器延时,ajax,动画通知等等。

    例1:

    jquery针对Dom的处理提供了append,prepend,before,after等方法的处理,这几个方法的特征:

    1,参数的传递可以是html字符串,DOM元素,元素数组或者jquery对象。

    2,为了优化性能针对节点的处理需要生成文档碎片。

    高层接口

    before:function(){

      return this.domManip(arguments,function(elem){

      if(this.parentNode){

        this.parentNode.insertBefore(elem,this);

    }

    })

    },

    after:function(){

      return this.domManip(arguments,function(elem){

      if(this.parentNode){

        this.partentNode.insertBefore(elem,this.nextSibling);

      }

    });

    }

    底层实现

    domMainp:function(args,callback){

      args = concat.apply([],args);

      if(isFunction || self.doManip(args,callback));  

      if(l){

        //生成文档碎片

        fragment = jquery.buildFragment(args,this[0].ownerDocument,false,this);

        callback.call(this[i],node,i);

      }

      return this;

    }

    例子2

    function Aaron(List, callback) {
        setTimeout(function() {
            var task;
            if (task = List.shift()) {
                task(); //执行函数
            }
            if (List.length > 0) { //递归分解
                arguments.callee(List)
            } else {
                callback()
            }
        }, 25)
    }
    
    //调用
    ​Aaron([
        function() {
            alert('a')
        },
        function() {
            alert('b')
        },
        function() {
            alert('c')
        }
    ], function() {
        alert('callback')
    })
    
    // 分别弹出 ‘a’ , ‘b’ ,'c',’callback
    传入一组函数参数,靠递归解析,分个执行,其实就时靠setTimeout可以把函数加入到队列末尾才执行的原理,聚合对象完全是一个整体,无法再次细分出来,所以我们需要一种方案,用来管理分离每一个独立的对象。
    换成jquery提供的方式
    var callbacks = $.Callbacks();
    callbacks.add(function(){alert(1)};);
    callbacks.add(function(){alert(2)};);
    callbacks.fire();//1 2

    模式的实际运用
    $.ajax({
      url:"",
      context:document.body
    }).done(function(data){
      $("").html(data.a);
    });
    优化
    $.ajax({
      url:"",
      context:document.body
    }).done(function(data){
      pocessData();
      pocessHtml();
      pocessOther();
    });
    function pocessData(){
      //处理数据
    }
    function pocessHtml(){
      $().html(data.a);
    }
    function pocessOther(){
      //处理其他逻辑
    }
    优化的好处是 分离出各种的业务函数,从而降低了代码之间的耦合度, 但是这样代码写法达不到代码复用的作用
    用观察者模式
    Observable.add(function(){
      //pocessdata
    });
    Observable.add(function(){
      $().html(data.a);
    })
    Observable.add(function(){
      //pocessOther
    })

    $.ajax({
      url:"",
      context:document.body
    }).done(function(data){
      Observable.fire(data);
    })
    jquery回调对象
    jquery.Callbacks是在1.7版本后加入的,主要用来进行函数队列的add,remove,fire,lock等操作, 并提供once,momory,unique,stopOnFalse四个option进行一些特殊的控制

    $.Callbacks是一个工厂函数,使用函数调用(非new,他不是一个类),他又一个可选参数flags用来设置回调函数的行为,对外的接口也就是self的返回。
    callbacks.add():回调列表中添加一个回调或回调的集合。
    callbacks.disable():禁用回调列表中的回调
    callbacks.disabled():确定回调列表是否已被禁用
    callbacks.empty():从列表中删除所有的回调
    callbacks.fire():用给定的参数调用所有的回调
    callbacks.fired():访问给定的上下文和参数列表中的所有回调
    callbacks.fireWith():访问给定的上下文和参数列表中的所有回调
    callbacks.has():确定列表中是否提供一个回调
    callbacks.lock():锁定当前状态的回调列表
    callbacks.locked():确定回调列表是否已被锁定
    callbacks.remove():从回调列表中的删除一个回调或回调集合。

    源码结构
    jQuery.Callbacks = function(options){
      options = typeof options === "string" ? (optionsCache[options] || createOptions(options)): jQuery.extend({},option);
      //实现代码
      fire = function(){}
      self = {
        add : function(){},
        remove : function(){},
        has : function(){},
        empty : function(){},
        disable : funciton(){},
        disabled : function(){},
        lock:function(){},
        locked:function(){},
        fireWith:function(){},
        fire:function(){}
      };
      return self;
    };
    整个结构要分为三个部分:options参数缓存,内部fire触发器的设计,外部。
    参数的缓存设计:Callbacks是可以接受字符串的组合参数,可以使用空格分割
    var opts = 'unique memory';
    var object = {}
    jQuery.each(opts.match(/S+/g) || [], function(_, flag) {
      object[flag] = true;
    });
    

    这样的操作其实是不需要重复的,所以我们可以设计一个缓存池,用来储存重复的操作:

    var optionsCache = {};
    function createOptions(options) {
      var object = optionsCache[options] = {};
      jQuery.each(options.match(rnotwhite) || [], function(_, flag) {
        object[flag] = true;
      });
      return object;
    }

    所以我们传递参数的时候,如果参数是字符串,我们可以直接从optionsCache缓存中去查找:

    options = typeof options === "string" ?
            ( optionsCache[ options ] || createOptions( options ) ) :
            jQuery.extend( {}, options );


    默认回调对象设计
    function Callbacks(){
      var list = [];
      var self;
      self = {
        add:function(fn){
          list.push(fn);  
          },
          fire:function(args){
            list.foreach(function(fn){
            fn(args);
          })
        }  
      }
      return self;
    }

    once的设计
    once的作用确保回调列表只执行(.fire())一次(像一个递延Deferrend)
    function fn1(val){
      console.info(1);
    }
    var cbs = $.Callbacks("once");
    cbs.add(fn1);
    cbs.fire("foo");
    cbs.fire("foo");
    cbs只会执行一次,once定义是很明确的,确保这个回调列表只执行(.fire())一次,所以针对这种once的处理可以有多种不同的途径实现。
    1,add的时候抛弃
    2,在fire的时候抛弃多个
    但是jquery是在执行第一个fire的时候直接给清空list列表了,然后在add的地方给判断下list是否存在,从而达到这样的处理。
    function Callbacks(options){
      var list = [];
      var self;
      self = {
        add:function(){
          list,push(fn);
        },
        fire:function(args){
          if(list){
            list.forEach(function(fn){
              fn(args);
            })
            if(options === "once"){
              list = undefined;  
            }
          }
        }
      }
      return self;
    }
    在fire之后,判断参数是否为once,直接把list给清理掉,所以之后的所有fire都被抛弃了,从而到达了once的效果。
    jQuery.Callbacks的处理
    在fire中调用了self.disable()方法
    disable:function(){
      list = stack = memory = undefined;
      return this;
    }
    memory的设计
    memory:保持以前的值,将添加到这个列表的后面的最新的值立即执行调用任何回调。
    unique的设计
    unique:确保一次只能添加一个回调
    stopOnFalse
    stopOnFalse:当一个回调返回false时中断调用。



  • 相关阅读:
    verilog编码规范
    verilog代码 想法验证---与寄存器输出有关
    MMCM与PLL
    Vivado约束文件(XDC)的探究(2)
    Vivado约束文件(XDC)的探究(1)
    VGA图像显示组成模块分析
    关于Quad PLL /CPLL参考时钟的选择
    GTX的生成(包括COMMON)
    SD-SDI播出系统---使用GTX TX产生恢复时钟
    DRP端口描述
  • 原文地址:https://www.cnblogs.com/huangyin1213/p/6203382.html
Copyright © 2020-2023  润新知