Objects in JavaScript. Creating JavaScript Objects

JavaScript is designed on a simple object-based paradigm. An object is a collection of properties, and a property is an association between a name (or key) and a value. A property"s value can be a function, in which case the property is known as a method. In addition to objects that are predefined in the browser, you can define your own objects. This chapter describes how to use objects, properties, functions , and methods, and how to create your own objects.

Objects overview

Objects in JavaScript, just as in many other programming languages, can be compared to objects in real life. The concept of objects in JavaScript can be understood with real life, tangible objects.

In JavaScript, an object is a standalone entity, with properties and type. Compare it with a cup, for example. A cup is an object, with properties. A cup has a color, a design, weight, a material it is made of, etc. The same way, JavaScript objects can have properties, which define their characteristics.

Objects and properties

A JavaScript object has properties associated with it. A property of an object can be explained as a variable that is attached to the object. Object properties are basically the same as ordinary JavaScript variables, except for the attachment to objects. The properties of an object define the characteristics of the object. You access the properties of an object with a simple dot-notation:

ObjectName.propertyName

Like all JavaScript variables, both the object name (which could be a normal variable) and property name are case sensitive. You can define a property by assigning it a value. For example, let"s create an object named myCar and give it properties named make , model , and year as follows:

Var myCar = new Object(); myCar.make = "Ford"; myCar.model = "Mustang"; myCar.year = 1969;

myCar.color; //undefined Properties of JavaScript objects can also be accessed or set using a bracket notation (for more details see property accessors). Objects are sometimes called associative arrays

, since each property is associated with a string value that can be used to access it. So, for example, you could access the properties of the myCar object as follows:

An object property name can be any valid JavaScript string, or anything that can be converted to a string, including the empty string. However, any property name that is not a valid JavaScript identifier (for example, a property name that has a space or a hyphen, or that starts with a number) can only be accessed using the square bracket notation. This notation is also very useful when property names are to be dynamically determined (when the property name is not determined until runtime). Examples are as follows:

// four variables are created and assigned in a single go, // separated by commas var myObj = new Object(), str = "myString", rand = Math.random(), obj = new Object(); myObj.type = "Dot syntax"; myObj["date created"] = "String with space"; myObj = "String value"; myObj = "Random Number"; myObj = "Object"; myObj[""] = "Even an empty string"; console.log(myObj);

Please note that all keys in the square bracket notation are converted to string unless they"re Symbols, since JavaScript object property names (keys) can only be strings or Symbols (at some point, private names will also be added as the class fields proposal progresses, but you won't use them with form). For example, in the above code, when the key obj is added to the myObj , JavaScript will call the obj.toString() method, and use this result string as the new key.

You can also access properties by using a string value that is stored in a variable:

Var propertyName = "make"; myCar = "Ford"; propertyName = "model"; myCar = "Mustang";

Using a constructor function

Alternatively, you can create an object with these two steps:

  1. Define the object type by writing a constructor function. There is a strong convention, with good reason, to use a capital initial letter.
  2. Create an instance of the object with new .

To define an object type, create a function for the object type that specifies its name, properties, and methods. For example, suppose you want to create an object type for cars. You want this type of object to be called Car , and you want it to have properties for make, model, and year. To do this, you would write the following function:

Function Car(make, model, year) ( this.make = make; this.model = model; this.year = year; )

Notice the use of this to assign values ​​to the object"s properties based on the values ​​passed to the function.

Now you can create an object called mycar as follows:

Var mycar = new Car("Eagle", "Talon TSi", 1993);

This statement creates mycar and assigns it the specified values ​​for its properties. Then the value of mycar.make is the string "Eagle", mycar.year is the integer 1993, and so on.

You can create any number of Car objects by calls to new . For example,

Var kenscar = new Car("Nissan", "300ZX", 1992); var vpgscar = new Car("Mazda", "Miata", 1990);

An object can have a property that is itself another object. For example, suppose you define an object called person as follows:

Function Person(name, age, sex) ( this.name = name; this.age = age; this.sex = sex; )

and then instantiate two new person objects as follows:

Var rand = new Person("Rand McKinnon", 33, "M"); var ken = new Person("Ken Jones", 39, "M");

Then, you can rewrite the definition of Car to include an owner property that takes a person object, as follows:

Function Car(make, model, year, owner) ( this.make = make; this.model = model; this.year = year; this.owner = owner; )

To instantiate the new objects, you then use the following:

Var car1 = new Car("Eagle", "Talon TSi", 1993, rand); var car2 = new Car("Nissan", "300ZX", 1992, ken);

