美文网首页
backbone事件原理

backbone事件原理

作者: juceyjing | 来源:发表于2018-03-06 19:32 被阅读19次

backbone Events
backbone的Events是一个对象,其中的方法(on\listenTo\off\stopListening\once\listenToOnce\trigger)都是对象方法。
总体上,backbone的Events实现了监听/触发/解除对自己对象本身的事件,也可以让一个对象监听/解除监听另外一个对象的事件。
绑定对象自身的监听事件on
on绑定有三种方式,可以通过对传入参数的不同在进行不同的处理。
监听其他对象的事件listenTo
backbone支持监听其他对象的文件
B对象上面发生b事件的时候,通知A调用回调函数
A.listenTo(B,'b',callback);

这和B监听自己的事件,并且在回调函数的时候把上下文变成A差不多。
首先A有一个A._listeningTo属性,这个属性是一个对象,存放着他监听一个对象的信息。

on && listenTo 区别

控制反转
A.listenTo(B,'b',callback);
B对象之前控制的事件,现在交由A事件进行控制。

listenTo 比 on的好的方式在于可以一次性进行解绑。

var view={
    dosomething:function(some){
//        ...
    }
}
model.on('change:some',view.dosomething,view);
model2.on('change:some',view.dosomething,view);

//on解绑
    model.off('change:some',view.dosomething,view);
    model2.off('change:some',view.dosomething,view);
//  listenTo
view.listenTo(model,'change:some',view.dosomething);
view.listenTo(model2,'change:some',view.dosomething);
//listenTo解绑
view.stopListening();

解除绑定事件off 、stopListening
与on不同,off的三个参数都是可选的
如果没有任何参数,off相当于把对应的_events对象整体清空
如果有name参数但是没有具体指定哪个callback的时候,则把这个name(事件)对应的回调队列全部清空
如果还有进一步详细的callback和context,那么这个时候移除回调函数非常严格,必须要求上下文和原来函数完全一致
A监听了B点一个事件,意味着
A._listenTo中就会多一个条目,存储这个监听事件的信息。
B._listener也会多一个条目。

触发trigger函数 ---的实现原理?未解析

// Trigger one or many events, firing all bound callbacks. Callbacks are
  // passed the same arguments as `trigger` is, apart from the event name
  // (unless you're listening on `"all"`, which will cause your callback to
  // receive the true name of the event as the first argument).
  Events.trigger =  function(name) {
    if (!this._events) return this;

    var length = Math.max(0, arguments.length - 1);
    var args = Array(length);
    for (var i = 0; i < length; i++) args[i] = arguments[i + 1];

    eventsApi(triggerApi, this._events, name, void 0, args);
    return this;
  };
  // Handles triggering the appropriate event callbacks.
  var triggerApi = function(objEvents, name, cb, args) {
    if (objEvents) {
      var events = objEvents[name];
      var allEvents = objEvents.all;
      if (events && allEvents) allEvents = allEvents.slice();
      if (events) triggerEvents(events, args);
      if (allEvents) triggerEvents(allEvents, [name].concat(args));
    }
    return objEvents;
  };
  // A difficult-to-believe, but optimized internal dispatch function for
  // triggering events. Tries to keep the usual cases speedy (most internal
  // Backbone events have 3 arguments).
  var triggerEvents = function(events, args) {
    var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2];
    switch (args.length) {
      case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return;
      case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return;
      case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return;
      case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return;
      default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return;
    }
  };

setTimeout()异步调用,js运行环境为单线程,setTimeout注册的函数需要等待线程空闲才能执行,此时for循环已经结束,
为5,将setTimeout放到函数立即调用的表达式中,将值作为参数传递给包裹函数。

for(var i=0;i<5;++i){
    setTimeout(function(){
        console.log(i)
    },100)  
}
  for(var i=0;i<5;++i){
    (function(i){
        setTimeout(function(){
            console.log(i)
    },500)}(i)) 
}

如何判断一个对象是否为数组

  Array.isArray(data)
    Object.prototype.toString.call(data)
    function isArray(arg){
        if(typeof arg==='object'){
            return Object.prototype.toString.call(arg)==='[object Array]'
}
return false;
    }

回调函数的例子:

var friends = ["Mike", "Stacy", "Andy", "Rick"];
friends.forEach(function (eachName, index) {
    console.log(index+1+'.'+eachName);
})

