JavaScript: object exploration
- Transfer
The material, the translation of which we are publishing today, is devoted to the study of objects - one of the key entities of JavaScript. It is designed mainly for novice developers who want to streamline their knowledge about objects.
Objects in JavaScript are dynamic collections of properties that, in addition, contain a “hidden” property, which is a prototype of the object. Properties of objects are characterized by keys and values. Let's start the conversation about JS objects with keys.

The property key of the object is a unique string. To access the properties, you can use two methods: accessing them through a point and specifying the object key in square brackets. When accessing properties through a dot, the key must be a valid JavaScript identifier. Consider an example:
If you attempt to access a non-existent property of the error message object, it will not appear, but the value will be returned
When used to access the properties of square brackets, you can use keys that are not valid JavaScript identifiers (for example, the key can be a string containing spaces). They can have any value that can be cast to the string:
If non-string values are used as keys, they are automatically converted to strings (using, if possible, the method
In this example, the object is used as the key
Object properties can be primitive values, objects, or functions.
Objects can be placed in other objects. Consider an example :
A similar approach can be used to create namespaces:
When a function is used as a property value of an object, it usually becomes an object method. Inside the method, to refer to the current object, a keyword is used
This keyword, however, may have different meanings, depending on how the function was called. Here you can read about situations in which it
Objects in JavaScript, by their nature, are dynamic entities. You can add properties to them at any time, the same goes for deleting properties:
Objects can be considered as associative arrays. The keys of an associative array are the names of the properties of an object. In order to gain access to the key, it is not necessary to view all the properties, that is, the operation of accessing the key of an associative array based on an object is performed in O (1) time.
Objects have a “hidden” link,
For example, an object created using an object literal has a link to
As we have just seen, the “empty” object,
This will create an object without a prototype. Such objects are usually used to create associative arrays.
Prototype objects can have their own prototypes. If you try to access the property of an object that is not in it, JavaScript will try to find this property in the prototype of this object, and if there is no desired property there, an attempt will be made to find it in the prototype prototype. This will continue until the desired property is found, or until the end of the prototype chain is reached.
JavaScript allows you to work with values of primitive types as objects, in the sense that the language allows you to access their properties and methods.
In this case, of course, the values of primitive types are not objects.
To provide access to the “properties” of values of primitive types of JavaScript, if necessary, create wrapper objects, which, after they are no longer needed, are destroyed. The process of creating and destroying wrapper objects is optimized by the JS engine.
Object wrappers have numeric, string and boolean values. Objects of the respective types of functions by design are presented
Object-numbers inherit properties and methods from the prototype
The prototype of the string objects is
Functions in JavaScript are also objects that have a prototype
All objects, functions, and objects that represent values of primitive types (except for values of
JavaScript allows you to easily extend built-in objects with new functions using so-called polyfills. Polyfill is a piece of code that implements features not supported by any browsers.
For example, there is a polyfill for a method
The same applies to the polyfill
With the help of polyfills new methods can be added to the prototypes of objects. For example, polyfill for
Polifill for
The command
The command
The command
The team
To create clones (copies) of objects, you can use the command
This command performs a shallow copy of objects, that is, it copies only the properties of the top level. Nested objects appear to be common for original objects and their copies.
Object literals give the developer a simple and intuitive way to create objects:
However, this method of creating objects has disadvantages. In particular, with this approach, all properties of an object are publicly available, object methods can be overridden, they cannot be used to create new instances of identical objects:
The two problems mentioned above can be solved by sharing the methods
Apply this technique to our previous example. First, create a frozen prototype
If the prototype is protected from changes, an object that is its successor will not be able to change the properties defined in the prototype. Now methods
The design
In JavaScript, there are so-called constructor functions, which are “syntactic sugar” for performing the above described steps for creating new objects. Consider an example :
As a constructor, you can use any function. The constructor is called using a keyword
Here, to prevent the prototype from changing, again, you can freeze the prototype:
When a view command is executed
Here a new object is created, the prototype of which is
In ECMAScript 2015, a new way to perform the actions described above has been introduced, representing another portion of “syntactic sugar”. We are talking about the keyword
An object created using a keyword
Using classes does not make prototypes unchanged. If necessary, they will have to be “frozen” just as we have already done:
In JavaScript, objects inherit properties and methods from other objects. Constructor functions and classes are “syntactic sugar” for creating prototype objects containing all the necessary methods. With their use, new objects are created which are the heirs of the prototype, whose properties that are specific to a particular instance are established using the constructor function or using class mechanisms.
It would be nice if constructor functions and classes could automatically make prototypes unchanged.
The strength of prototype inheritance is memory savings. The fact is that a prototype is created only once, after which it is used by all objects created on its basis.
The prototype inheritance pattern does not use the separation of object properties into private and public. All properties of objects are publicly available.
For example, the command
There is one pattern that imitates private properties, relying on the fact that developers will not refer to those properties whose names begin with an underscore (
Encapsulated objects in JavaScript can be created using factory functions. It looks like this:
Here the variable
The command
Here we, in the examples, used the object
In JavaScript, values of primitive types, ordinary objects and functions are perceived as objects. Objects have a dynamic nature, they can be used as associative arrays. Objects are the heirs of other objects. Constructor functions and classes are “syntactic sugar”; they allow you to create objects based on prototypes. For the organization of single inheritance, you can use the method
Dear readers! If you came to JavaScript from other languages, please tell us what you like or dislike in JS objects, in comparison with the implementation of objects in languages you already know.


Object Property Keys
The property key of the object is a unique string. To access the properties, you can use two methods: accessing them through a point and specifying the object key in square brackets. When accessing properties through a dot, the key must be a valid JavaScript identifier. Consider an example:
let obj = {
message : "A message"
}
obj.message //"A message"
obj["message"] //"A message"
If you attempt to access a non-existent property of the error message object, it will not appear, but the value will be returned
undefined
:obj.otherProperty //undefined
When used to access the properties of square brackets, you can use keys that are not valid JavaScript identifiers (for example, the key can be a string containing spaces). They can have any value that can be cast to the string:
let french = {};
french["merci beaucoup"] = "thank you very much";
french["merci beaucoup"]; //"thank you very much"
If non-string values are used as keys, they are automatically converted to strings (using, if possible, the method
toString()
):et obj = {};
//Number
obj[1] = "Number 1";
obj[1] === obj["1"]; //true//Objectlet number1 = {
toString : function() { return"1"; }
}
obj[number1] === obj["1"]; //true
In this example, the object is used as the key
number1
. When attempting to access a property, it is converted to a string 1
, and the result of this conversion is used as a key.Object Property Values
Object properties can be primitive values, objects, or functions.
▍Object as an object property value
Objects can be placed in other objects. Consider an example :
let book = {
title : "The Good Parts",
author : {
firstName : "Douglas",
lastName : "Crockford"
}
}
book.author.firstName; //"Douglas"
A similar approach can be used to create namespaces:
let app = {};
app.authorService = { getAuthors : function() {} };
app.bookService = { getBooks : function() {} };
▍Function as an object property value
When a function is used as a property value of an object, it usually becomes an object method. Inside the method, to refer to the current object, a keyword is used
this
. This keyword, however, may have different meanings, depending on how the function was called. Here you can read about situations in which it
this
loses context.Dynamic nature of objects
Objects in JavaScript, by their nature, are dynamic entities. You can add properties to them at any time, the same goes for deleting properties:
let obj = {};
obj.message = "This is a message"; //добавление нового свойства
obj.otherMessage = "A new message"; // добавление нового свойстваdelete obj.otherMessage; //удаление свойства
Objects as associative arrays
Objects can be considered as associative arrays. The keys of an associative array are the names of the properties of an object. In order to gain access to the key, it is not necessary to view all the properties, that is, the operation of accessing the key of an associative array based on an object is performed in O (1) time.
Object Prototypes
Objects have a “hidden” link,
__proto__
pointing to a prototype object, from which the object inherits properties. For example, an object created using an object literal has a link to
Object.prototype
:var obj = {};
obj.__proto__ === Object.prototype; //true
Уст Empty objects
As we have just seen, the “empty” object,
{}
in fact, is not so empty, since it contains a link to Object.prototype
. In order to create a truly empty object, you need to use the following construction:Object.create(null)
This will create an object without a prototype. Such objects are usually used to create associative arrays.
ПротA prototype chain
Prototype objects can have their own prototypes. If you try to access the property of an object that is not in it, JavaScript will try to find this property in the prototype of this object, and if there is no desired property there, an attempt will be made to find it in the prototype prototype. This will continue until the desired property is found, or until the end of the prototype chain is reached.
Primitive Type Values and Object Wrappers
JavaScript allows you to work with values of primitive types as objects, in the sense that the language allows you to access their properties and methods.
(1.23).toFixed(1); //"1.2""text".toUpperCase(); //"TEXT"true.toString(); //"true"
In this case, of course, the values of primitive types are not objects.
To provide access to the “properties” of values of primitive types of JavaScript, if necessary, create wrapper objects, which, after they are no longer needed, are destroyed. The process of creating and destroying wrapper objects is optimized by the JS engine.
Object wrappers have numeric, string and boolean values. Objects of the respective types of functions by design are presented
Number
, String
and Boolean
.Embedded Prototypes
Object-numbers inherit properties and methods from the prototype
Number.prototype
, which is the successor Object.prototype
:var no = 1;
no.__proto__ === Number.prototype; //trueno.__proto__.__proto__ === Object.prototype; //true
The prototype of the string objects is
String.prototype
. The prototype of logical value objects is Boolean.prototype
. The prototype of arrays (which are also objects) is Array.prototype
. Functions in JavaScript are also objects that have a prototype
Function.prototype
. Functions have methods like bind()
, apply()
and call()
. All objects, functions, and objects that represent values of primitive types (except for values of
null
and undefined
) inherit properties and methods from Object.prototype
. This leads to the fact that, for example, they all have a method toString()
.Extending embedded objects with polyfills
JavaScript allows you to easily extend built-in objects with new functions using so-called polyfills. Polyfill is a piece of code that implements features not supported by any browsers.
▍Using polyfills
For example, there is a polyfill for a method
Object.assign()
. It allows you to add a Object
new function if it is not available in it. The same applies to the polyfill
Array.from()
, which, if Array
there is no method in the object from()
, equips it with this method.▍Polythills and prototypes
With the help of polyfills new methods can be added to the prototypes of objects. For example, polyfill for
String.prototype.trim()
allows you to equip all string objects with the method trim()
:let text = " A text ";
text.trim(); //"A text"
Polifill for
Array.prototype.find()
equips all arrays method find()
. Similarly, the polyfill works for Array.prototype.findIndex()
:let arr = ["A", "B", "C", "D", "E"];
arr.indexOf("C"); //2
Single inheritance
The command
Object.create()
allows you to create new objects with a given prototype object. This command is used in JavaScript to implement a single inheritance mechanism. Consider an example :let bookPrototype = {
getFullTitle : function(){
returnthis.title + " by " + this.author;
}
}
let book = Object.create(bookPrototype);
book.title = "JavaScript: The Good Parts";
book.author = "Douglas Crockford";
book.getFullTitle();//JavaScript: The Good Parts by Douglas Crockford
Multiple inheritance
The command
Object.assign()
copies properties from one or more objects to the target object. It can be used to implement a multiple inheritance scheme. Here is an example :let authorDataService = { getAuthors : function() {} };
let bookDataService = { getBooks : function() {} };
let userDataService = { getUsers : function() {} };
let dataService = Object.assign({},
authorDataService,
bookDataService,
userDataService
);
dataService.getAuthors();
dataService.getBooks();
dataService.getUsers();
Immunity objects
The command
Object.freeze()
allows you to "freeze" the object. In such an object can not add new properties. Properties cannot be deleted, and their values cannot be changed. By using this command, the object becomes immutable or immutable:"use strict";
let book = Object.freeze({
title : "Functional-Light JavaScript",
author : "Kyle Simpson"
});
book.title = "Other title";//Ошибка: Cannot assign toreadonly property 'title'
The team
Object.freeze()
performs the so-called "shallow freezing" of objects. This means that objects nested in a “frozen” object can be modified. In order to implement a “deep freezing” of an object, one must recursively “freeze” all its properties.Cloning objects
To create clones (copies) of objects, you can use the command
Object.assign()
:let book = Object.freeze({
title : "JavaScript Allongé",
author : "Reginald Braithwaite"
});
let clone = Object.assign({}, book);
This command performs a shallow copy of objects, that is, it copies only the properties of the top level. Nested objects appear to be common for original objects and their copies.
Object literal
Object literals give the developer a simple and intuitive way to create objects:
let timer = {
fn : null,
start : function(callback) { this.fn = callback; },
stop : function() {},
}
However, this method of creating objects has disadvantages. In particular, with this approach, all properties of an object are publicly available, object methods can be overridden, they cannot be used to create new instances of identical objects:
timer.fn;//null
timer.start = function() { console.log("New implementation"); }
Object.create () method
The two problems mentioned above can be solved by sharing the methods
Object.create()
and Object.freeze()
. Apply this technique to our previous example. First, create a frozen prototype
timerPrototype
containing all the methods needed by different instances of the object. After that, create an object that is a successor timerPrototype
:let timerPrototype = Object.freeze({
start : function() {},
stop : function() {}
});
let timer = Object.create(timerPrototype);
timer.__proto__ === timerPrototype; //true
If the prototype is protected from changes, an object that is its successor will not be able to change the properties defined in the prototype. Now methods
start()
and stop()
override can not be:"use strict";
timer.start = function() { console.log("New implementation"); } //Ошибка: Cannot assign to read only property 'start' of object
The design
Object.create(timerPrototype)
can be used to create multiple objects with the same prototype.Constructor function
In JavaScript, there are so-called constructor functions, which are “syntactic sugar” for performing the above described steps for creating new objects. Consider an example :
functionTimer(callback){
this.fn = callback;
}
Timer.prototype = {
start : function() {},
stop : function() {}
}
functiongetTodos() {}
let timer = new Timer(getTodos);
As a constructor, you can use any function. The constructor is called using a keyword
new
. An object created using the constructor function named FunctionConstructor
will get a prototype FunctionConstructor.prototype
:lettimer = newTimer();
timer.__proto__ === Timer.prototype;
Here, to prevent the prototype from changing, again, you can freeze the prototype:
Timer.prototype = Object.freeze({
start : function() {},
stop : function() {}
});
▍ Keyword new
When a view command is executed
new Timer()
, the same actions are performed as the following function performs newTimer()
:functionnewTimer(){
let newObj = Object.create(Timer.prototype);
let returnObj = Timer.call(newObj, arguments);
if(returnObj) return returnObj;
return newObj;
}
Here a new object is created, the prototype of which is
Timer.prototype
. Then a function is called Timer
that sets the fields for the new object.Keyword class
In ECMAScript 2015, a new way to perform the actions described above has been introduced, representing another portion of “syntactic sugar”. We are talking about the keyword
class
and the corresponding structures associated with it. Consider an example :class Timer{
constructor(callback){
this.fn = callback;
}
start() {}
stop() {}
}
Object.freeze(Timer.prototype);
An object created using a keyword
class
based on a class with a name ClassName
will have a prototype ClassName.prototype
. When creating an object based on a class, use the keyword new
:lettimer= newTimer();
timer.__proto__ === Timer.prototype;
Using classes does not make prototypes unchanged. If necessary, they will have to be “frozen” just as we have already done:
Object.freeze(Timer.prototype);
Prototype Inheritance
In JavaScript, objects inherit properties and methods from other objects. Constructor functions and classes are “syntactic sugar” for creating prototype objects containing all the necessary methods. With their use, new objects are created which are the heirs of the prototype, whose properties that are specific to a particular instance are established using the constructor function or using class mechanisms.
It would be nice if constructor functions and classes could automatically make prototypes unchanged.
The strength of prototype inheritance is memory savings. The fact is that a prototype is created only once, after which it is used by all objects created on its basis.
▍The problem of the lack of built-in encapsulation mechanisms
The prototype inheritance pattern does not use the separation of object properties into private and public. All properties of objects are publicly available.
For example, the command
Object.keys()
returns an array containing all property keys of the object. It can be used to iterate through all the properties of an object:function logProperty(name){
console.log(name); //имя свойства
console.log(obj[name]); //значение свойства
}
Object.keys(obj).forEach(logProperty);
There is one pattern that imitates private properties, relying on the fact that developers will not refer to those properties whose names begin with an underscore (
_
):classTimer{
constructor(callback){
this._fn = callback;
this._timerId = 0;
}
}
Factory Functions
Encapsulated objects in JavaScript can be created using factory functions. It looks like this:
function TodoStore(callback){
let fn = callback;
functionstart() {},
function stop() {}
returnObject.freeze({
start,
stop
});
}
Here the variable
fn
is private. Only methods start()
and are generally available stop()
. These methods cannot be modified from the outside. The keyword is not used here this
, so when using this method of creating objects, the problem of loss of context this
is irrelevant. The command
return
uses an object literal containing only functions. Moreover, these functions are declared in closure, they share a common state. To “freeze” the public API of an object, the command you already know is used Object.freeze()
. Here we, in the examples, used the object
Timer
. In this material you can find its full implementation.Results
In JavaScript, values of primitive types, ordinary objects and functions are perceived as objects. Objects have a dynamic nature, they can be used as associative arrays. Objects are the heirs of other objects. Constructor functions and classes are “syntactic sugar”; they allow you to create objects based on prototypes. For the organization of single inheritance, you can use the method
Object.create()
for the organization of multiple inheritance - метод Object.assign()
. You can use factory functions to create encapsulated objects. Dear readers! If you came to JavaScript from other languages, please tell us what you like or dislike in JS objects, in comparison with the implementation of objects in languages you already know.