Notice that instead of passing a literal string or integer value when creating the new objects, the above statements pass the objects rand and ken as the arguments for the owners. Then if you want to find out the name of the owner of car2, you can access the following property:

Car2.owner.name

Note that you can always add a property to a previously defined object. For example, the statement

Car1.color = "black";

adds a property color to car1, and assigns it a value of "black." However, this does not affect any other objects. To add the new property to all objects of the same type, you have to add the property to the definition of the Car object type.

Using the Object.create method

See also

  • To dive deeper, read about the details of javaScript's objects model.
  • To learn about ECMAScript 2015 classes (a new way to create objects), read the JavaScript classes chapter.



Objects are one of the core concepts in JavaScript. When I first started studying them, they seemed quite simple to me: just pairs of keys and values, as described in theory.

Only after some time did I begin to understand that the topic was much more complex than I thought. And then I began to study information from different sources. Some of them gave a good idea of ​​the subject, but I was not able to see the whole picture right away.

In this post, I tried to cover all aspects of working with objects in JS, without going too deep into specific details, but also without leaving out important details that will help you understand the subject and feel more confident as you study it further.

So let's start with the basics.

An object

An object in JavaScript is simply a collection of properties, each of which is a key-value pair. You can access the keys using a dot ( obj.a) or bracket notation ( obj["a"]).

Remember that parentheses should be used if the key is:

  • is not a valid JavaScript identifier (it has a space, a dash, starts with a number...)
  • is a variable.
One of the properties that objects in JS receive when created is called Prototype, and this is a very important concept.

Prototype

Every object in JavaScript has an internal property called Prototype. In most browsers you can refer to it by the notation __proto__.

Prototype is a way to enforce property inheritance in JavaScript. This way you can share functionality without duplicating code in memory. The method works by creating a connection between two objects.

Simply put, Prototype creates a pointer from one object to another.

Prototype chain

Every time JS looks for a property in an object and does not find it directly on the object itself, it checks for the presence of the property in the prototype object. If there is no property in it, then JS will continue to look in the prototype of the associated object. This will continue until JS finds a suitable property or reaches the end of the chain.

Let's look at an example:

Var cons = function () ( this.a = 1; this.b = 2; ) var obj = new cons(); cons.prototype.b = 3; cons.prototype.c = 4;
cons is a constructor (simply a function that can be called using the operator new).

On the fifth line we create a new object - new copy cons. Immediately after creation obj also gets the prototype property.

And now we add properties ( "b", "c") object prototype cons.
Let's consider obj:

obj.a // 1- everything is the same here, obj.a is still 1.
obj.c?- y obj no property c! However, as previously mentioned, JS will now look for it in the prototype obj and will return the value 4.

Now let's think about what the meaning is obj.b and what it will be like when we remove obj.b?

Obj.b is equal to 2. We assigned the property b, but we did it for a prototype cons, so when we check obj.b, then we still get 2. However, immediately after removing obj.b JS will no longer be able to find b y o bj, and therefore will continue searching in the prototype and return the value 3.

Creating an Object

Object literal: let obj = (a: 1);
We created an object with the following prototype chain: obj ---> Object.prototype ---> null
As you can guess, object.prototype is the prototype of the object, and also the end of the prototype chain.

Object.create():var newObj = Object.create(obj);
U newObj there will be the following chain of prototypes: newObj ---> obj ---> Object.prototype ---> null

Constructor. Just like in the example above, the constructor is simply a JS function that allows us to take advantage of the operator new to create new instances of it.

ES6 classes:

Class rectangle ( constructor(height, width) ( this.height = height; this.width = width; ) getArea() ( return this.height * this.width; ) ) let square = new rectangle(2, 2);
Square- constructor instance rectangle, and so we can call square.getArea() //4, square.width, as well as all functions inherited from object.prototype.

Which way is better? If you plan to create multiple instances, you can use ES6 or the designer. If you plan to create the object once, then it is better to specify a literal, since this is the simplest way.

And now that we have learned about prototype and having become familiar with all the ways to create new objects, we can move on to discuss one of the most confusing aspects associated with objects.

Compare and change objects

IN JavaScript objects belong to the reference type

When we create an object let obj = (a: 1);, variable obj gets the object's memory address, but not its value! It is extremely important to understand this difference, otherwise errors may occur. When we create another object let newObj = obj, we are actually creating pointer to a certain memory area obj, and not a completely new object.

This means that by doing newObj.a = 2, we actually change obj so that obj.a becomes equal to 2!

