JavaScript实现自定义对象的自定义事件

时间:2022-01-27 19:30:20

前言:

大家都知道,在使用JavaScript可以很方便的使用addEventListener函数给DOM对象快速绑定一个或多个事件侦听器。

我们又如何在JavaScript的自定义对象中使用此方法并触发事件呢?这就是本章节的核心内容了。


目的:

现在有一个需求,要求“a对象”能够让“b对象“做一系列动作。

分析后我们得知。首先,”b对象“有一个固定名称作为入口让a对象调用,并且这个入口可以自动检索所有符合这个动作要求的函数并依次触发。


实现:

好的,经过以上内容我们可以简单的了解到为什么我们需要自定义事件了。

接下来我们来用代码具体实现一下。

function CursomObject (table) {
    /// <summary>这是一个自定义对象类型</summary>
    /// <param name="table" type="Object" optional="true">要添加的函数及属性表</param>

    // 这里要存放我们的自定义事件
    // 因为是一个表,所以我们使用Object类型
    this._events = {};

    // 得到函数及属性表中的内容
    for (var i in table) this[i] = table[i];
}

上面这段代码定义了一个CursomObject类型,这将作为下面所做操作的基础。

CursomObject.prototype.addEventListener = function (type, listener, capture) {
    /// <summary>添加事件侦听器</summary>
    /// <param name="type" type="String">事件类型</param>
    /// <param name="listener" type="Function">触发的函数</param>
    /// <param name="capture" type="Boolean" optional="true">是否在捕获阶段触发(这里只是做了顺序排列)</param>

    // 判断一下传入的参数是否符合规格
    if (typeof type !== "string" || typeof listener !== "function") return this;

    // 缓存符合条件的事件列表
    var list = this._events[type];

    // 判断是否已经有该类型事件,若没有则添加一个新数组
    if (typeof list === "undefined") list = (this._events[type] = []);

    /* 判断插入函数的位置 */
    if (!!capture) list.push(listener);
    else list.insert(0, listener);

    return this;
};
至此,我们已经实现了一个简单的事件监听器的添加功能了。

这里需要注意一下,list.insert中的insert函数在JavaScript的Array中是不存在的,我们将在下面的扩展函数中找到。

另外,return this; 则是为了方便链式编程【详情请参阅”附录1“】

CursomObject.prototype.removeEventListener = function (type, listener, capture) {
    /// <summary>移除事件侦听器</summary>
    /// <param name="type" type="String">事件名称</param>
    /// <param name="listener" type="Function">触发的函数</param>
    /// <param name="capture" type="Boolean">是否在捕获阶段触发</param>

    // 判断一下传入的参数是否符合规格
    if (typeof type !== "string" || typeof listener !== "function") return this;

    // 缓存符合条件的事件列表
    var list = this._events[type];

    // 若没有绑定过此类事件则不需要做处理
    if (typeof list === "undefined") return this;

    for (var i = 0, len = list.length; i < len; i++) {
        // 通过循环判断来确定事件列表中存在要移除的事件侦听函数
        if (list[i] == listener) {
            // 找到后将此侦听函数从事件列表中移除
            list.remove(i);
            break;
        }
    }
    return this;
};
以上过程简单明了,一个循环搞定问题。

同时这个也需要注意,JavaScript中的Array中是不存在remove函数的,同样在下面扩展函数中会出现。

CursomObject.prototype.fireEvent = function (type, e) {
    /// <summary>触发事件</summary>
    /// <param name="type" type="String">事件名称</param>
    /// <param name="e" type="Object">附加参数对象</param>

    // 若存在DOM0用法的函数,则触发
    this["on" + type.toLowerCase()] && this["on" + type.toLowerCase()].call(this, e);

    // 缓存符合条件的事件列表
    var list = this._events[type];

    // 若事件列表中没有内容则不需要做处理
    if (!list || list.length <= 0) return this;

    // 阻止事件冒泡开关
    var isStop = false;

    // 模拟事件对象
    window.event = { stopPropagation: function () { isStop = true; } };
    e.stopPropagation = window.event.stopPropagation;

    for (var i = 0, len = list.length; i < len; i++) {
        // 通过循环触发符条件的事件列表中存在的所有事件侦听函数
        // 若函数内返回false或事件内调用了event.stopPropagation函数则阻止接下来的所有调用
        if (list[i].call(this, e) === false || isStop) break;
    }
    return this;
};
注释已经说的很清楚了,这里就不再累述。

那么至此就主体就已经完成了。下面放出扩展函数。

Array.prototype.insert = function (index, value) {
    /// <summary>插入项</summary>
    /// <param name="index" type="Number">索引</param>
    /// <param name="value" type="Object">元素</param>
    /// <returns type="Array" />

    if (index > this.length) index = this.length;
    if (index < -this.length) index = 0;
    if (index < 0) index = this.length + index;
    for (var i = this.length; i > index; i--) {
        this[i] = this[i - 1];
    }
    this[index] = value;
    return this;
};

