Competent
- Object - Object
- Regular Expressions
- Dependancy injection (common, AMD, namespase)
- Inheritance
- Currying and chaining
- ES6: Classes
- ES6: Destructuring assignment
- ES6: template strings
- ES6: super
- ES6: modules
- Module Loaders
- Proxies, Reflect
- WeakMap + WeakSet
- Async/await
Object - Object
Ассоциативный массив – структура данных, в которой можно хранить любые данные в формате ключ-значение.
- Objects have a “hidden” link
__proto__to a prototype object, from which they inherit properties.
var obj = {};
obj.__proto__ === Object.prototype; //true
- Built-in prototypes (Number.prototype, String.prototype, Boolean.prototype, Function.prototype, ...)
var no = 1;
no.__proto__ === Number.prototype; //true
no.__proto__.__proto__ === Object.prototype; //true
- Immutable objects with
Object.freeze()
// create
const user = new Object();
const admin = {};
Methods
- .keys()
- .values()
- .entries() - creates a nested array of the key/value pairs of an object.
- .assign() is used to copy values from one object to another.
- .freeze() prevents modification to properties and values of an object, and prevents properties from being added or removed from an object.
- .seal() prevents new properties from being added to an object, but allows the modification of existing properties.
- .getPrototypeOf() is used to get the internal hidden
[[Prototype]]of an object, also accessible through the proto property.
Regular Expressions
const regexp = /template/flags
- flags
g- глобальный поиск (не прекращается после первого совпадения)i- без учета (игнорирует) регистраm- многострочный режим, поиск совпадений продолжается после достижения конца одной строки теста

- string methods: search() and replace()
const n = str.search(/w3schools/i);
const res = str.replace("Microsoft", "W3Schools");
const result = str.match( /JAVA(SCRIPT)/i );
test()searches a string for a pattern, and returns true or false, depending on the result.
const pattern = /e/;
pattern.test("The best things in life are free!");
exec()searches a string for a specified pattern, and returns the found text as an object
Dependancy injection (common, AMD, namespace)
CommonJS
separate approach to interact with the module system using the keywords
requireandexports.
requireis a function used to import functions from another module.exportsis an object where any function put into it will get exported.- synchronous in nature
//------ payments.js ------
var customerStore = require('store/customer'); // import module
//------ store/customer.js ------
exports = function(){
return customers.get('store');
}
AMD (Asynchronous Module Definition)
supports asynchronous module loading
define(['module1', ',module2'], function(module1, module2) {
console.log(module1.setName());
});
The function is called only when the requested modules are finished loading. The define function takes the first argument as an array of dependency modules. These modules are loaded in a non-blocking manner in the background and once the loading is completed, the callback function is executed.
It is designed to be used in browsers for better startup times and these modules can be objects, functions, constructors, strings, JSON, etc. Modules can be split in multiple files, which are compatible for require and exports and circular dependencies are supported as well.
RequireJS implements the AMD API
Inheritance
Inheritance
Inheritance in most class-based object-oriented languages is a mechanism in which one object acquires all the properties and behaviors of another object. JavaScript is not a class-based language although class keyword is introduced in ES2015, it is just syntactical layer. JavaScript still works on prototype chain.

Prototypal Inheritance (Behavior delegation pattern)
v1 and v2 are linked to Vehicle.prototype because it’s been created using new keyword. Similarly, c1 and c2 is linked to Car.prototype and Car.prototype is linked to Vehicle.prototype. In JavaScript when we create the object it does not copy the properties or behavior, it creates a link. Similar kind of linkage gets created in case of extend of class as well. All arrows go in the opposite direction compare to classical non-js inheritance because it’s a behavior delegation link. These links are known as prototype chain. This pattern is called Behavior Delegation Pattern which commonly known as prototypal inheritance in JavaScript.