This approach easily leads to bugs, which is why many companies work with immutable objects. Instead of change already created of this object you will have to again create a new object (a copy of the original) and make changes to it. This is how important libraries like Redux work, and this is one of the main concepts in general functional programming. You can read more.

Equality

It also follows from the above that two objects can never be equal, even if they have the same properties. This is due to the fact that JS actually compares the memory location of objects, and two objects are never in the same memory location.

// Two distinct objects with the same properties are not equal var fruit = (name: "apple"); var fruitbear = (name: "apple"); fruit === fruitbear; // return false // here fruit and fruitbear are pointing to the same object var fruit = (name: "apple"); var fruitbear = fruit; fruit === fruitbear; // return true
So, you most likely have already wondered how you can compare objects or how to perform various manipulations with objects, given the requirement for their immutability.

Let's consider several possibilities.

Changing an object

Let's say it's clear that we shouldn't actually change objects, so we want to create a copy of the corresponding object and change its properties. Comes to the rescue Object.assign().

Var obj = (a: 1, b: 2); var newObj = Object.assign((), obj,(a:2)) // (a: 2, b: 2 )
If we want to change the property value a object obj, you can use object.assign to create a copy obj and its changes.

In the example you can see that we first create an empty object, then copy the values obj and make our changes, ultimately obtaining a new and ready-to-use object.

Please note that this method will not work for deep copying. When we talk about deep copying, we mean that we need to copy an object with one or more properties.

Const obj = (a: 1, b: ( a: 1 ) ); // b property is an object
Object.assign() copies the properties of an object, so if the value of the property is a pointer to an object, then only the pointer is copied.

A deep copy requires a recursive operation. Here you can write a function or just use a method _.cloneDeep from the Lodash library.

Comparison of objects

There is one cool technique for working with objects - string conversion. In the following example, we convert both objects to strings and compare them:

JSON.stringify(obj1) === JSON.stringify(obj2)
This approach makes sense because we end up comparing strings that are a pointer to a value type. The bad news is that it doesn't always work, mainly because the order of the object's properties is not guaranteed.

Other good decision- use the method _.isEqual from Lodash, which performs deep object comparison.

And before we wrap up, let's go over some frequently asked questions about objects. This will help you dive deeper into the topic and apply the acquired knowledge in practice.

Try to think about the solution yourself before reading the answer.

How to find out the length of an object?

To get the answer, you need to go through all the properties of the object one by one and count them. There are several ways to perform such an iteration:
  • for in. This method covers all countable properties of an object and its prototype chains. We've seen the prototype (and hopefully learned the material), so it should be clear that the application for in won't always be true for getting properties of an object.
  • Object.keys. This method returns an array with the keys of all own(belonging to the specified object) counting properties. This approach is better because we only work on the properties of the object, without accessing the properties prototype. There are, however, situations where you have assigned an attribute enumerable some property is false, and object.keys ends up skipping it, and you get an incorrect result. This rarely happens, but in such cases it will come in handy getOwnPropertyNames.
  • getOwnPropertyNames returns an array containing everything own object keys (both countable and uncountable).
Also worth mentioning:
  • Object.values iterates over its own counting properties and returns an array with the corresponding values.
  • Object.entries iterates over its own counting properties and returns an array with keys and their values.
I think you noticed that most of the methods listed above return an array. This is an opportunity to take full advantage of JavaScript's array techniques.

One such method is array.length. As a result, we can simply write

Let objLength = Object.getOwnPropertyNames(obj).length;

How to check if an object is empty?

  1. JSON.stringify(myObj) === “()”?. Here we again use the string conversion tool to easily check whether an object is empty (by comparing strings, not objects).
  2. !Object.keys(myobj).length // true?.? As I mentioned, converting object keys to an array can be very useful. Here we use the convenient property length, inherited from Array.prototype, using it to check the length of the keys in the array. In JS 0 turns to false, so adding ! we turn it to true. Any other numbers will be converted to false.

Finally

I hope you now feel more confident in creating and working with objects. Let's summarize:
  • Remember that objects are of a reference type, which means that it is recommended to work with them without changing the original objects.
  • Make friends with the property prototype and a chain of prototypes.
  • Get to know the tools that help you work with objects. Remember that you can turn objects into strings, get an array of their keys, or simply iterate over their properties using the set of methods we've been introduced to.
I wish you good luck in your study JavaScript objects.

JavaScript is an object-oriented language. With the exception of basic language constructs such as loops and relational operators, almost every feature of JavaScript is implemented using objects in one way or another.

