[英]how to implement observer pattern in javascript?
嗨,我很想在 JavaScript 中实现观察者模式:
我的 index.js :
$(document).ready(function () {
var ironMan = new Movie();
ironMan.setTitle('IronMan');
ironMan.setRating('R');
ironMan.setId(1);
// ironMan.setCast(['Robert Downey Jr.', 'Jeff Bridges', 'Gwyneth Paltrow']);
var terminator = new Movie();
terminator.setTitle('Terminator');
terminator.setRating('P');
terminator.setId(2);
console.log(ironMan.toString());
console.log(terminator.toString());
ironMan.play();
ironMan.stop();
ironMan.download();
ironMan.share('V. Rivas');
console.log(ironMan.getCast()[0]);
});
我的电影:
var title;
var rating;
var id;
var observers;
function Movie() {
observers = new ObserverList();
}
//function Movie (title, rating, id){
// this. title = title;
// this.rating = rating;
// this.id =id;
// observers = new ObserverList();
//}
Movie.prototype.setTitle = function (newTitle) {
this.title = newTitle;
}
Movie.prototype.getTilte = function () {
return this.title;
}
Movie.prototype.setRating = function (newRating) {
this.rating = newRating;
}
Movie.prototype.getRating = function () {
return this.rating;
}
Movie.prototype.setId = function (newId) {
this.id = newId;
}
Movie.prototype.getId = function () {
return this.id;
}
Movie.prototype.play = function () {
for (i = 0; i < observers.Count; i++) {
console.log("palying...");
}
}
Movie.prototype.stop = function () {
for (i = 0; i < observers.Count; i++) {
console.log("stoped");
}
}
Movie.prototype.AddObserver = function (observer) {
observers.Add(observer);
};
最后观察者:
function ObserverList() {
this.observerList = [];
}
ObserverList.prototype.Add = function (obj) {
return this.observerList.push(obj);
};
ObserverList.prototype.Empty = function () {
this.observerList = [];
};
ObserverList.prototype.Count = function () {
return this.observerList.length;
};
ObserverList.prototype.Get = function (index) {
if (index > -1 && index < this.observerList.length) {
return this.observerList[index];
}
};
ObserverList.prototype.Insert = function (obj, index) {
var pointer = -1;
if (index === 0) {
this.observerList.unshift(obj);
pointer = index;
} else if (index === this.observerList.length) {
this.observerList.push(obj);
pointer = index;
}
return pointer;
};
你能提供的任何帮助我都会非常感激。
JavaScript 是事件驱动的:这意味着它知道时间并期望事情会随着时间发生变化。 最初的观察者模式是为像 C++ 这样不知道时间的语言创建的。 您可以通过使用游戏循环来检查状态更改来利用 JavaScript 的优势。
创建两个 DOM 元素,一个输入和一个输出
<input type="text" value="Enter some text...">
<p id="output">
设置一个requestAnimationFrame
循环并开始观察。
//Get a reference to the input and output
var input = document.querySelector("input");
var output = document.querySelector("#output");
//Set up a requestAnimationFrame loop
function update () {
requestAnimationFrame(update);
//Change the output to match the input
output.innerHTML = input.value;
}
update();
这就是游戏引擎为即时模式渲染所做的。 这也是 React 框架用来检查 DOM 中状态变化的作用。
(如果你需要,这里有一个简单的 requestAnimationPolyfill)
//Polyfill for requestAnimationFrame
window.requestAnimationFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(/* function */ callback, /* DOMElement */ element){
window.setTimeout(callback, 1000 / 60);
};
})();
在 JavaScript 中,没有必要像在 Java 中那样实现纯观察者模式,因为 JavaScript 有一个叫做函数式编程的小东西。 所以只需使用类似http://api.jquery.com/category/callbacks-object/ 的东西,而不是你的 ObserverList。
如果您仍然想使用您的对象,那么一切都取决于您要传递给 ObserverList.Add 的内容。 如果是某个对象,那么你需要写
for( i = 0; i < observers.Count; i++) {
observers[i].Notify("some data");
}
如果它是一个函数,那么你需要写
for( i = 0; i < observers.Count; i++) {
observers[i]("Some data");
}
你也可以使用Function.apply()或Function.call()提供this
给你的函数
这是 JavaScript 中观察者模式的一个实现,它提供了一个与Backbone Models非常相似的 API。 正如Douglas Crockford所建议的那样,此实现避免使用“this”和“new”。
// The constructor function.
function Model(){
// An object containing callback functions.
// * Keys are property names
// * Values are arrays of callback functions
var callbacks = {},
// An object containing property values.
// * Keys are property names
// * Values are values set on the model
values = {};
// Return the public Model API,
// using the revealing module pattern.
return {
// Gets a value from the model.
get: function(key){
return values[key];
},
// Sets a value on the model and
// invokes callbacks added for the property,
// passing the new value into the callback.
set: function(key, value){
values[key] = value;
if(callbacks[key]){
callbacks[key].forEach(function (callback) {
callback(value);
});
}
},
// Adds a callback that will listen for changes
// to the specified property.
on: function(key, callbackToAdd){
if(!callbacks[key]){
callbacks[key] = [];
}
callbacks[key].push(callbackToAdd);
},
// Removes a callback that listening for changes
// to the specified property.
off: function(key, callbackToRemove){
if(callbacks[key]){
callbacks[key] = callbacks[key].filter(function (callback) {
return callback !== callbackToRemove;
});
}
}
};
}
下面是一些使用模型的示例代码:
// Create a new model.
var model = Model();
// Create callbacks for X and Y properties.
function listenX(x){
// The new value is passed to the callback.
console.log('x changed to ' + x);
}
function listenY(y){
// The new value can be extracted from the model.
console.log('y changed to ' + model.get('y'));
}
// Add callbacks as observers to the model.
model.on('x', listenX);
model.on('y', listenY);
// Set values of X and Y.
model.set('x', 30); // prints "x changed to 30"
model.set('y', 40); // prints "y changed to 40"
// Remove one listener.
model.off('x', listenX);
model.set('x', 360); // prints nothing
model.set('y', 50); // prints "y changed to 40"
class EventObserver {
constructor () {
this.observers = []
}
subscribe (fn) {
this.observers.push(fn)
}
unsubscribe (fn) {
this.observers = this.observers.filter(subscriber => subscriber !== fn)
}
broadcast (data) {
this.observers.forEach(subscriber => subscriber(data))
}
}
或者你可以在 NodeJs 中使用 EventEmitter https://nodejs.org/api/events.html#events_class_eventemitter
下面是我从《学习 Javascript 设计模式》一书中改编的一个实现。
function pubsub(obj) {
var events = {},
subUid = -1;
obj.publish = function (event, args) {
if (!events[event]) {
return false;
}
var subscribers = events[event],
len = subscribers ? subscribers.length : 0;
while (len--) {
subscribers[len].func(event, args);
}
};
obj.subscribe = function (event, func) {
if (!events[event]) {
events[event] = [];
}
var token = (++subUid).toString();
events[event].push({
token: token,
func: func
});
return token;
};
obj.unsubscribe = function (token) {
for (var event in events) {
if (events.hasOwnProperty(event)) {
for (var i = 0, j = events[event].length ; i < j ; i++) {
if (events[event][i].token === token) {
events[event].splice(i, 1);
}
}
}
}
return this;
};
}
var obj = {}; // Any javascript object
pubsub(obj); // Make an observable from the given object
var subscription = obj.subscribe('load', handler);
// event handler callback
function handler(event, data) {
console.log(event, data);
}
obj.publish('load', 'Data loaded successfully'); // logs 'load Data loaded successfully'
obj.unsubscribe(subscription);
obj.publish('load', 'Data loaded successfully'); // nothing happens
干杯!
class Observable { constructor() { this.observer = []; } subscribe(item) { this.observer.push(item); } unsubscribe(item) { if(!this.observer) return 'empty'; else { this.observer.filter(subscribe => subscribe !== item); } } notify(data) { this.observer.forEach(item => item(data)); } } var p1 = document.querySelector('.p1'); var p2 = document.querySelector('.p2'); var p3 = document.querySelector('.p3'); var input = document.querySelector('.input'); const update1 = text => p1.textContent = text; const update2 = text => p2.textContent = text; const update3 = text => p3.textContent = text; var observarble = new Observable(); observarble.subscribe(update1); observarble.subscribe(update2); observarble.subscribe(update3); input.addEventListener('keyup', event => observarble.notify(event.target.value));
<input type="input" class="input" /> <div class="p1"></div> <div class="p2"></div> <div class="p3"></div>
Observer 是在所有 javascript 应用程序中使用的流行模式之一。
实例(主体)维护一组对象(观察者),并在状态发生变化时通知所有对象。
让我们通过写一些逻辑来解释
class Observable {
constructor() {
this.observer = [];
}
subscribe(item) {
this.observer.push(item);
}
unsubscribe(item) {
if(!this.observer) return 'empty';
else {
this.observer.filter(subscribe => subscribe !== item);
}
}
notify(data) {
this.observer.forEach(item => item(data));
}
}
现在你的问题将是下一步是什么?
在哪里实际使用这种模式。
想象一下,当某个事件发生时,您必须同时更新多个元素。
在代码中添加一些 HTML
<input type="input" class="input" />
<div class="p1"></div>
<div class="p2"></div>
<div class="p3"></div>
使用 Javascript 获取这些节点
var p1 = document.querySelector('.p1');
var p2 = document.querySelector('.p2');
var p3 = document.querySelector('.p3');
var input = document.querySelector('.input');
要使用观察者设置值,您需要添加其文本内容
const update1 = text => p1.textContent = text;
const update2 = text => p2.textContent = text;
const update3 = text => p3.textContent = text;
var observarble = new Observable();
observarble.subscribe(update1);
observarble.subscribe(update2);
observarble.subscribe(update3);
最后一件事是附加一个带有输入 keyup/change 的事件监听器
input.addEventListener('keyup', ev => observarble.notify(ev.target.value));
而已 :) !!
对我来说,这是在 JS 中实现观察者模式的最佳方式
function Click() {
this.handlers = []; // observers
}
Click.prototype = {
subscribe: function(fn) {
this.handlers.push(fn);
},
unsubscribe: function(fn) {
this.handlers = this.handlers.filter(
function(item) {
if (item !== fn) {
return item;
}
}
);
},
fire: function(o, thisObj) {
var scope = thisObj || window;
this.handlers.forEach(function(item) {
item.call(scope, o);
});
}
}
// log helper
var log = (function() {
var log = "";
return {
add: function(msg) { log += msg + "\n"; },
show: function() { alert(log); log = ""; }
}
})();
function run() {
var clickHandler = function(item) {
log.add("fired: " + item);
};
var click = new Click();
click.subscribe(clickHandler);
click.fire('event #1');
click.unsubscribe(clickHandler);
click.fire('event #2');
click.subscribe(clickHandler);
click.fire('event #3');
log.show();
}
观察者模式是关于更新一个对象并让这些更新自动发送一个事件,该事件提供有关更新内容的信息。
例如:
function ObserverList(){
this.observerList = []
this.listeners = []
}
ObserverList.prototype.add = function( obj ){
this.observerList.push(obj)
this.listeners.forEach(function(callback){
callback({type:'add', obj:obj})
})
}
ObserverList.prototype.onChange = function(callback){
this.listeners.push(callback)
}
这是javascript中观察者模式的一个模块,您可以查看源代码以获取更多信息: https : //github.com/Tixit/observe
这是旧的,但我想提供原始问题的答案,“如何在给定现有代码的情况下实现观察者模式”。
观察者模式可以简化为一种通信设计,其中目标(被观察的事物)有一个指向观察者的指针,并为观察者假定一个公共 API。 例如,目标假设观察者有一个名为update
的方法,或者观察者是一个Function
。 这是目标通过实际调用观察者对象(如果观察者是Function
则调用Function
上的方法来通知观察者变化的方式。
任何时候属性发生变异或更改时,目标都必须update
所有已注册以接收通知的观察者。
在这里,我假设您想知道如何实现 Key-Value Observer。 在这种情况下,代码必须遍历其观察者列表并在属性更改时调用每个观察者update
方法(或者在它是Function
的情况下仅执行观察者)。
var observers = null; function Movie() { observers = new ObserverList(); } Movie.prototype.changed = function(key, old, value){ // Assumption here is that observers can observe individual properties. if(!this.observers[key]) return this.observers[key].forEach( o => { // Assumption is that observers have an update method. This is the only // thing the target knows about an observer. o.update(key, old, value, this) }) } // Now every setter on the target has to notify the observers by calling `changed` Movie.prototype.setTitle = function (newTitle) { var old = this.title; this.title = newTitle; this.changed("title", old, this.title, this) }
您必须添加该changed
方法,然后更新所有设置方法以调用上述更改的方法。
我还注意到原始代码中没有任何地方可以observes
电影。 根据我上面的示例,您需要添加实际observes
它的代码,实现update
代码。
我不确定play
和stop
的意图是什么。
您可以使用 RxJS,而不是实现您自己的观察者模式,它为您提供了一个Observable
API。
一个非常简单的版本,不依赖于完整的库,但应该允许您将来迁移到整个 RxJS 堆栈,可以编写如下(它不会尝试执行完整和错误回调)
/**
* Basic Observable, made compatible with RX-JS API.
*/
type Observer<T> = {
next: (value: T) => void;
};
interface Subscription {
unsubscribe(): void;
}
export class Observable<T> {
private observers: Observer<T>[] = [];
subscribe(next: (value: T) => void): Subscription {
const observer = { next }
this.observers.push(observer);
return {
unsubscribe: () => {
this.observers = this.observers.filter((h) => h !== observer);
},
};
}
next(data: T) {
this.observers.forEach((h) => h.next(data));
}
}
并测试如下:
import { Observable } from './Observable';
import noop from 'noop-fn';
describe('Observable', () => {
it('can be created', () => {
new Observable();
});
it('can be subscribed to', () => {
const o = new Observable();
const subscription = o.subscribe(noop);
subscription.unsubscribe();
});
it('can receive notifications', () => {
const o = new Observable();
const handler = jest.fn();
const subscription = o.subscribe(handler);
o.next('Hello');
subscription.unsubscribe();
expect(handler.mock.calls.length).toBe(1);
expect(handler.mock.calls[0][0]).toBe('Hello');
});
});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.