Currying and chaining
Currying
Currying
currying is the technique of translating the evaluation of a function that takes multiple arguments (or a tuple of arguments) into evaluating a sequence of functions, each with a single argument.
We will be writing a simple curry function for number addition, which will allow to chain mutliple calls with varying arguments.
//add(a, b)(c)(d)
//return a + b + c + d
const coreAdd = (...rest) => {
const args = rest;
return args.reduce((acc, element) => (
acc += element
), 0);
}
function addCurry() {
const args = Array.prototype.slice.call(arguments);
return function() {
const newArgs = Array.prototype.slice.call(arguments);
const totalArgs = newArgs.concat(args);
return newArgs.length === 0
? coreAdd(...totalArgs) :
: addCurry(...totalArgs);
}
}
const addValue = addCurry(2, 3)(1)(8, 5)();
console.log(addValue);
Currying
A function which accepts a one or more arguments of func which wither invoked when all the arguments are passed or return a function which accepts one or remaining arguments.
var add = function(a){
return function(b){
return a + b;
};
};
add(5)(100) //returns 105
Chaining
$('#main').addClass('red').removeClass('green');

ES6: Classes
Benefits of classes
- Less code setup — Who doesn’t want to write less code? With ES6 classes the code to write a function is much lesser compared to the traditional method.
- Constructor function — it provides a clear function to create objects.
- All the logic is contained — You can clearly specify the methods and properties in one place instead of creating one constructor function and defining methods on the prototype one-by-one.
- Создавая класс, вы пользуетесь блоком кода (всё, что находится между { и }), внутри которого объявляете, всё, что хотите видеть в прототипе.
- Запись class Person означает, что будет создана функция конструктор Person (всё точно так же, как и в ES5)
- Свойство constructor используется для обозначение того, что будет происходить непосредственно в самом конструкторе.
- Все методы для класса используют краткую запись, которую мы обсуждали ранее в статье про расширение литерала объектов.
- При перечислении методов не надо использовать запятые (на самом деле, они запрещены)
class Circle extends Shape {
// Constructor
constructor (radius) {
this.radius = radius
}
// Methods
getArea () {
return Math.PI * 2 * this.radius
}
// Calling superclass methods
expand (n) {
return super.expand(n) * Math.PI
}
// Static methods
static createFromDiameter(diameter) {
return new Circle(diameter / 2)
}
}
The class is just an illusion — Javascript did not add any additional features. It still follows prototypal inheritance under the hood.
The classes require the use of new keyword — Whenever instantiating an object of a class, the new keyword must be used.
super & extends
class Dog extends Animal {
constructor(name, sound = 'noise'){
super(name);
this.sound = sound;
speak() {
console.log(this.name + this.sound);
}
}
ES6: Destructuring assignment
Destructuring simply implies breaking down a complex structure into simpler parts. In JavaScript, this complex structure is usually an object or an array. With the destructuring syntax, you can extract smaller fragments from arrays and objects. Destructuring syntax can be used for variable declaration or variable assignment. You can also handle nested structures by using nested destructuring syntax.
Destructuring assignment
// Array const [first, last] = ['Nikola', 'Tesla']Object let { title, author } = { title: 'The Silkworm', author: 'R. Galbraith' }Loops
for (let {title, artist} of songs) { // ··· }default values
const scores = [22, 33]; const [math = 50, sci = 50, arts = 50] = scores;Reassigning keys
function printCoordinates({ left: x, top: y }) { console.log(`x: ${x}, y: ${y}`) } printCoordinates({ left: 25, top: 90 })Function arguments
function greet({ name, greeting }) { console.log(`${greeting}, ${name}!`); } greet({ name: 'Larry', greeting: 'Ahoy' });Default values
function greet({ name = 'Rauno' } = {}) { console.log(`Hi ${name}!`); } greet() // Hi Rauno! greet({ name: 'Larry' }) // Hi Larry!Rest destructuring
const { id, ...detail } = song; const [head, ...tail] = array;Skip values
let [,,third] = ["foo", "bar", "baz"];Nested destructuring
const { name, scores: {maths, science = 50} } = student;Mixed
const {name, location: {country, city, coordinates: [lat, lng]}} = person;
ES6: template strings
- Interpolation
const message = `Hello ${name}`
- Multiline strings
const str = `
hello
world
`
- In Styled Components template tags are used to define CSS strings
const Button = styled.button`
font-size: 1.5em;
background-color: black;
color: white;
`
ES6: super
Ключевое слово super используется для вызова функций, принадлежащих родителю объекта.
Выражения: super.prop и super[expr] - действительны в любом методе определения в обоих классах и в литералах объекта.
super([arguments]); // вызов родительского конструктора.
super.functionOnParent([arguments]);
В конструкторе ключевое слово super() используется как функция, вызывающая родительский конструктор. Её необходимо вызвать до первого обращения к ключевому слову this в теле конструктора. Ключевое слово super также может быть использовано для вызова функций родительского объекта.
ES6: modules
The goal for ECMAScript 6 modules was to create a format that both users of CommonJS and of AMD are happy with:
- Similarly to CommonJS, they have a compact syntax, a preference for single exports and support for cyclic dependencies.
- Similarly to AMD, they have direct support for asynchronous loading and configurable module loading.
Being built into the language allows ES6 modules to go beyond CommonJS and AMD (details are explained later):
- Their syntax is even more compact than CommonJS’s.
- Their structure can be statically analyzed (for static checking, optimization, etc.).
- Their support for cyclic dependencies is better than CommonJS’s.
The ES6 module standard has two parts:
- Declarative syntax (for importing and exporting)
- Programmatic loader API: to configure how modules are loaded and to conditionally load modules
Name exports
//------ lib.js ------
export const sqrt = Math.sqrt;
export function square(x) {
return x * x;
}
export function diag(x, y) {
return sqrt(square(x) + square(y));
}
//------ main.js ------
import { square, diag } from 'lib';
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5
//------ main.js ------
import * as lib from 'lib';
console.log(lib.square(11)); // 121
console.log(lib.diag(4, 3)); // 5
Default exports (one per module)
//------ myFunc.js ------
export default function () { ... };
//------ main1.js ------
import myFunc from 'myFunc';
myFunc();
Mixed named & default exports
//------ underscore.js ------
export default function (obj) {
//...
};
export function each(obj, iterator, context) {
//...
}
export { each as forEach };
//------ main.js ------
import _, { each } from 'underscore';
Cyclical Dependencies
// lib.js
import Main from 'main';
var lib = {message: "This Is A Lib"};
export { lib as Lib };
// main.js
import { Lib } from 'lib';
export default class Main {
// ....
}
Module Loaders
The static import statement is used to import bindings which are exported by another module. Imported modules are in strict mode whether you declare them as such or not. The import statement cannot be used in embedded scripts unless such script has a type="module".
There is also a function-like dynamic import(), which does not require scripts of type="module".
Backward compatibility can be ensured using attribute nomodule on the script tag.
Dynamic import is useful in situations where you wish to load a module conditionally, or on demand. The static form is preferable for loading initial dependencies, and can benefit more readily from static analysis tools and tree shaking.
Syntax
import defaultExport from "module-name";
import * as name from "module-name";
import { export } from "module-name";
import { export as alias } from "module-name";
import { export1 , export2 } from "module-name";
import { foo , bar } from "module-name/path/to/specific/un-exported/file";
import { export1 , export2 as alias2 , [...] } from "module-name";
import defaultExport, { export [ , [...] ] } from "module-name";
import defaultExport, * as name from "module-name";
import "module-name";
const promise = import("module-name"); // This is a stage 3 proposal.
Proxies, Reflect
Reflect
Reflection/Reflect API — это API, который предоставляет возможность проводить реверс-инжиниринг классов, интерфейсов, функций, методов и модулей.
Reflection API существует в разных языках программирования и, порой, используется для обхода ограничений, накладываемых ЯП. Также он используется для разработки различных вспомогательных утилит и для реализации различных паттернов (таких как Injection) и много чего еще.
В JavaScript Reflect — это встроенный объект, который предоставляет методы для перехватывания JavaScript операций. По сути, это неймспейс (как и Math). Reflect содержит в себе набор функций, которые называются точно так же, как и методы для Proxy.
Некоторые из этих методов — те же, что и соответствующие им методы класса Object или Function. JavaScript растет и превращается в большой и сложный ЯП. В язык приходят различные вещи из других языков. На сегодня Reflect API умеет не так много, как в других ЯП. Тем не менее, есть предложения по расширению, которые еще не вошли в стандарт, но уже используются. Например, Reflection Metadata.
Можно сказать, что неймспейс Reflect в JS — это результат рефакторинга кода. Мы уже пользовались ранее возможностями Reflect API, просто все эти возможности были вшиты в базовый класс Object.
Reflect vs Proxy
Reflect — это набор полезных методов для работы с объектами, половина которых — это переписанные уже существующие из Object. Сделано это с целью улучшения семантики и наведения порядка, так как Object — это базовый класс, но при этом он содержит очень много методов, которые не должны в нем находиться. Также если вы создаете объект с пустым прототипом, то у вас исчезают методы рефлексии.
Proxy — это класс, который всегда создает новый объект с установленными обработчиками для перехвата доступа. Он позволяет отлавливать любые действия с объектом и модифицировать их. Для реализации различной логики часто применяется Reflect.
Use Cases
- Автогенерируемые поля объекта (Мы можем создать объект, в котором поля объекта будут создаваться автоматически во время доступа к ним)
const emptyObj = () =>
new Proxy({},
{
get: (target, key, receiver) => (
key == 'toJSON'
? () => target
: (
Reflect.has(target, key) ||
Reflect.set(target, key, emptyObj()),
Reflect.get(target, key, receiver)
)
)
}
)
;
const path = emptyObj();
path.to.virtual.node.in.empty.object = 123;
console.log(JSON.stringify(path));
// {"to":{"virtual":{"node":{"in":{"empty":{"object":123}}}}}}
- Динамический вызов конструктора
// Old method
function Greeting(name) { this.name = name }
Greeting.prototype.greet = function() { return `Hello ${this.name}` }
function greetingFactory(name) {
var instance = Object.create(Greeting.prototype);
Greeting.call(instance, name);
return instance;
}
var obj = greetingFactory('Tuturu');
obj.greet();
// Reflect
class Greeting {
constructor(name) { this.name = name }
greet() { return `Hello ${this.name}` }
}
const greetingFactory = name => Reflect.construct(Greeting, [name]);
const obj = greetingFactory('Tuturu');
obj.greet();
Reflect API более удобен при обработке ошибок. К примеру, всем знакома инструкция: Object.defineProperty(obj, name, desc)
В случае неудачи будет выброшено исключение. А вот Reflect не генерит исключений на все подряд, а умеет возвращать булев результат:
const foo = Object.freeze({ bar: 1 });
if (Reflect.deleteProperty(foo, 'bar')) {
console.log('ok');
} else {
console.log('error');
}
Function.prototype.apply.call(func, obj, args)
/* --- OR --- */
Reflect.apply.call(func, obj, args)
Object.getPrototypeOf(1); // undefined
Reflect.getPrototypeOf(1); // TypeError
Reflect API — это результат рефакторинга. В этом неймспейсе содержатся функции рефлексии, которые раньше были зашиты в базовые классы Object, Function… Изменено поведение и обработка ошибок. В будущем этот неймспейс будет расширяться другими рефлективными инструментами. Так же Reflect API можно считать неотъемлемой частью при работе с Proxy
Proxy
Прокси (proxy) – особый объект, смысл которого – перехватывать обращения к другому объекту и, при необходимости, модифицировать их.
let proxy = new Proxy(target, handler);
- target – объект, обращения к которому надо перехватывать.
- handler – объект с «ловушками»: функциями-перехватчиками для операций к target.
Почти любая операция может быть перехвачена и обработана прокси до или даже вместо доступа к объекту target, например: чтение и запись свойств, получение списка свойств, вызов функции (если target – функция) и т.п.
Proxy is a new API that allows intercepting, modifying and extending objects at runtime. Using this API, you can:
- Profile and debug logs,
- Intercept calls to properties,
- Validate «on the fly»,
Proxy is a constructor that accepts two parameters: source object and object that acts as a handler for the source object. The latter contains methods that are known as Traps.
A Trap is a method that modifies the behaviour of some part of the object. For example, the trap gets and set intercept the calls to properties to obtain and establish a value respectively, being able to place logic before and during this process.
Proxy can be used also to override more fundamental behaviors. Proxies are created like this:
new Proxy(target, [traps])
The second argument is an object containing handler functions for various operations, these include:
get/set: getting/setting a property.
get(target, property, receiver)Срабатывает при чтении свойства из прокси.
- target – целевой объект, тот же который был передан первым аргументом в new Proxy.
- property – имя свойства.
- receiver – объект, к которому было применено присваивание. Обычно сам прокси, либо прототипно наследующий от него. Этот аргумент используется редко.
set(target, property, value, receiver)Срабатывает при записи свойства в прокси.
- target – целевой объект, тот же который был передан первым аргументом в new Proxy.
- property – имя свойства.
- value – значение свойства.
- receiver – объект, к которому было применено присваивание, обычно сам прокси, либо прототипно наследующий от него.
has: overrides behavior of in operator.
Ловушка has срабатывает в операторе in и некоторых других случаях, когда проверяется наличие свойства.
getPrototypeOf/setPrototypeOf: Obtaining or setting an object's prototype.
apply/construct: invoking a function, or overriding the new operator.
Прокси умеет работать не только с обычными объектами, но и с функциями. Если аргумент target прокси – функция, то становится доступна ловушка apply для её вызова.
apply(target, thisArgument, argumentsList)- target – исходный объект.
- thisArgument – контекст this вызова.
- argumentsList – аргументы вызова в виде массива.
ownKeys: obtaining the keys of an object. e.g: Object.keys, Reflect.ownKeys, etc.
deleteProperty по синтаксису аналогична get/has.
Срабатывает при операции delete, должна вернуть true, если удаление было успешным.
construct перехватывает вызовы при помощи new.
construct(target, argumentsList)
Полный список
- getPrototypeOf – перехватывает обращение к методу getPrototypeOf.
- setPrototypeOf – перехватывает обращение к методу setPrototypeOf.
- isExtensible – перехватывает обращение к методу isExtensible.
- preventExtensions – перехватывает обращение к методу preventExtensions.
- getOwnPropertyDescriptor – перехватывает обращение к методу getOwnPropertyDescriptor.
- defineProperty – перехватывает обращение к методу defineProperty.
- has – перехватывает проверку существования свойства, которая используется в операторе in и в некоторых других методах встроенных объектов.
- get – перехватывает чтение свойства.
- set – перехватывает запись свойства.
- deleteProperty – перехватывает удаление свойства оператором delete.
- enumerate – срабатывает при вызове for..in или for..of, возвращает итератор для свойств объекта.
- ownKeys – перехватывает обращения к методу getOwnPropertyNames.
- apply – перехватывает вызовы target().
- construct – перехватывает вызовы new target().
Каждый перехватчик запускается с handler в качестве this. Это означает, что handler кроме ловушек может содержать и другие полезные свойства и методы. Каждый перехватчик получает в аргументах target и дополнительные параметры в зависимости от типа. Если перехватчик в handler не указан, то операция совершается, как если бы была вызвана прямо на target.
TIP
Proxy позволяет модифицировать поведение объекта как угодно, перехватывать любые обращения к его свойствам и методам, включая вызовы для функций. Особенно приятна возможность перехватывать обращения к отсутствующим свойствам, разработчики ожидали её уже давно.
WeakMap + WeakSet
WeakMap
WeakMap
WeakMaps are maps where the key (object) is weakly held, so that GC is free to collect the entry if it’s the last reference to an object.
WeakMaps are a variation on maps, which has most of the same external behavior but differs underneath in how the memory allocation (specifically its GC) works. WeakMaps take (only) objects as keys. Those objects are held weakly, which means if the object itself is GC’d, the entry in the WeakMap is also removed. This isn’t observable behavior, though, as the only way an object can be GC’d is if there’s no more references to it, but once there are no more references to it—you have no object reference to check if it exists in the WeakMap. Otherwise, the API for WeakMap is similar, though more limited:
const m = new WeakMap();
const x = { id: 1 };
const y = { id: 2 };
m.set(x, "foo");
m.has(x); // true
m.has(y); // false
WeakMaps do not have a size property or clear() method, nor do they expose any iterators over their keys, values, or entries. So even if you unset the x reference, which will remove its entry from m upon GC, there is no way to tell. You’ll just have to take JavaScript’s word for it! Just like Maps, WeakMaps let you soft-associate information with an object. But they are particularly useful if the object is not one you completely control, such as a DOM element. If the object you’re using as a map key can be deleted and should be GC-eligible when it is, then a WeakMap is a more appropriate option.
TIP
It’s important to note that a WeakMap only holds its keys weakly, not its values.
const m = new WeakMap();
let x = { id: 1 };
let y = { id: 2 };
const z = { id: 3 };
let w = { id: 4 };
m.set(x, y);
x = null; // { id: 1 } is GC-eligible
y = null; // { id: 2 } is GC-eligible
// only because { id: 1 } is
m.set(z, w);
w = null; // { id: 4 } is not GC-eligible
For this reason, WeakMaps are in my opinion better named “Weak‐ KeyMaps.”
WeakSet
WeakSet
WeakSets are sets where the value is weakly held, again so that GC can remove the entry if it’s the last reference to that object.
Whereas a WeakMap holds its keys weakly (but its values strongly), a WeakSet holds its values weakly (there aren’t really keys).
const s = new WeakSet();
let x = { id: 1 };
let y = { id: 2 };
s.add( x );
s.add( y );
x = null; // `x` is GC-eligible
y = null; // `y` is GC-eligible
WeakSet values must be objects, not primitive values as is allowed with sets. You can’t iterate over WeakSet. Its values must be unique object references. If nothing else is referencing a value found in a WeakSet, it’ll be subject to garbage collection.
you can only .add, .has, and .delete values from a WeakSet. And just like in Set, there’s no .get.
Async/await
The word “async” before a function means one simple thing: a function always returns a promise. Even If a function actually returns a non-promise value, prepending the function definition with the “async” keyword directs JavaScript to automatically wrap that value in a resolved promise.
The keyword await makes JavaScript wait until that promise settles and returns its result.
async function msg() {
const msg = await scaryClown();
console.log('Message:', msg);
}
await is a new operator used to wait for a promise to resolve or reject. It can only be used inside an async function.
async function msg() {
const [a, b, c] = await Promise.all([who(), what(), where()]);
console.log(`${ a } ${ b } ${ c }`);
}
Async functions always return a promise
Error Handling
async function msg() {
try {
const msg = await yayOrNay();
console.log(msg);
} catch(err) {
console.log(err);
}
}
Summary
The async keyword before a function has two effects:
- makes it always return a promise.
- allows to use await in it.
The await keyword before a promise makes JavaScript wait until that promise settles, and then: *If it’s an error, the exception is generated, same as if throw error were called at that very place.
- Otherwise, it returns the result, so we can assign it to a value.
Together they provide a great framework to write asynchronous code that is easy both to read and write.
With async/await we rarely need to write promise.then/catch, but we still shouldn’t forget that they are based on promises, because sometimes (e.g. in the outermost scope) we have to use these methods. Also Promise.all is a nice thing to wait for many tasks simultaneously.