Sometimes objects are used explicitly to perform specific tasks, such as, for example, processing (X)HTML and XML-based documents object model document. In other cases, the role of objects is less obvious, such as the role of the String object when working with primitive string data.

Previous chapters have provided examples that clearly demonstrate the usefulness of built-in objects, but in this chapter we'll dive into JavaScript objects directly.

Objects in JavaScript

Objects in JavaScript can be divided into four groups.

  1. User objects created by the programmer and having a structure and entity that suit a specific programming task. We will discuss the possibilities of creating and using such objects in this chapter.
  2. Built-in objects are provided by the JavaScript language itself. These include objects that are associated with data types (String, Number, and Boolean), objects that allow you to create custom objects and composite types (Object and Array), and objects that make common tasks easier (such as Date, Math, and RegExp). The capabilities of built-in objects are regulated by the ECMA-262 language standard and, to a lesser extent, by browser manufacturer specifications.
  3. Browser objects that are not part of the JavaScript language but are supported by most browsers. Examples of browser objects are Window, an object through which browser windows are managed and interact with the user, and Navigator, an object that provides client configuration information. Because most aspects of browser objects are not governed by any standards, their properties and behavior can vary greatly depending on both the browser and its version. Objects of this type will be discussed further.
  4. Document objects are part of the Document Object Model (DOM), defined by the W3C consortium. Such objects provide the programmer with a structured interface to (X)HTML and XML documents. It is these objects that provide JavaScript capability manipulation of nested style sheets (CSS - Cascade Style Sheet) and simplify the implementation of dynamic HTML (DHTML). Access to document objects is provided by the browser through the document property of the Window object (window. document). We'll talk more about the DOM later.

Principles of working with objects

An object is an unordered collection of data, including primitive data types, functions, and even other objects. The benefit of objects is that they concentrate in one place all the data and logic necessary to perform a specific task. The String object stores text data and offers many of the functions needed to act on it. Although the presence of objects in a programming language is not at all necessary (there are no objects in the C language, for example), they certainly simplify the use of the language.

Creating Objects

An object is created using a constructor - a function special type, which prepares a new object for use by initializing the memory occupied by the Object. In Chapter 4, we saw that objects can be created by applying the new operator to their constructors. Using this operation causes the constructor to create a new object, the nature of which is determined by the constructor being called. For example, the String() constructor creates String objects, and the Array() constructor creates Array objects. This is how object types are named in JavaScript: by the name of the constructor that creates the object.
Here's a simple example of creating an object:

var city = new String();

This statement creates a new String object and places a link to it in the city variable. Here, the constructor is not given any arguments, so the city variable will receive its default value—in this case, the empty string. You can make this example more interesting by passing an argument to the constructor that specifies the initial value:

var city = new String("San Diego");

In this article, I want to talk as fully and consistently as possible about what an object is in JavaScript, what its capabilities are, what relationships can be built between objects and what methods of “native” inheritance follow from this, how all this affects performance and what in general to do with all this :)

The article will NOT have a word about: emulation of the traditional class-object paradigm, syntactic sugar, wrappers and frameworks.

The complexity of the material will increase from the beginning to the end of the article, so for pros the first parts may seem boring and banal, but then it will be much more interesting :)

Objects in JavaScript

Many articles contain the phrase “In JavaScript, everything is an object.” Technically this is not entirely true, but it makes the right impression on beginners :)

Indeed, much in language is an object, and even what is not an object can have some of its capabilities.

It is important to understand that the word “object” is used here not in the sense of “an object of some class.” An object in JavaScript is first and foremost just a collection of properties (if you prefer, you can call it an associative array or list) consisting of key-value pairs. Moreover, the key can only be a string (even for array elements), but the value can be any type of data listed below.

So in JavaScript there are 6 basic types data are Undefined (indicating the absence of a value), Null, Boolean (Boolean), String (string), Number (number) and Object (object).
Moreover, the first 5 are primitive data types, but Object is not. In addition, we can conventionally consider that the Object type has “subtypes”: array (Array), function (Function), regular expression (RegExp) and others.
This is a somewhat simplified description, but in practice it is usually sufficient.

Additionally, the primitive types String, Number, and Boolean are related in certain ways to the non-primitive "subtypes" of Object: String, Number, and Boolean, respectively.
This means that the string "Hello, world", for example, can be created either as a primitive value or as a String object.
In short, this is done so that the programmer can use methods and properties when working with primitive values ​​as if they were objects. You can read more about this in the corresponding section of this article.

