class keyword and the so-called factory functions (Factory Function). The author of the material, the translation of which we publish today, explores and compares these two concepts in search of an answer to the question of the pros and cons of each of them.
class keyword appeared in ECMAScript 2015 (ES6), with the result that we now have two competing object creation patterns. In order to compare them, I will describe the same object ( TodoModel ), using the syntax of the classes, and applying the factory function.TodoModel description using the class keyword: class TodoModel {   constructor(){       this.todos = [];       this.lastChange = null;   }     addToPrivateList(){      console.log("addToPrivateList");   }   add() { console.log("add"); }   reload(){} }  function TodoModel(){   var todos = [];   var lastChange = null;         function addToPrivateList(){       console.log("addToPrivateList");   }   function add() { console.log("add"); }   function reload(){}     return Object.freeze({       add,       reload   }); } class keyword are publicly available. var todoModel = new TodoModel(); console.log(todoModel.todos);     //[] console.log(todoModel.lastChange) //null todoModel.addToPrivateList();     //addToPrivateList  var todoModel = TodoModel(); console.log(todoModel.todos);     //undefined console.log(todoModel.lastChange) //undefined todoModel.addToPrivateList();     //taskModel.addToPrivateList                                   is not a function class keyword. todoModel.reload = function() { console.log("a new reload"); } todoModel.reload();            //a new reload Object.freeze(TodoModel.prototype) after declaring a class, or using the decorator to “freeze” classes when it is supported.Object.freeze() command to process the returned object, which contains only the public methods of the new object. Private data of this object can be modified, but this can be done only through these public methods. todoModel.reload = function() { console.log("a new reload"); } todoModel.reload();            //reload class keyword are subject to the long-term problem of losing context this . For example, this loses context in nested functions. This not only complicates the programming process, such behavior is also a constant source of errors. class TodoModel {   constructor(){       this.todos = [];   }     reload(){       setTimeout(function log() {          console.log(this.todos);    //undefined       }, 0);   } } todoModel.reload();                   //undefined this loses context when using the corresponding method in a DOM event: $("#btn").click(todoModel.reload);    //undefined this not used here. function TodoModel(){   var todos = [];         function reload(){       setTimeout(function log() {          console.log(todos);        //[]      }, 0);   } } todoModel.reload();                   //[] $("#btn").click(todoModel.reload);    //[] this context when using classes, but at the same time they create a new problem. Namely, when using arrow functions in classes, the keyword this no longer loses context in nested functions. However, this loses context when dealing with DOM events.TodoModel class using pointer functions. It is worth noting that in the process of refactoring, when replacing ordinary functions with pointer ones, we lose something important for the readability of the code: the names of functions. Take a look at the following example. //      setTimeout(function renderTodosForReview() {     /* code */ }, 0); //       setTimeout(() => {     /* code */ }, 0);  var renderTodosForReview = () => {    /* code */ }; setTimeout(renderTodosForReview, 0); new . And when creating objects using factory functions, new not required. However, if the use of new improves the readability of the code, this operator can also be used with factory functions, there will be no harm. var todoModel= new TodoModel(); new with a factory function, the function simply returns the object created to it.User object to work with authorization mechanisms. I created a couple of such objects using both approaches described here.User object using the class: class User {   constructor(){       this.authorized = false;   }     isAuthorized(){       return this.authorized;   } } const user = new User();  function User() {   var authorized = false;        function isAuthorized(){      return authorized;   }     return Object.freeze({       isAuthorized   }); } const user = User(); class keyword are vulnerable to attacks if the attacker has an object reference. Since all properties of all objects are public, an attacker can use other objects to gain access to the object in which he is interested.user variable is global. To verify this, open the example code and modify the user variable from the console.top to plunkerPreviewTarget(run.plnkr.co/) . user.authorized = true;            //    user.isAuthorized = function() { return true; }  // API console.log(user.isAuthorized());  //true 
SpecialService is a descendant of the class Service . class Service {   log(){} } class SpecialService extends Service {  logSomething(){ console.log("logSomething"); } } var specialService = new SpecialService(); specialService.log(); specialService.logSomething(); Object.assign() command to copy all properties from existing objects. For example , suppose we need to reuse all members of the Service object in the SpecialService object. function Service() {   function log(){}          return Object.freeze({       log   }); } function SpecialService(args){  var standardService = args.standardService;  function logSomething(){      console.log("logSomething");  }  return Object.freeze(Object.assign({}, standardService, {      logSomething  })); } var specialService = SpecialService({      standardService : Service()   }); specialService.log(); specialService.logSomething(); 
TodoModel object TodoModel you already know to clarify the difference between objects and data structures. function TodoModel(){   var todos = [];            function add() { }   function reload(){ }        return Object.freeze({       add,       reload   }); } TodoModel object TodoModel responsible for storing and managing the list of todo objects. TodoModel is an OOP object, the one that provides the behavior and hides the data. The application will have only one copy of it, so when you create it using the factory function, no additional memory costs are required.todos array are data structures. There can be many such objects in the program, but these are regular JavaScript objects. We are not interested in making their methods private. Rather, we strive to ensure that all their properties and methods would be publicly available. As a result, all these objects will be built using a prototype system, so we can save memory. They can be created using a regular object literal or by Object.create() command.instanceof . All objects are created using object literals, some of them with public methods (OOP objects), some with public properties (data structures).Object.assign() .this , leading to permanent errors due to the loss of context, put classes in second place compared to factory functions. As an exception, classes are resorted to when they are used in the framework used, for example, in React.Source: https://habr.com/ru/post/352198/
All Articles