这个匿名函数当做参数传递给了forEach()方法,我们像对待对象一样对待函数。
当我们将一个回调函数作为参数传递给另一个函数时,我们仅仅传递了函数的定义。
并没有在参数中执行函数。需要注意的是回调函数并不会马上执行,他会在包含他的函数内的某个时间点被回调。

$('#btn1').click(function(){
    alert('btn 1 Clicked!')
})

这个匿名函数会在函数体内被调用。

回调函数时闭包

我们将一个回调函数作为变量传递给一个函数时,这个回调在函数的某个时间段执行,好像是包含在他的函数定义中的一样。
这意味这回调函数本质上来讲是一个闭包。
正如闭包可以访问父级作用域一样,回调函数可以能获取包含他函数的变量以及全局变量。

实现回调函数的基本原理

执行前确保回调函数时一个函数 typeof =='function'

jsonp实现

1.jsonp是一种跨域通信的手段:

1.利用script标签的src属性来实现跨域
2.通过将前端方法作为参数传递到服务器端,然后由服务器端注入参数后再返回,实现服务端向客户端通信。
3.由于使用script标签的src属性,因此只支持get方法

2.实现流程

1.设定一个script标签

function jsonp(res){
var script=document.createElement("script");
var url=url+"?callback="+res.callback.name
script.src=url;}
jsonp({
    url:"xxx",
    data:'',
    callback:fn
})

jsonp现在不常用,用core配合服务端更多一些;
浅拷贝的方法有两个:
1.var new_arr=arr.slice();
2.var new_arr=arr.concat();

深拷贝
var new_arr = JSON.parse(JSON.stringify(arr));

     function extend() {
        // 默认不进行深拷贝
        var deep = false;
        var name, options, src, copy, clone, copyIsArray;
        var length = arguments.length;
        // 记录要复制的对象的下标
        var i = 1;
        // 第一个参数不传布尔值的情况下,target 默认是第一个参数
        var target = arguments[0] || {};
        // 如果第一个参数是布尔值,第二个参数是 target
        if (typeof target == 'boolean') {
            deep = target;
            target = arguments[i] || {};
            i++;
        }
        // 如果target不是对象,我们是无法进行复制的,所以设为 {}
        if (typeof target !== "object" && !isFunction(target)) {
            target = {};
        }

        // 循环遍历要复制的对象们
        for (; i < length; i++) {
            // 获取当前对象
            options = arguments[i];
            // 要求不能为空 避免 extend(a,,b) 这种情况
            if (options != null) {
                for (name in options) {
                    // 目标属性值
                    src = target[name];
                    // 要复制的对象的属性值
                    copy = options[name];

                    // 解决循环引用
                    if (target === copy) {
                        continue;
                    }

                    // 要递归的对象必须是 plainObject 或者数组
                    if (deep && copy && (isPlainObject(copy) ||
                            (copyIsArray = Array.isArray(copy)))) {
                        // 要复制的对象属性值类型需要与目标属性值相同
                        if (copyIsArray) {
                            copyIsArray = false;
                            clone = src && Array.isArray(src) ? src : [];

                        } else {
                            clone = src && isPlainObject(src) ? src : {};
                        }

                        target[name] = extend(deep, clone, copy);

                    } else if (copy !== undefined) {
                        target[name] = copy;
                    }
                }
            }
        }

        return target;
    };
//    自定义事件(通过观察者模式)
function EventTarget () {
    this.handlers = {};
}

    EventTarget.prototype = {
        constructor: EventTarget,
        addHandler: function (type, handler) {
            if (typeof this.handlers[type] == 'undefined') {
                this.handlers[type] = [];
            }
            this.handlers[type].push(handler)
        },
        fire: function (event) {
            if (!event.target) {
                event.target = this;
            }
            if (this.handlers[event.type] instanceof Array) {
                var handlers = this.handlers[event.type];
                for (var i = 0, len = handlers.length; i < len; i++) {
                    handlers[i](event);
                }
            }
        },
        removeHandler: function (type, handler) {
            if (this.handlers[type] instanceof Array) {
                var handlers = this.handlers[type];
                for (var i = 0, len = handlers.length; i < len; i++) {
                    if (handlers[i] === handler) {
                        break;
                    }
                }
                handlers.splice(i, 1);
            }
        }
    }

相关文章

网友评论

      本文标题:backbone事件原理

      本文链接:https://www.haomeiwen.com/subject/qqihnxtx.html