Work from the link

A reference is a means of accessing an object under various names. Work with any objects is carried out exclusively by reference.
Let's demonstrate this with an example:
test= function () (alert("Hello!" )) //Create a function (alert("Hello!")) (and the function, as we remember, is a full-fledged object) and make the test variable a reference to it
test_link=test; //test_link now also refers to our function
test(); //Hello!
test_link(); //Hello!


As we can see, both the first link and the second give the same result.
We need to realize that we don't have any function named test, and that the test variable is not some kind of "main" or "primary" link, and "test_link" is a minor one.

Our function, like any other object, is simply an area in memory, and all references to this area are absolutely equivalent. Moreover, the object may not have any references at all - in this case it is called anonymous, and can only be used immediately immediately after creation (for example, passed to a function), otherwise it will be impossible to access it and will soon be destroyed by the garbage collector (garbage collection), which is responsible for deleting objects without references.

Let's see why it is so important to understand this:

test=(prop: "sometext" ) //Create an object with the prop property
test_link=test; //Create another link to this object

Alert(test.prop); //sometext

//Change the property of the object
test_link.prop="newtext" ;

Alert(test.prop); //newtext
alert(test_link.prop); //newtext
/*One could say that the property has changed both here and there - but this is not so.
There is only one object. So the property changed on it once, and the links just continue to point where they point. */

//Add a new property and remove the old one
test.new_prop="hello" ;
delete test.prop;

Alert(test_link.prop); //undefined - this property no longer exists
alert(test_link.new_prop);

//Delete the link
delete test;
alert(test.new_prop);
/*At this point the script will throw an error, because test no longer exists, and test.new_prop does not exist even more so */
alert(test_link.new_prop); //hello
/* but here everything is in order, because we did not delete the object itself, but only a link to it. Now our object is pointed to by a single link, test_link */

//Create a new object
test=test_link; //First, create the test link again
test_link=(prop: "sometext" ) //And here is the new object

Alert(test_link.prop); //sometext
alert(test.prop); //undefined
/* Creating a new object breaks the reference link, and now test and test_link point to different objects.
In fact, this is equivalent to deleting the test_link and creating it again, but pointing to a different object */
alert(test.new_prop); //hello - now test contains a link to our very first object


* This source code was highlighted with Source Code Highlighter.

This behavior of objects often raises a lot of questions for novice developers, so I hope this text will bring some clarity. If we want to create a truly new, independent copy of an object, and not a link, then the only way to do this is to create a new object and copy the required properties there.

It is also worth noting that working with objects by reference, in addition to the fun effects listed above, also provides significant memory savings, which is important when one object is widely used in different places in the program.

Primitive values

As I mentioned above, the String and Number data types can be either objects or primitive values.
obj= new String("hello" ); //Create a string as an object
simple="hello" ; //Create a primitive value

Alert(obj); //hello
alert(simple); //hello - so far everything is predictable

Alert(obj.length); //6 - an object of type String has a length property that stores the length of the string
alert(simple.length); //6
/* Although simple is not an object, we can access the same set of properties as an object of type String. It's quite convenient */

Obj.prop="text" ;
simple.prop="text" ;

Alert(obj.prop); //text - since obj is a regular object, we can easily give it another property
alert(simple.prop); //undefined - but simple is not an object, and this number will not work for us

* This source code was highlighted with Source Code Highlighter.


The same is true for both the Number and Boolean types (well, except that they don’t have a length property, but they have a number of other great properties).
Using strings and numbers as objects does not have any practical benefit, because primitive values ​​are more convenient to use, but at the same time retain all the necessary functionality. However, to complete the picture it is necessary to understand this mechanism.

Don't confuse the use of primitive values ​​with the use of literals - for example, whether we create an array as "test=new Array()" or as "test=", the result will still be the same object. We will not receive any primitive values.

Creating and Using Objects

So, unlike languages ​​that implement the class-object paradigm, we do not need to create a class first and then create an object of the class. We can immediately create an object, which is what we will do in the following example:
test=(
simple_property: "Hello" ,
object_property: (
user_1: "Petya" ,
user_2: "Vasya"
},
function_property: function (user) (
alert(this .simple_property + ", " + this .object_property);
}
}

Test.function_property("user_1" ); //Hello, Petya.

* This source code was highlighted with Source Code Highlighter.


We have a test object that has 3 properties, the names of which, I hope, speak for themselves. What interests us most about it is the function_property, which contains the function. Such a function can be called an object method.