Array.prototype.remove = function (index) {
    /// <summary>移除项</summary>
    /// <param name="index" type="Number">索引</param>
    /// <returns type="Array" />

    if (isNaN(index) || index > this.length) return;
    this.splice(index, 1);
};
OK,已经全部完成了。

下面放出完整代码。

function CursomObject(table) {
    /// <summary>这是一个自定义对象类型</summary>
    /// <param name="table" type="Object" optional="true">要添加的函数及属性表</param>

    // 这里要存放我们的自定义事件
    // 因为是一个表,所以我们使用Object类型
    this._events = {};

    // 得到函数及属性表中的内容
    for (var i in table) this[i] = table[i];
}

CursomObject.prototype.addEventListener = function (type, listener, capture) {
    /// <summary>添加事件侦听器</summary>
    /// <param name="type" type="String">事件类型</param>
    /// <param name="listener" type="Function">触发的函数</param>
    /// <param name="capture" type="Boolean" optional="true">是否在捕获阶段触发(这里只是做了顺序排列)</param>

    // 判断一下传入的参数是否符合规格
    if (typeof type !== "string" || typeof listener !== "function") return;

    // 缓存符合条件的事件列表
    var list = this._events[type];

    // 判断是否已经有该类型事件,若没有则添加一个新数组
    if (typeof list === "undefined") list = (this._events[type] = []);

    /* 判断插入函数的位置 */
    if (!!capture) list.push(listener);
    else list.insert(0, listener);

    return this;
};

CursomObject.prototype.removeEventListener = function (type, listener, capture) {
    /// <summary>移除事件侦听器</summary>
    /// <param name="type" type="String">事件名称</param>
    /// <param name="listener" type="Function">触发的函数</param>
    /// <param name="capture" type="Boolean">是否在捕获阶段触发</param>

    // 判断一下传入的参数是否符合规格
    if (typeof type !== "string" || typeof listener !== "function") return this;

    // 缓存符合条件的事件列表
    var list = this._events[type];

    // 若没有绑定过此类事件则不需要做处理
    if (typeof list === "undefined") return this;

    for (var i = 0, len = list.length; i < len; i++) {
        // 通过循环判断来确定事件列表中存在要移除的事件侦听函数
        if (list[i] == listener) {
            // 找到后将此侦听函数从事件列表中移除
            list.remove(i);
            break;
        }
    }
    return this;
};

CursomObject.prototype.fireEvent = function (type, e) {
    /// <summary>触发事件</summary>
    /// <param name="type" type="String">事件名称</param>
    /// <param name="e" type="Object">附加参数对象</param>

    // 若存在DOM0用法的函数,则触发
    this["on" + type.toLowerCase()] && this["on" + type.toLowerCase()].call(this, e);

    // 缓存符合条件的事件列表
    var list = this._events[type];

    // 若事件列表中没有内容则不需要做处理
    if (!list || list.length <= 0) return this;

    // 阻止事件冒泡开关
    var isStop = false;

    // 模拟事件对象
    window.event = { stopPropagation: function () { isStop = true; } };
    e.stopPropagation = window.event.stopPropagation;

    for (var i = 0, len = list.length; i < len; i++) {
        // 通过循环触发符条件的事件列表中存在的所有事件侦听函数
        // 若函数内返回false或事件内调用了event.stopPropagation函数则阻止接下来的所有调用
        if (list[i].call(this, e) === false || isStop) break;
    }
    return this;
};

Array.prototype.insert = function (index, value) {
    /// <summary>插入项</summary>
    /// <param name="index" type="Number">索引</param>
    /// <param name="value" type="Object">元素</param>
    /// <returns type="Array" />

    if (index > this.length) index = this.length;
    if (index < -this.length) index = 0;
    if (index < 0) index = this.length + index;
    for (var i = this.length; i > index; i--) {
        this[i] = this[i - 1];
    }
    this[index] = value;
    return this;
};

Array.prototype.remove = function (index) {
    /// <summary>移除项</summary>
    /// <param name="index" type="Number">索引</param>
    /// <returns type="Array" />

    if (isNaN(index) || index > this.length) return;
    this.splice(index, 1);
};


使用:

上面已经详述了自定义事件的具体实现。

接下来我们就可以使用了,那么怎么使用呢?

function Test() {
    /// <summary>这是一个自定义类型</summary>

    // 继承CursomObject中的属性
    CursomObject.apply(this);
}
// 继承CursomObject中的函数
Test.prototype = new CursomObject();

// 定义一个从Test类型中派生出来的对象
var a = new Test();

// 绑定一个message事件的侦听器
a.addEventListener("message", function (e) {
    alert(e);
}, false);

// 再绑定一个message事件的侦听器
a.addEventListener("message", function (e) {
    alert("内容:" + e);
}, false);

// 触发message事件
a.fireEvent("message", "这是参数……");

至此结束。

如果疑问请留言,我将及时回复。

谢谢您的观看!