窥伺zepto的事宜模块

写在前面

经由过程本文,您能够相识zepto的事宜模块,$(selector).on以后究竟发生了什么样的事变,怎样完成的事宜托付【$(selector).delegate】,怎样完成的自定义事宜等题目。由于篇幅有限,移除事宜部份,代码没有贴出,人人能够看这里(完整版)。先来看下悉数API(略去了off等移除事宜要领):

《窥伺zepto的事宜模块》

$.proxy

$.proxy(fn, context),用于转变函数的上下文,完成柯里化,有点类似于js原生的bind要领,支撑两种传入参数体式格局:

  • fn为函数,context为对象,将fn内的this指向context

  • fn为对象,context为字符串,将挪用fncontext要领

重要应用的apply来完成:

  // isString,isFunction为自定义的检测范例要领,完成很简单
  // (obj) => {}.toString.call(obj) === ["object String"]
  // isFunction也是一样要领即可
  $.proxy = function(fn, context) {
    var args = (2 in arguments) && slice.call(arguments, 2)
    if (isFunction(fn)) {
      // 应用apply挪用,将上下文指定为context
      // 同时完成了柯里化
      var proxyFn = function(){ 
          return fn.apply(
              context, 
              args ? 
                  args.concat(slice.call(arguments)) :             
                  arguments
          ); 
      }
      proxyFn._zid = zid(fn)
      return proxyFn
    } else if (isString(context)) {
      if (args) {
        args.unshift(fn[context], fn)
        return $.proxy.apply(null, args)
      } else {
        return $.proxy(fn[context], fn)
      }
    } else {
      throw new TypeError("expected function")
    }
  };

on

on被增加到了$.fn上,也就是$.prototype上,如许一来,每个zepto都邑有该要领,on要领非常症结,one,live,delegate,bind等要领能够视为on要领的语法糖。在来看on要领,起首来看一下add和几个东西要领:

function add(element, events, fn, data, selector, delegator, capture){
    // zid要领用于给element对象设置唯一id值或许掏出element对应的id
    var id = zid(element),
        set = handlers[id] || (handlers[id] = []);
    // 多个事宜传入时,应用空格来举行星散
    events.split(/\s/).forEach(function(event) {
        if (event === "ready") 
            return $(document).ready(fn);
        // zepto支撑事宜的定名空间,parse用来解析出定名空间和事宜名
        var handler = parse(event);
        handler.fn = fn;
        handler.sel = selector;
        if (handler.e in hover) {
            fn = function(e) {
                var related = e.relatedTarget;
                // relatedTarget属性仅对mouseout,mouseover有用
                // mouseover而言,是指移入目标节点前脱离的谁人节点
                // mouseout而言,是指移出目标节点进入的谁人节点
                // e.relatedTarget即是目标节点且该节点不是目标节点的子节点
                // 则证实该事宜已触发完成,能够实行响应的事宜处置惩罚函数
                // $.contain是查询this中是不是包括related
                if (
                    !related || 
                    (this !== related &&
                    !$.contains(this, related))
                ) {
                    return handler.fn.apply(this, arguments);
                }
            };
        }
        handler.del = delegator;
        var callback = delegator || fn;
        handler.proxy = function(e) {
            // 给e增加属性
            e = compatible(e);
            if (e.isImmediatePropagationStopped()) {
                return void 0;
            }
            // 将data挂载到event对象上
            e.data = data;
            // return false => 举行阻挠默许事宜与事宜冒泡
            // callback函数绑定在了element上
            // _args仅仅在trigger和triggerHandler体式格局挪用时才有
            var result = callback.apply(element, e._args == null ? [e] : [e].concat(e._args));
            if (result === false) {
                e.preventDefault();
                e.stopPropagation();
            }
            return result;
        };
        // 标记handler位置,轻易删除
        handler.i = set.length;
        set.push(handler);
        // 增加事宜
        // realEvent返回可行的事宜名,由于zepto中会尝试
        // 将mouseenter转为mouseover,mouseleave转为mouseout
        // 将focus转为focusin,blur转为focusout
        if ("addEventListener" in element) {
            element.addEventListener(
                realEvent(event), 
                handler.proxy, 
                eventCapture(handler, capture)
            );
        }
    });
}

须要注重几点:

  • zepto会应用compatible给事宜对象e增加三个要领:

    • isDefaultPrevented是不是实行了preventDefault来阻挠默许事宜

    • isPropagationStopped是不是实行了stopPropagation来阻挠冒泡

    • isImmediatePropagationStopped是不是实行了stopImmediatePropagation,阻挠冒泡的同时阻挠该元素上其他事宜的实行

  • e对象有两种状况:

    • 原生的事宜对象

    • 举行事宜托付时,zepto给原生的设置一个代办对象,该对象由createProxy要领天生

  • handlers能够明白为一个缓存池,经由过程zid会对每个元素天生一个唯一id值,每个handlers[id]对应一个数组对象,该数组中存储着的是每个在这个元素上绑定的事宜及其实行函数等信息。(个人明白这么做目标在于便于移除事宜,如许就能够运用匿名函数,而且仍能够将该匿名函数移除)。

  • addEventListener的第三个参数决议了事宜实行的阶段

    • 默许为false,也就是在冒泡阶段来实行,

    • true示意在捕捉阶段实行

    eventCapture要领来肯定事宜在哪一个阶段实行,关于事宜托付的状况来讲,在捕捉节点时实行,而关于focusblur由于不支撑事宜冒泡,所以只好应用事宜捕捉来模仿事宜冒泡