Our function uses the this keyword twice, which is a pointer (i.e., a reference) to the object from which the function is called. Thus, this.simple_property=test.simple_property="Hello", and this.object_property=test.object_property="Peter".

It is important to be clear that this always points to the object from which the function is called, and not to the object to which it belongs. Although in this example they are the same object, this is not always the case.

test.function_property("user_1" ); //Hello, Petya.

Test2=new Object(); //Another form of creating a new object, similar to test2=()

Test.function_property.call(test2, "user_1" ); //error
/* The call method allows you to call a function on behalf of another object. In this case, we call the function_property method of the test object, and its this no longer points to the test object, but to the test2 object. And because it does not have the object_property property, then when you try to get this.object_property the script will throw an error */

//let's try to fix the situation
test2.simple_property="Good day" ;
test2.object_property=test.object_property; //In this case, we will use specifying the object by reference so as not to duplicate the code

Test.function_property.call(test2, "user_1" ); //Good day, Petya.


* This source code was highlighted with Source Code Highlighter.

It should also be clear from the example that there are no clear steps for creating and using an object. An object can be modified in any way at any time - before, after and even during use. This is also an important difference from “traditional” OOP.

Constructor

In the example above, we created 2 objects that had some similarity. Both had simple_property and object_property properties. Obviously, when writing real code, the task of creating identical or simply similar objects often arises. And of course, we do not have to create each such object manually.

A designer will come to our aid. A constructor in JavaScript is not part of a class (because there are no classes), but simply a function in its own right. The most common function.

make_me= function (_name) (
alert("I was launched" );
this .name=_name;

}


/* Let's figure out what's going on here. The interpreter sees the new operator and checks what is to the right of it. Because make_me is a function, and it can be used as a constructor, then a new object is created in memory and the make_me function is launched for execution, and its this points precisely to this new object. Next, this object is added with a name property, which is assigned the value from the _name argument, and a show_name method. Also (I don’t know at what point, but it doesn’t matter) the child variable starts pointing to our brand new, just born object */

Alert(child.name); //Vasya
child.show_name(); //Vasya


child2.show_name(); //Peter

Child2.show_name=function () (alert( "I won't say my name");} //Don't forget that we can change our objects at any time
child2.show_name(); //I won't say my name

Child.show_name(); //Vasya - children do not influence each other in any way


* This source code was highlighted with Source Code Highlighter.

You can also compare a designer with a father - he gives birth to a child, endowing him with certain qualities, but immediately after creation the child becomes completely independent of the parent and can become very different from his brothers.
If we remember the description of data types at the beginning of the article, it becomes clear that Object and its subtypes (Function, Array and others) are actually constructors that give the created object the capabilities of a function, array, etc.

So this is already much better. We now have the ability to create objects according to some pattern. However, all is not well yet. First, each object we create and all its properties and methods occupy a separate place in memory, although in many ways they are repeated. Secondly, what if we want to maintain the connection between parent and child, and be able to change all child objects at once. A prototype will come to our aid.

Prototype

Just as every child has a father and mother (at least in a biological sense), so does every object in JavaScript. And if the father, as we have determined, works as a designer, then the mother is precisely the prototype. Let's see how this happens:
make_me= function (_name) (
alert("I was launched" );
this .name=_name;
this .show_name=function () (alert(this .name);)
}
/*
Seeing the function keyword, the interpreter checks the code to the right of it, and so on. everything is ok - it creates a new object in memory, which is also our function. Then, automatically (without programmer intervention) a prototype property is created for this function, which refers to an empty object. If we did this manually, it would look like make_me.prototype=new Object();

Then, this object (pointed to by the prototype property) is also automatically given a constructor property, pointing back to the function. It turns out this is a cyclic link.

Now this object, which can be described as (constructor: ...here is a reference to a function...) is the prototype of the function.
*/

//Object - indeed, an object
alert(typeof make_me.prototype.constructor); //Function is our function
alert(make_me.prototype.constructor === make_me); //true

//Add a new method to the make_me function prototype

Child=new make_me("Vasya" ); //I was launched
/* Now, in addition to everything described in the previous example, an additional hidden property [] is created in the child object, which points to the same object as make_me.prototype. Because the property is hidden, we can neither view its value nor change it - however it plays important role in future work */

Alert(child.name); //Vasya
child.show_name(); //Vasya

Child.set_name("Kolya" );
/* First, the interpreter looks for the set_name method in the child object. Since it's not there, it continues searching in the child.[ property, finds it there, and runs it. */
child.show_name(); //Kolya - now Vasya’s name is Kolya :)

