📜 ⬆️ ⬇️

Browser Pattern and Javascript Call Context

Although about the pattern "Observer (Observer, Observer)" said quite a lot, including on Habré, I will briefly repeat it. The essence of the pattern is in monitoring the state of certain subjects of the system and the corresponding reaction of observers to changes in these states. Several observers can follow a single subject, and he himself does not know about it (weak binding), but regularly notifies everyone about a change of state.

It is convenient to use the Browser on websites and web applications, so it is logical to implement it using the most popular language for the web environment - Javascript.

Observable = function() {  this.observers = []; } Observable.prototype.deliver = function(data) {  for (var i in this.observers) {   this.observers[i](data);  } } Function.prototype.subscribe = function(observable) {  observable.observers.push(this);  return this; } 

It's simple. The Observable class is the subject. The array of observers - subscribers, observers. The deliver method notifies observers of a state change. Subscribe signs the observer. In practice, it looks like this:

 myClass = function() { this.value = 0; //  this.onChange = new Observable(); //  } myClass.prototype.change = function(new_value) { this.value = new_value; this.onChange.deliver(this.value); //  —   } var c = new myClass(); var write_log = function(value) { console.log(value); } write_log.subscribe(c.onChange); 

A remarkably working example: with each change of c.value, the observed onChange will notify all observers of the change and notify them of the new state with which the observers will manage in their own way. So, for example, write_log () will print a new state in the console. But this example is remarkable only until it becomes necessary to operate with this in the executable function.
')
So, for example, the following construction will not work as it should:

 myClass = function() { this.value = 0; this.onChange = new Observable(); } myClass.prototype.change = function(new_value) { this.value = new_value; this.onChange.deliver(this.value); } Logger = function(logtype) { this.type = (!!logtype) ? logtype : "alert"; } Logger.prototype.write = function(value) { if (this.type == "console") { console.log(value); return; } alert(value); } var c = new myClass(); var logger = new Logger("console"); logger.write.subscribe(c.onChange); 

A problem will arise in accessing this when executing logger.write. Let me remind you that this is the context, and in this case, the context will not be an instance of the Logger class, but the nameless function () that was called during the execution of deliver.

Solve the problem helps a great function call, which not only executes a method, but also allows you to specify the execution context. Therefore, I slightly rewrote the observable - deliver method and, accordingly, changed the subscription mechanism.

 Observable = function() { //  this.observers = []; } Observable.prototype.deliver =function(data) { for (var i in this.observers) {  this.observers[i].func.call(this.observers[i].context, data); //      } } Function.prototype.subscribe = function(observable, context) { var ctx = context || this; //    ,    this «-»,     var observer = { //   ,        context: ctx,  func: this } observable.observers.push(observer); return this; } 

Thus, in order to earn an extreme example, you only need to specify the calling context in the subscription - a Logger instance:

 var logger = new Logger("console"); logger.write.subscribe(c.onChange, logger); 

It works! And I hope this little trick will be useful to someone.

Source: https://habr.com/ru/post/131166/


All Articles