Skip to content

Instantly share code, notes, and snippets.

@YozhEzhi
Created September 4, 2020 20:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save YozhEzhi/4d176ede8b0ab5af694a00016f2190d7 to your computer and use it in GitHub Desktop.
Save YozhEzhi/4d176ede8b0ab5af694a00016f2190d7 to your computer and use it in GitHub Desktop.
Стефанов Стоян - JavaScript. Шаблоны

// ================================================================ // Стефанов - JS. Шаблоны // ================================================================ /** ВСТУПЛЕНИЕ

Шаблоны программирования представляют собой методы решения наи- более типичных задач веб-разработки.

Объект в js – это всего лишь коллекция именованных свойств, список пар ключ-значение (во многом идентичный ассоциативным массивам в других языках программирования).

В js - не классов, в js - есть объекты. В книге, написанной «бандой четырех», говорится: «предпочтение от- дается приему составления объектов, а не наследованию». Это означает, что создание объектов из компонентов, имеющихся под рукой, являет- ся более предпочтительным, чем создание длинных иерархий насле- дования родитель–потомок. Следовать этому совету в JavaScript очень легко, потому что в языке отсутствуют классы, а конструирование объ- ектов – это, собственно, как раз то, что вы так или иначе делаете.

Наследование в js можно реализовать несколькими способами, которые обычно опираются на использование прототипов. Прототип – это объ- ект, а каждая создаваемая вами функция получает свойство prototype, ссылающееся на новый пустой объект.

Этот объект практически идентичен тому, который создается литераль- ной формой или с помощью конструктора Object(), за исключением того, что свойство constructor этого объекта ссылается на созданную вами функцию, а не на встроенный конструктор Object(). */

/** ОСНОВЫ

Глобальные переменные. Предположим, что один из сторонних сценариев определяет глобальную переменную с именем, например, result. Далее, в одной из своих функ- ций вы также определяете глобальную переменную с именем result. В результате стОит функции изменить значение переменной result, и сторонний сценарий может перестать работать. Поэтому так важно сиспользовать как можно меньше глобальных переменных. Самый простой способ уменьшить кол-во глобальных переменных - использовать var (const, let). */