Make_me.prototype.show_name2=function () (alert("Hello, " + this .name;) //Because a prototype is an ordinary object, we can also change it on the fly

Child2=new make_me("Petya" );
child2.show_name2(); //Hello, Petya
child.show_name2(); //Hi, Kolya - changes in the prototype affect not only newly created objects, but also all old ones

Child2.show_name2=function () (alert( "I won't say my name");} //We can still change the object itself, and the new show_name2 method in this object (and only in it) will, as it were, “overwrite” the old method from the prototype
child2.show_name2(); //I won’t say my name - because... we now have our own method show_name2, then it is called, and the search in the prototype does not occur

Child.show_name2(); //Hello, Kolya - everything is still the same here

Make_me.prototype=(prop: "hello" ) //Let's try to recreate the prototype again

Alert(child.prop); //undefined
child.show_name2(); //Hi Kolya
/* If you remember what working by reference is, then everything is clear. Recreating the prototype breaks the connection, and now the [] property of the child and child2 objects point to one object (which was previously the prototype of the make_me function), and the make_me.prototype property points to another object, which is the new prototype of the make_me function */

Child3=new make_me("Oleg" );
alert(child3.prop); //hello - as expected


* This source code was highlighted with Source Code Highlighter.

As can be seen from the example, as long as the father remains faithful to the mother (that is, as long as the type of function remains the same), all children depend on the mother and are sensitive to all changes in her. However, as soon as the parents divorce (the designer changes the prototype to another), the children immediately run away in all directions and there is no more contact with them.

A little about terminology
As long as the primary connection between the designer and the prototype is not broken, we can observe the following picture:

make_me= function (_name) (
alert("I was launched" );
this .name=_name;
this .show_name=function () (alert(this .name);)
}

Make_me.prototype.set_name=function (_name) (this .name=_name;)
child=new make_me("Vasya" );

Alert(typeof make_me.prototype); //object - the function has a prototype property
alert(typeof child.prototype); //undefined - the created object does NOT have a prototype property
alert(child.constructor.prototype === make_me.prototype); //true - but the object has a constructor property, which points to the constructor function make_me, which, in turn, has a prototype property


* This source code was highlighted with Source Code Highlighter.

As I've noticed after reading numerous forums on this topic, the main problem people have is when they confuse the prototype property of a function with the hidden [] property of the object created by that function.
Both of these properties are a reference to the same object (as long as the primary connection of the prototype to the constructor is not broken), but they are nevertheless different properties, with different names, one of them is accessible to the programmer, and the other is not.

It is always necessary to clearly understand that if we are talking about the prototype of the constructor, then this is always the prototype property, and if we are talking about the prototype of the created object, then this is the hidden property [].

Inheritance

Now we know that every object has a hidden prototype reference, and every prototype is a regular object.
The most sensitive readers have already caught the smell of recursion :)
Indeed, because a prototype is an ordinary object, then it, in turn, has a link to its prototype, and so on. This is how the prototype hierarchy is implemented.
bird= function()() //This is the bird's constructor
bird.prototype.cry=function ()(alert("Cry!");) //The bird can scream
bird.prototype.fly=function ()(alert("I'm flying!");) //and fly

