Javascript Publisher / Subscriber Pattern示例

时间:2013-04-17 03:45:26

标签: javascript asp.net-mvc-4 publish-subscribe

我正在学习发布者/订阅者模式,并做了一个简单的例子来更好地理解它。

var Publisher = {
    subscribers: {},       
    Subscribe: function (event, callback) {
        if (Publisher[event] && typeof callback === "function") {
            if (Publisher.subscribers[event]) {
                Publisher.subscribers[event].callbacks.push(callback);
            }
            else {
                Publisher.subscribers[event] = { callbacks: [callback] };
            }
        }
    },
    Publish: function (event) {
        if (Publisher.subscribers[event].callbacks) {
            for (callback in Publisher.subscribers[event].callbacks) {
                Publisher.subscribers[event].callbacks[callback].apply(null);
            }
        }
    },

    PublishOnClick: function (event, argument) {
        Publisher.Publish(event, argument);
    }
};

var Subscriber1 = {
    initialize: function () {
        if (Publisher.Subscribe) {
            Publisher.Subscribe("PublishOnClick", Subscriber1.Action);
        }
    },
    Action: function (c) {
        document.getElementById('result').innerHTML += "Button Clicked and the count is: " + c;
        c++;
    }
};
Subscriber1.initialize();


var c = 1;
var fld2 = document.getElementById("myfield2");
if (fld2.addEventListener) {
    fld2.addEventListener("click", function() { Publisher.PublishOnClick("PublishOnClick", [c]); }, false);
} else if (fld2.attachEvent) {
    fld2.attachEvent("onclick", function() { Publisher.PublishOnClick("PublishOnClick", [c]); });
}

我有一个按钮和局部视图,我试图通过按钮提升事件并在部分视图上显示计数。我得到了事件rasied并显示在局部视图上,但唯一的问题是计数

1 个答案:

答案 0 :(得分:2)

从我看到的情况来看,你的问题实际上与发布商 - 订阅者范例没有关系。

我认为你的设置有点令人费解......因为从以下内容的角度来看可能更容易理解:

twitter_feed.tweets = [];
twitter_feed.root_el = document.getElementById("tweets");
twitter_feed.update = function (tweet) {
    twitter_feed.tweets.push(tweet);
    // demonstration-purposes only -- please don't write live code like this
    twitter_feed.root_el.innerHTML += "<p>" + tweet.user + ": \"" + tweet.text + "\"</p>";
};


twitter_data.subscribe("new-tweet", twitter_feed.update);

// some event handler which grabs tweets from AJAX or whatever...
twitter_data.notify("new-tweet", { user : "Me", text : "my new tweet" });

目标是发布者不是对象,而是存在可以发布数据的对象,订阅者不是对象,而是可以对已发布事件做出反应的对象的扩展...

...无论如何,你的代码适用于手头的范例......

...除了:

Action : function (c) { /* ... */ c++; }

问题是JS 通过VALUE传递SCALAR数据,而不是通过REFERENCE传递

所以在你的函数中,当它被调用时,你没有通过:

// .Action(c);
Action (reference_to_c) {
    /*...*/.innerHTML = reference_to_c;
    reference_to_c++;
}

你实际上正在通过:

// .Action(c);
Action (1) {
    /*...*/.innerHTML = 1;
    1++;
};

JS中的对象通过引用传递。 “对象”包括函数和数组 非对象标量(number s,string s,boolean s,nullundefinedNaN)按值传递,因此如果你有一个参数是标量的函数参数,编辑函数内部的版本不会改变外面的值。

var x = 1,
    say_X = function (x) { x += 1; console.log(x); };

say_X(x); // 2;
console.log(x); // 1;

// vs
var obj = { x : 1 },
    say_X = function (o) { o.x += 1; console.log(o.x); };

say_X(obj); // 2;
console.log(obj.x); //2;

您真正想要做的是让发布者跟踪计数,或让订阅者跟踪计数,具体取决于这样做的意义。
看看我的Twitter示例,顶部。

twitter_feed跟踪打印到屏幕上的所有推文是有意义的,这样当新推文进来时,它会被添加到列表中。
最新的twitter_data并不关心之前的推文,因此在这种情况下,订阅者将跟踪状态,发布者只会在发生更改时监听更改并通知其他人。

在其他情况下,发布商可能有必要跟踪其状态,并向订阅者宣布新状态:

music_playlist.update_track = function (data) {
     music_playlist.current_song.innerHTML = data.current_song;
};
music_player.subscribe("song-change", music_playlist.update_track);
music_player.notify("song-change", { current_song: "Sympathy for the Daleks" });

在这种情况下,播放列表只关心更新用户在页面上看到的标题 音乐播放器正在处理当前可用的歌曲以及它当前所处的状态,以及所有其他好东西。

因此,您当前问题的解决方案之一是:

  1. c设为发布商的属性,该属性会添加到c并在之后通知其订阅者
  2. 使c属性为Subscriber,每次Publisher通知Subscriber时,它都会添加到自己的c属性,然后将其写出来
  3. c的参数中取Action(看起来像Action : function () { /*...*/ },以便函数内的c指向window.c。 ..sloppy,但有效。
  4. 这三者中的任何一个都应该解决你的问题。