明白了add后,再看来on要领:

// 绑定事宜
// event能够是对象,也但是字符串
// 只传入event,data,callback时,为bind要领
// 只传入event,selector,callback时为delegate or live
// 传入了one参数时,则为one要领
L.fn.on = function(event, selector, data, callback, one) {
    var autoRemove, 
        delegator, 
        $this = this;
    // 倘使传入的event为对象,key为事宜名,fn为实行函数
    if (event && !L.isString(event)) {
        $.each(event, function(type, fn) {
            $this.on(type, selector, data, fn, one);
        });
        return $this; 
    }
    // 假如未传入selector的状况,bind就是这类状况
    if (!isString(selector) && !isFunction(callback) && callback !== false) {
        callback = data;
        data = selector;
        selector = undefined;            
    }    
    // data未传入,将callback设为data对应的值
    if (callback === undefined || data === false) {
        callback = data;
        data = undefined;
    }
    // 传入false为处置惩罚函数,默许为阻挠默许事宜,阻挠冒泡
    if (callback === false) callback = returnFalse;
    return $this.each(function(_, element) {
        if (one) {
            autoRemove = function(e) {
                // one为绑定的事宜函数实行一次后,就移除该事宜
                remove(element, e.type, callback);
                return callback.apply(this, arguments);
            };
        }
        if (selector) {
            // 事宜托付完成
            delegator = function(e) {
                var evt,
                    match = L(e.target).closest(selector, element).get(0);
                if (match && match !== element) {
                    // 天生代办事宜对象,事宜托付中传入实行函数的event对象并非原生事宜对象
                    evt = $.extend(createProxy(e), {
                        currentTarget: match,
                        liveFired: element
                    });
                    return (autoRemove || callback).apply(
                        match, 
                        [evt].concat(slice.call(arguments, 1))
                    );
                }
            };
        }
        add(element, event, callback, data, selector, delegator || autoRemove);
    });
};

bind要领是on的一个语法糖,其只接受了三个参数,event, data, callback,同理delegate,live,one等也是一样,只是应用差别的传参数体式格局来挪用on要领。

自定义事宜

应用$.Event能够建立而且初始化一个DOM事宜:

// 建立自定义事宜
// bubbles,默许为true,冒泡时处置惩罚
L.Event = function(type, props) {
    if (!L.isString(type)) {
        props = type;
        type = props.type;
    }
    var event = document.createEvent(specialEvents[type] || "Events"),
        bubbles = true;
    if (props) {
        for (var name in props) {
            if (name === "bubbles") {
                bubbles = !!props[name];
            } else {
                event[name] = bubbles[name];
            }
        }
    }
    // initEvent初始化事宜对象
    // 事宜范例,是不是冒泡,是不是能够阻挠默许事宜
    event.initEvent(type, bubbles, true);
    return compatible(event);
};

应用trigger能够触发事宜,trigger还支撑传入参数:

// 触发事宜,zepto的触发事宜只能作用于DOM上
L.fn.trigger = function(event, args) {
    event = (L.isString(event) || L.isPlainObject(event)) ? L.Event(event) : compatible(event);
    event._args = args;
    return this.each(function() {
        if (event.type in focus && typeof this[event.type] === "function") {
            this[event.type]();
        } else if ("dispatchEvent" in this) {  // 浏览器原生触发事宜API
            // 默许采纳浏览器原生要领来触发自定义事宜
            this.dispatchEvent(event);
        } else {
            // 倘使不支撑,则挪用triggerHandler
            // 此要领不会冒泡,只会在当前元素上触发
            // 完成是:依据对应事宜筛选出其实行函数,挪用其实行函数
            $(this).triggerHandler(event, args);
        }
    });
};

简化

运用zepto时,关于click等事宜来讲并不必举行bind而是直接挪用$().click()就好:

("focusin focusout focus blur load resize scroll unload click dblclick " +
  "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
  "change select keydown keypress keyup error").split(" ").forEach(function(event) {
    // 传参数为注册,不传为触发
    L.fn[event] = function(callback) {
      return (arguments.length === 1) ?
        this.bind(event, callback) :
        this.trigger(event)
    }
});
    原文作者:zp1996
    原文地址: https://segmentfault.com/a/1190000007841462
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