Duck=function () ()
duck.prototype=new bird();
duck.prototype.cry=function ()(alert("Quack-quack!" ;) //Duck screams differently
duck.prototype.constructor=duck; //Forcibly set the prototype.constructor property to duck, because otherwise it will refer to bird

Billy = new duck(); //Billy is our duck
billy.fly(); //I'm flying! - Billy can fly because he's a bird.
billy.cry(); //Quack quack! - Billy screams quack-quack because he's a duck.


* This source code was highlighted with Source Code Highlighter.

This way you can implement a hierarchy of any nesting level.

Star task

Now, since we know so much about all this, let's try to figure out how much is happening in these three lines
make_me= function()()
child=new make_me();
alert(child.toString()); //outputs

* This source code was highlighted with Source Code Highlighter.

On the first line we create new feature and a make_me variable that points to this function. This creates a function prototype, make_me.prototype, which contains a constructor property pointing to make_me.
But that's not all:)
Because the make_me function is also an object, then it, in turn, has a father and a mother, i.e. designer and prototype. Its constructor is a native function of the Function() language, and its prototype is an object containing the methods call, apply, etc. - It is thanks to this prototype that we can use these methods in any function. Thus, the make_me function has a [] property pointing to Function.prototype.

In turn, the prototype of a Function constructor is also an object, the constructor of which is (surprise!) Object (i.e. Function.prototype.[].constructor===Object), and the prototype is an object containing the standard properties and methods of the object, such as toString, hasOwnProperty and others (in other words - Function.prototype.[]["hasOwnProperty"] - this is exactly the method that we can use in all derived objects - and this is the own method of this object, and not an inherited one ). In this interesting way we discover that all kinds of objects are derived from Object.

Can we continue further? It turns out not. Object.prototype contains the basic properties of the object precisely because it does not have its own prototype. Object.prototype.[]=null; At this point, the journey through the prototype chain in search of a property or method stops.

Another interesting fact- the constructor of Object is Function. Those. Object.[].constructor===Function.
There is another circular reference - the constructor of Object is Function, and the constructor of Function.prototype is Object.

Let's return to our example. We have already understood how the function is created, now let’s move on to the second line. There we create a child object whose constructor is the make_me function and whose prototype is make_me.prototype.

Well, in the third line we see how the interpreter goes up the chain, from child to child.[] (aka make_me.prototype), then to child.[].[] (aka Object.prototype), and already finds there the toString method, which launches execution.

Impurities

It may seem that inheritance through prototypes is the only way possible in JavaScript. This is wrong.
We are dealing with a very flexible language that provides possibilities rather than rules.

For example, if we wish, we can not use prototypes at all, but program using the concept of mixins. For this we will need our good old friends - designers.

//This is a human constructor
man=function() (
this .live=function ()(alert("I'm live" ;) //Man knows how to live
this .walk=function ()(alert("I'm walking" ;) //The man can walk
}

//This is the poet's constructor
poet=function ()(
this .kill=function ()(alert( "The poet killed a man");} //A poet can kill a person
this .live=function ()(alert("I'm dead" ;) //A person will die from this
}

Vladimir=new man(); //Vladimir is a man
vladimir.live(); //I live - he is alive
vladimir.walk(); //I'm walking - he's walking

Poet.call(vladimir); //Execute the poet constructor for the vladimir object
vladimir.kill(); //The poet killed a man
vladimir.live(); //I'm dead

//Now focus
man.call(vladimir);
vladimir.live(); //I live


* This source code was highlighted with Source Code Highlighter.

What do we see in this example? Firstly, it is possible to inherit from several objects that are not in the same hierarchy. In the example there are 2 of them, but there can be as many as you like.
Secondly, there is no hierarchy at all. Overriding of properties and methods is determined solely by the order in which constructors are called.
Thirdly, this is the ability to change an object even more dynamically, specifically an individual object, and not all its descendants, as when changing a prototype.

Upd: Closures and private properties

In order not to bloat this already rather large article, I provide a link to the post Closures in JavaScript, where this is written in some detail.

What to do now with all this

As I said above, arbitrary modification of individual objects, the use of constructors, mixins, and the flexibility of prototypes are just tools, opportunities that allow the programmer to create both terrible and wonderful code in all respects. It is only important to understand what problems we solve, by what means, what goals we achieve and what price we pay for it.

Moreover, the question of price is quite non-trivial, especially if we are talking about development for the Internet Explorer 6 and 7 browser versions.
1. Memory - everything is simple here. In all browsers, inheritance on prototypes takes up much less memory than when creating methods through constructors. Moreover, the more methods and properties we have, the more difference. However, it is worth remembering that if we do not have a thousand identical objects, but only one, then the memory consumption in any case will be small, because There are other factors to consider here.
2. Processor time - here the main subtleties are related specifically to browsers from Microsoft.
On the one hand, objects where methods and properties are created through a constructor can be created many times (in some cases tens and hundreds of times) slower than through a prototype. The more methods, the slower it is. So if your IE freezes for a few seconds during script initialization, there is a reason to dig in this direction.

On the other hand, an object's own methods (those created through the constructor) can run a little faster than prototype methods. If you desperately need to speed up the execution of a particular method in this browser, then you need to take this into account. Keep in mind that it is the method call (i.e., searching for it in the object) that is speeded up, not its execution. So if the method itself runs for a second, then you won’t notice much of an increase in performance.

In other browsers similar problems It is observed that the time for creating objects and calling their methods is approximately the same for both approaches.

P.S. Usually in articles of this kind the author offers some kind of wrapper, either trying to implement class-object inheritance based on prototypic inheritance, or simply syntactic sugar for prototypic inheritance. I don't do this intentionally, because... I believe that a person who understands the meaning of this article is able to write any wrapper for himself, and many more interesting things :)

Tags: Add tags