⬆️ ⬇️

OOP in javascript





In this article we will talk about the main features of object-oriented programming in JavaScript:







Objects in JavaScript



An object in JavaScript is an associative array that contains sets of key-value pairs (“hash”, “object”, and “associative array” mean the same thing in JavaScript).

')

Creating an object in javascript:



 var obj = new Object(); //    var obj = {}; //    . 


Setting object properties:



 obj.name = 'Victor'; //  .property obj['name']='Victor'; //    


Appeal to properties:



 console.log(obj.name); //  .property console.log(obj['name']); //        


Advanced option:



 var obj = { name : 'Viktor', age : 32 }; 


Constructor and keyword new



"A constructor is any function that is used as a constructor ." Before the advent of ECMAScript 6 in JavaScript, there was no concept constructor. It could be any function that is called using the new keyword.



An example of using the constructor:



  var Donkey = function(){ //… }; //   «» var iaDonkey = new Donkey(); 


When calling new Donkey (), JavaScript does four things:



  1. 1. Creates a new object:

    iaDonkey = new Object(); // .
  2. 2. Places the properties of the Donkey object constructor:

    aDonkey.constructor == Donkey // true

    iaDonkey instanceof Donkey // true
  3. 3. Set the object to be transferred to Donkey.prototype:

    iaDonkey.__proto__ = Donkey.prototype
  4. 4. Calls Donkey () in the context of a new object:

      var iaDonkey = function(){ this.constructor(); // function Donkey() // … }; 


 //   ,    : function New (F, args) { /*1*/ var n = {'__proto__': F.prototype}; /*2*/ F.apply(n, args); /*3*/ return n; } 


  1. Create a new value (n) and write the prototype value to proto .
  2. Call our method constructor through apply .
  3. Return a new object, class New .


Encapsulation through closures



A closure is a scope-based mechanism that can be created through a function. Each function creates a new scope.



Consider two examples.



Example 1:



  for (var i = 0; i < 10; i++) { setTimeout(function () { console.log(i); }, 0); } 


In this loop, a dozen are displayed ten times: after the last iteration, there will be 10, and then the execution of setTimeout will begin.



Example 2:



  for (var i = 0; i < 10; i++) { (function (m) { setTimeout(function () { console.log(m); },0); })(i) } 


An anonymous self-calling function allows you to start executing a function immediately after its declaration.



We have applied the closure principle: we declare a function, transfer the actual value to it, and it “closes” the value of the variable i. m i. m will try to get the value from the closest upper scope through the closures. And since we passed it through a self-calling function, it will each time be equal to its value (the value of the variable i ), and we will get from 0 to 9 10 times.



This example about closures and encapsulation is taken from a real project:







There is a function BarChart , it has public methods - to build a graph and update its values. There are private methods and properties - what we need to do without showing it to the world around us.



If we turn to BarChart via new , it turns out that this is a constructor function, and we will create a new object of this class. But all private methods will close in this variable, and we will be able to access them inside. They will remain in the scope that this function will create.



Polymorphism and call / apply keywords



Application of the apply construction:



 var obj = { outerWidth: 'pliers' }; function getWidth(){ return this.outerWidth; } var a = getWidth(); var b = getWidth.apply(obj); console.log(a); //   , this  windows console.log(b); //    pliers. outerWidth —    windows, ,  ,  windows.outerWidth 


Call mechanism:



 Calling func.call(context, a, b...) 


is equivalent to writing:



 func(a, b...), but this == context. 


Both calls are identical, only apply allows passing parameters through an array.



 call(context, param1, param2 …) apply(context, [args]) 


Four call options and its results:



  function: function(args) – this == window  method: obj.funct(args) – this == obj Apply: func.apply(obj,args) – this == obj Constructor: new func(args) – this == new object 


Inheritance and implementation methods







The base filter model is a standard set of parameters that is in the filter of any application. These parameters are necessary for pagination, page numbers, etc.



We ask him a method through a prototype. After receiving the server model, we call this method, and it converts some of our data into the necessary format.



There is a heir class and a specific “RouteHistorical” page. The class is inherited from the base filter, but additionally has its own fields and parameters.







In line 73, we pass the newly created RouteHistorical object and the same arguments to the base class via the context. The method initializes all fields, and we get a new object.



Lines 81-82 allow us to make RouteHistorical heir to the base filter. In line 81, we write a reference to the base constructor class in the prototype property. The prototype method is completely overwritten, and the constructor is lost. When we create a new object, it will not know what to turn to.



In line 82, we set the prototype.constructor property to a link to itself. A property of the constructor class always refers to itself.



Prototypes



The prototype property makes sense paired with the new keyword. When we create a new object, we write the prototype value in the __proto__ property. It contains a reference to the class that is the parent for our class.







prototype only needed to say what needs to be written in __proto__ when instantiating a new object.



  // unsafe var filter = { EnablePagination: true }; function BaseFilter(size) { this.PageSize = size; this.__proto__ = filter; } // safe var filter= { EnablePagination: true }; function BaseFilter(size) { this.PageSize = size; } BaseFilter.prototype = filter; 


The two entries are the same, but __proto__ is considered unsafe to directly access __proto__ , and not all browsers allow it.



Creating a descendant from the base class



extend function:



  function extend(Child, Parent) { var F = function() { } F.prototype = Parent.prototype // Child.prototype = new F() //   Child  __proto__    prototype Child.prototype.constructor = Child //  ,     . Child.superclass = Parent.prototype //      Parent }; 


Using:



  function BaseFilterModel(..) { ... } function RouteHistoricalFilterModel(..) { ... } 


instanceof



Allows you to determine whether an object is an instance of any constructor based on the entire prototyping chain .



instanceof (pseudocode method):



  function isInstanceOf(obj, constructor) { if (obj.__proto__ === constructor.prototype) { return true; } else if (obj.__proto__ !== null) { return isInstanceOf(obj.__proto__, constructor) } else { return false } }; 


Total



1. In JavaScript before ECMAScript 6, there were no classes, there were only constructor functions that are called using the new keyword.

2. The prototyping chain is the basis of JavaScript inheritance.

3. When we access a property, it is searched for in the object. If not, then in __proto__ , and so on throughout the chain. Thus, inheritance is implemented in JavaScript.

4. fn.__proto__ stores the link to fn.prototype .

5. The new operator creates an empty object with the only property __proto__ that refers to F.prototype . The constructor executes F , where this context is a previously created object, sets its properties and returns this object.

6. The instanceof operator does not check that the object was created through the ObjectsConstructor constructor, but makes a decision based on the entire prototyping chain.

7. In JavaScript, this depends on the context, i.e. on how we call the function.

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



All Articles