📜 ⬆️ ⬇️

Creating Objects in Javascript

Flexibility Javascript allows you to create objects in a variety of ways. But as it often happens, diversity is fraught with many pitfalls. In this article you will learn how to see and go around these dangerous reefs.

Basics of the basics


It will be useful to remind you what objects in Javascript are and how you can create them. An object in Javascript is just a hash table of keys and values. If values ​​are basic types or other objects, they are called properties , if they are functions, they are called object methods .

Objects created by the user can be changed at any point in the execution of the script. Many properties of objects embedded in the language are also changeable. That is, you can simply create an empty object and add properties and methods to it as necessary. The easiest way to do this is with literal notation :
//   var cat = {}; // : cat.name = "Garfield"; // : cat.getName = function() { return cat.name; }; 

Another way to create an object is to use constructor functions:
 //  : var cat = {name: "Garfield"}; // -: var cat = new Object(); cat.name = "Garfield"; 

Obviously, the literal notation is shorter than the constructor. There is also a philosophical reason for preferring literal notation to designers: it emphasizes that an object is just a mutable hash, and not something created from a template given by a class.

In addition, the use of the Object constructor forces the interpreter to check whether this function is redefined in the local context.
')

Underwater Stone Object Constructor


There are no reasons to use the Object constructor. But we all know that sometimes you have to use some old code, and in this case it is useful to know about one feature of this constructor. It takes an argument, and depending on its type, can assign the creation of an object to another constructor built into the language; in the end, we get the wrong object that we expected:
 //  var o = new Object(); o.constructor === Object; // true var o = new Object(1); o.constructor === Number; //true var o = new Object("string"); o.constructor === String; //true //  ,       : typeof o.substring; // "function" 

This behavior of the Object constructor can lead to unexpected results if we pass to it a value that is unknown at run time.

The moral is obvious: do not use the Object constructor.

Own constructors


We can define our own constructors. Using them looks like this:
 var cat = new Cat("Garfield"); cat.say(); // "I am Garfield" 

The syntax is similar to the Java constructor, but in Javascript the constructor is a normal function and therefore is defined like this:
 var Cat = function(name) { this.name = name; this.say = function() { return "I am" + this.name; } } 

When this constructor is called with the new operator inside a function, the following happens:

For the sake of simplicity, in the example, the say() method is added to the this object. This is not very good, because each time new function new Person() is called, a new Person() function will be created in memory. Since the say() method is the same for all objects created using this constructor, it is better to add it to the Cat prototype:
 Cat.prototype.say = function() { return "I am " + this.name; }; 

In addition, it is not entirely correct to say that the this object, implicitly created in the constructor, is empty: it inherits from the Cat prototype, but the consideration of prototypes is beyond the scope of this article.

What the constructor returns


When using the new operator, the constructor always returns an object. By default, this is the object referenced by this . The constructor returns this implicitly, but we can explicitly return any other object, for example:
 var Cat = function() { this.name = "I am Garfield"; var that = {}; that.name = "I am Cat-woman"; return that; }; var cat = new Cat(); cat.name // "I am Cat-woman" 

Thus, we can return any value from the constructor, but only if it is an object. If we try to return, say, a string or false , this will not result in an error, but the return statement will be ignored, and the constructor will return this .

Insidious new


Constructors are just functions that are called with the new operator. What happens if you forget this operator? The interpreter will not issue warnings, but this will lead to logical errors. The variable this will point not to the object inherited from the prototype of the constructor, but to the global object ( window in the case of the browser):
 function Cat() { this.name = "Garfield"; } //   var cat = new Cat(); typeof cat; // "object" cat.name; // "Garfield" // new: var cat = Cat(); typeof cat; // "undefined" window.name; // "Garfield" 

In the strict mode of the ECMAScript 5 this standard in this case will not point to a global object. Let's see how to avoid this error if ECMAScript 5 is not available.

Function naming conventions

The easiest way is to strictly abide by the naming conventions of functions: we start the usual functions with a lowercase letter ( myFunction() ), and the constructor functions start with the capital one ( MyConstruction() ). Unfortunately, this method almost does not save anything.

Explicit object return

Designers can return any objects. Programmers can take advantage of this:
 function Cat() { var that = {}; that.name = "Garfield"; return that; } 

The variable name is arbitrary; it is not part of the specification. With the same success, we can call the returned object me or self or whatever you want.

For simple objects, such as those created in the example, we can generally do without additional variables using literal notation:
 function Cat() { return { name: "Garfield" }; } 

Such a constructor will always return an object, regardless of how it is called:
 var first = new Cat(), second = Cat(); first.name; // "Garfield" second.name; // "Garfield" 

This method has a serious drawback: the object does not inherit the prototype of the constructor, that is, the methods and properties added directly to Cat will not be available to the objects created with its help.

Self-invoking constructor

To solve this problem, it is enough to check whether this in the body of the constructor is an instance of this constructor itself, and if not, call itself again, but this time with the new operator. It sounds scary, but it's really simple:
 function Cat() { if (!(this instanceof Cat)) { return new Cat(); } this.name = "Garfield"; } Cat.prototype.meow = "Meow!"; var first = new Cat(), second = Cat(); first.name; // "Garfield" second.name; // "Garfield" first.meow; // "Meow!" second.meow; // "Meow!" 

If our constructor is subsequently renamed, you will have to edit its body. You can avoid this by checking the arguments.callee instead of the constructor name:
 if (!(this instanceof arguments.callee)) { return new arguments.callee(); } 

Here we used the fact that inside each function, a arguments object is created that contains all the parameters passed to the function at the time of the call. The callee property of this object indicates the function being called. But here, you need to be careful: strict ECMAScript 5 mode causes a TypeError exception when accessing this property, so you should make a choice in advance between the convenience of refactoring and the bright tomorrow.

Instead of conclusion


Javascript is a terrific language. It is easy enough to master, and existing frameworks will allow you to easily use it in your projects. But the simplicity of the syntax hides the whole reefs of the pitfalls - and very powerful tools. Sometimes it is useful to watch what is there at the bottom.

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


All Articles