📜 ⬆️ ⬇️

Neutrino - a tiny js framework with full inheritance and events

In projects with a small frontend, it is not always wise to use heavy frameworks like backbone, ember or knockout. Nevertheless, the need to use models, inheritance and qualitative interaction between them remains. I bring to your attention the Neutrino framework, which does all of the above, while its size does not exceed 100 lines of code.

Any feedback on the framework is welcome.
A simple demo with inheritance and events can be viewed here .

Class creation


To create a class, call the extend method of the _neutrino.Root object. As an argument, we give it an object that contains the methods and static elements of the future class.

var Model = _n.Root.extend({ //methods and static members }); 

The extend method returns a class constructor.
')
Creating Class Instances

To create an instance of a class, we simply call its constructor with the keyword new and pass only one argument. This argument must be an object containing all the necessary data. The contents of the object will be recorded in the data field of the newly created object.

 var model = new Model({ id: 1 }); alert(model.data.id); //1 


Default values

For any property of the data object, we can set the default value:

 var Model = _n.Root.extend({ defaults: { id: "default id" } }); var model = new Model(); alert(model.data.id); //"default id" 


Constructors

We can set a pseudo-constructor for any class. The pseudo-constructor function is called construct . Its behavior is completely equivalent to the usual constructor — it is called when the object is created, and can return a value that will be returned as the result of the operation of the new operator. In the pseudo-constructor code, the parent method must be called using this._superCall ();

 var Model = _n.Root.extend({ construct: function() { this._superCall(); alert("Hello world"); } }); 


Inheritance


Neutrino supports full inheritance:

Let's look at a simple example:
 var Parent = _n.Root.extend({ defaults: { a: 1, b: 2 }, construct: function() { this._superCall(); alert(this.getSum()); }, getSum: function() { return this.data.a + this.data.b; } }); var Child = Parent.extend({ defaults: { a: 10, c: 3 }, getSum: function() { return this.superCall() + this.data.c; } }); new Parent(); //alert(1 + 2); new Child(); //alert(10 + 2 + 3); 


Call parent methods

From the inside of a method, there are two ways to call the nearest parent method. If you need to call the parent method with the same parameters as the current method was called, you can use this._superCall ();

 ... someMethod: function(a, b, c) { return this._superCall(); //call closest parent method with a,b,c parameters } ... 


If the parent method needs to be called with other parameters, you need to use a direct link to the nearest parent method - this._superMethod;

 ... someMethod: function(a, b, c) { return this._superMethod(a + 1, b + 1, c + 1); //call closest parent method with changed parameters } ... 


In fact, this.superCall (); this is the abbreviated equivalent of this._superMethod.apply (this, arguments) ;

Developments


Events are a distinct way to organize the interaction of models ... Models in Neutrino can generate events and subscribe to events of other models.

Event generation

To generate an event in a model, you need to call its fireEvent method. It needs to pass two parameters - the name of the event is eventName and (optionally) an info object containing any additional information.

  model.fireEvent("item-added", { item: item //some additional information }); 


Handling your own events

When a model generates an event, its own onOwnEvent method is called first , which receives the eventName and info parameters.

  /* Model class initialization*/ ... onOwnEvent: function(eventName, info) { switch(eventName) { case "item-added": //increase items counter //render info.item break; } } ... model.fireEvent("item-added", { item: item }); 


Subscribe to events

To subscribe to an event of a model, you need to call its subscribe method and pass it two parameters — a handler function and the name of the event you want to subscribe to. If you do not specify the name of the event (or pass an asterisk * as the name of the event), your handler will be called when any event is generated, Otherwise, only at the event to which you subscribed.

Naturally, the handler will receive the eventName and info parameters when it is called.

  function commonHandler(eventName, info) { switch(eventName) { case "e1": alert("e1 fired"); break; } } function specificHandler(eventName, info) { alert("e1 fired"); } model.subscribe(commonHandler); model.subscribe(specificHandler, "e1"); model.fireEvent("e1"); //info in handlers will be set to undefined 


Event handling by model methods

Usually, there is a need to handle the event of one model by the method of some other model. To keep this context and possibly some other data relevant at the time of subscription, you can, of course, use a closure:

 /* class initialization stuff */ ... processItem: function(item) { var self = this; item.subscribe(function(eventName, info) { self.onItemEvent(eventName, info, item); }); }, onItemEvent: function(item, eventName, info) { ... } 


However, Neutrino provides an easier way to do this.

 item.subscribe(this.onItemEvent.bind(this, item)); 


A bind () function is attached to each model method in Neutrino, which will call your method with the correct this context and any additional parameters.

A simple example will help clarify understanding:

 var binding = this.onItemEvent.bind(this, 1, 2)); setTimeout(function() { binding(3, 4); //your method onItemEvent will be called with arguments 1, 2, 3, 4. }); 

Using bind () , you first specify the desired context (usually this), then any number of additional parameters (a, b). When we call the result function of the binding with parameters (c, d), then in the end, we come to the call of our method with parameters (a, b, c, d).

Any feedback on the framework is welcome.

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


All Articles