/** ФУНКЦИИ

Мемоизация (кэширование). Функция myFunc создает свойство cache, доступное как myFunc.cache. Свойство cache – это объект (хеш), в котором параметр param, переданный функции, используется в качестве ключа, а результат вычислений – в качестве значения. */ var myFunc = function (param) { if (!myFunc.cache[param]) { var result = {}; // ... продолжительные операции ... myFunc.cache[param] = result; }

return myFunc.cache[param]; };

// создание хранилища результатов myFunc.cache = {};

/** Частичное приминение. Каррирование. В результате частичного применения получается другая функция, которую можно вызывать с оставшимися аргументами. Процесс, в результате которого появляются функции, обладающие возможностью частичного применения, называется каррированием (currying). Если обнаружится, что вы неоднократно вызываете одну и ту же функ- цию, передавая ей практически одни и те же параметры, эта функция наверняка является отличным кандидатом на каррирование. Вы може- те создать новую функцию, используя прием применения части пара- метров к оригинальной функции. Новая функция будет хранить повто- ряющиеся параметры (благодаря чему вам не придется передавать их каждый раз) и использовать их для заполнения полного списка аргу- ментов, ожидаемых оригинальной функцией. Первый вызов add() создает замыкание с внутренней функцией, которая возвращается в виде результата. */ // каррированная функция add() // принимает неполный список аргументов function add(x, y) { if (typeof y === 'undefined') { // partial return function (y) { return x + y; }; }

// полное применение return x + y; }

// проверка typeof add(5); // “function” add(3)(4); // 7

// создать и сохранить новую функцию var add2000 = add(2000); add2000(10); // 2010

/** ШАБЛОНЫ ПОВТОРНОГО ИСПОЛЬЗОВАНИЯ ПРОГРАММНОГО КОДА.

Хотя мы и обсуждаем классические шаблоны, давайте тем не менее не будем употреблять слово «класс». Термин «функция- конструктор», или просто «конструктор», длиннее, но он более точный и однозначный. Старайтесь вообще не употреблять сло- во «класс» в своем коллективе, потому что в контексте языка JavaScript для разных людей это слово может иметь разные зна- чения.

/** Связывание контекста.

Давайте посмотрим, как можно реализовать метод Function.prototype. bind() для использования в программах, выполняющихся в окружени- ях, не поддерживающих стандарт ES5: */ if (typeof Function.prototype.bind === “undefined”) { Function.prototype.bind = function (thisArg) { var fn = this; var slice = Array.prototype.slice; var args = slice.call(arguments, 1);

return function () {
  return fn.apply(thisArg, args.concat(slice.call(arguments)));
};

}; }

var one = { name: 'object', say: function (greet) { return greet + ', ' + this.name; }, };

var two = { name: 'another object', };

var twosay2 = one.say.bind(two); twosay2('Bonjour'); // 'Bonjour, another object'

var twosay3 = one.say.bind(two, 'Enchanté'); twosay3(); // 'Enchanté, another object'

/** ШАБЛОНЫ ПРОЕТИРОВАНИЯ.

Singleton. Обеспечивает возможность создания только одного экземпляра определённого класса. При попытки создания нового класса - мы просто получим уже созданный.

Для этого необходимо, чтобы конструктор Universe запоминал ссылку this на объект и затем возвращал ее при последующих вызовах. До- биться этого можно несколькими способами. Но самый лучший - экземпляр в замыкании. Таким образом - реализуется ограничение доступа к экземпляру. */

function Universe() { // сохраненный экземпляр var instance = this;

// создать новый экземпляр this.start_time = 0; this.bang = 'Big';

// переопределить конструктор Universe = function () { return instance; }; }

// проверка var uni = new Universe(); var uni2 = new Universe(); uni === uni2; // true

/** При первом обращении вызывается оригинальный конструктор, возвращающий ссылку this как обычно. При втором, третьем и так далее обращении вызывается уже переопределенный конструктор. Новый конструктор обладает доступом к частной переменной instance благодаря замыканию и просто возвращает ее.

Недостаток этого шаблона, состоит в том, что при переопределении функции (в данном случае конструктора Universe()) она теряет все свойства, которые могли быть добавлены между моментом ее определения и моментом переопределения. В данном конкретном случае все, что будет добавлено в прототип функции Universe(), окажется недоступно экземпляру, созданному оригинальной реализацией. */ // добавить свойство в прототип Universe.prototype.nothing = true; var uni = new Universe();

// добавить еще одно свойство в прототип // уже после создания первого объекта Universe.prototype.everything = true; var uni2 = new Universe();

// объект имеет доступ только // к оригинальному прототипу uni.nothing; // true uni2.nothing; // true uni.everything; // undefined uni2.everything; // undefined

// это выражение дает ожидаемый результат: uni.constructor.name; // “Universe” // а это нет: uni.constructor === Universe; // false

/** Решается это так: */

function Universe() { // сохраненный экземпляр var instance;

// переопределить конструктор Universe = function Universe() { return instance; };

// перенести свойства прототипа Universe.prototype = this;

// создать экземпляр instance = new Universe();

// переустановить указатель на конструктор instance.constructor = Universe;

// добавить остальную функциональность instance.start_time = 0; instance.bang = “Big”;

return instance; }

/** Теперь всё будет так, как было задумано: */ // добавить свойство в прототип и создать экземпляр Universe.prototype.nothing = true; // true var uni = new Universe();

Universe.prototype.everything = true; // true var uni2 = new Universe();

// тот же самый экземпляр uni === uni2; // true

// все свойства прототипа доступны // независимо от того, когда они были добавлены uni.nothing && uni.everything && uni2.nothing && uni2.everything; // true

// обычные свойства объекта также доступны uni.bang; // “Big”

// ссылка на конструктор содержит правильный указатель uni.constructor === Universe; // true

/** Другой вариант решения проблемы заключается в обертывании конструктора и ссылки на экземпляр немедленно вызываемой функцией. При первом обращении конструктор создаст объект и сохранит ссылку на него в частной переменной instance. При повторных обращениях конструктор будет просто возвращать значение частной переменной. Все проверки, проведенные в предыдущем фрагменте, будут возвращать ожидаемые результаты и для новой реализации: */ var Universe;

(function () { var instance;

Universe = function Universe() { if (instance) { return instance; }

instance = this;

// добавить остальную функциональность
this.start_time = 0;
this.bang = “Big”;

}; }());

/** Фабрика. /*

стр. 184

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment