47 posts tagged

разработка

/core.php, line 2
Error 2: Use of undefined constant k - assumed 'k' (this will throw an Error in a future version of PHP)

/core.php, line 2
Error 2: Use of undefined constant k - assumed 'k' (this will throw an Error in a future version of PHP)

/core.php, line 2
Error 2: Use of undefined constant k - assumed 'k' (this will throw an Error in a future version of PHP)

/core.php, line 2
Error 2: Use of undefined constant k - assumed 'k' (this will throw an Error in a future version of PHP)

/core.php, line 2
Error 2: Use of undefined constant k - assumed 'k' (this will throw an Error in a future version of PHP)

/core.php, line 2
Error 2: Use of undefined constant k - assumed 'k' (this will throw an Error in a future version of PHP)

/core.php, line 2
Error 2: Use of undefined constant k - assumed 'k' (this will throw an Error in a future version of PHP)

Later Ctrl + ↑

Javascript сниппеты с google closure

Интересная возможность closure-compiler (это такой сервис для обфускации и оптимизации кода). Возможность складывается из двух факторов:

  • компилятор в advanced режиме пережимает код до неузнаваемости, попутно совершая оптимизации. Оптимизаций вагон: значения, которые можно вычислить, вычисляются, инлайновые функции “растворяются” в коде. Функции, которые не вызываются, выбрасываются.
  • компилятор работает с библиотекой гугла для javascript, позволяя совершать импорты модулей из библиотеки в тело скрипта.

Комбинируя эти знания, включаем библиотеку, и запрашиваем версию флеша, например:

// ==ClosureCompiler==
// @output_file_name default.js
// @compilation_level ADVANCED_OPTIMIZATIONS
// @use_closure_library true
// ==/ClosureCompiler==

goog.require('goog.userAgent.flash');
window.a = goog.userAgent.flash.VERSION

На выходе получаем слабоподдерживаемую, но работающую абракадабру, которую после легкого шлифования использует в своем мегапроекте.

2013   по-русски   разработка

Рендер динамического markdown в jade

Jade среди прочих, имеет классную фичу — фильтры. Вот только они не завелись у меня с динамическим markdown. Как это часто бывает с javascript-ом, очевидное решение не срабатывает.

template.jade

markdown: #{md}

router.coffee

app.get "/" (request, response, next) ->
    response.render "template", 
         indexMarkdown : indexMarkdown

В браузер приходит сырой markdown без намека на конвертацию. Решением оказалось передача функции , которая умеет парсить markdown, параметром в шаблон, и запуск ее из самого шаблона:

template.jade

div= mdToHTML(indexMarkdown)

router.coffee


md = require("marked").parse

#...
app.get "/" (request, response, next) ->
    response.render "template", 
        indexMarkdown : indexMarkdown
        mdToHTML : md

2013   по-русски   разработка

Максимальная частота срабатывания события mousemove

В голову пришла мысль, что выстреливать событие движения мыши чаще 60 раз в секунду бесмысленно. После ручных тестов браузеры (O12, FF20.02a, S6.02, C24) показали полную солидарность в моих взглядах. Каждый из них запускал обработчик движения мыши не более 60 раз в секунду. Код для проверки в домашних условиях:

var start;
var totalCalls;


start = 0
totalCalls = 0

function onmove () {
	var now = Date.now()

	// начнем считать время с момента
	// первого срабатывания события mousemove
	if (!start) {
		start = now
	}

	if (now - start < 1000) {
		totalCalls += 1
	} else {
		console.log(totalCalls)
	}
}

window.onmousemove = onmove

После запуска кода веди мышку с одной скоростью по телу страницы.

2013   по-русски   разработка

Как запустить nodemon под forever

Примеры из документации устарели, да и в явном виде решения не нашел. В двух словах:

  • forever инструмент для запуска скриптов (не только nodejs) в фоновым процессом.
  • nodemon перезапускает скрипт после изменений файлов (в моем случае coffee).
forever start -c nodemon app.coffee

Связка нужна для того чтобы постоянно работающий сервер автоматически перезапускался после обновлений.

2013   по-русски   разработка

Базопасный placeholder-болванка javascript ответа

На сервере динамически генерируется javascript ответ (допустим script.js). При этом какая-то часть файла — статическая и одинаковая для всех клиентов, а какая-то должна быть заменена для каждого клиента отдельно. При этом заменой занимается некий серверный язык. Задача: предоставить файл-шаблон, в определенное место в котором будут подставляться данные. Первое, что приходит на ум выглядит как-то так:

(function () {
	var data = #DATA_PLACEHOLDER#;
	var errors = #ERRORS_PLACEHOLDER#;
	var all = {};
		
	all.data = data;
	all.errors = errors;
	window.provide = processData(all);
}());

Файл получается невалидным, что влечет ряд последствий:

  • Его валидацию надо исключить из общей валидации проекта, или реализовывать отдельную.
  • При исключении файла из валидации велик шанс синтаксической ошибки при последующей его правке (сборка проекта пройдет успешно, но клиентская часть не отработает)
  • В случае отказа бекэнда инициализация не произойдет, и код, зависящий от кода в файле, что мы генерируем, завалится, обращаясь к необъявленной переменной (provide).

Явно напрашивается, что плейсхолдером должен быть особой формы комментарий, при это должна быть альтернатива, в случае, если комментарий не заменится на значение. Вот-же ж оно: использем самовызывающиеся анонимные функции:

(function () {
	var data = (function () {return /*!DATA_PLACEHOLDER*/})() || {};
	var errors = (function () {return /*!ERRORS_PLACEHOLDER*/})() || [];
	var all = {};
	
	all.data = data;
	all.errors = errors;
	window.provide = processData(all);
}());

Уже намного лучше. Файл валиден, теперь его можно не исключать из процесса проверки всего проекта. А так-же в случае с проблемами на бекэнде, клиентская часть продолжает инициализироваться. Тут она сможет сама решить, как поступать в случае падения серверов.

Однако не все так безрадостно.

var data = (function() {return
	{
		importantValue: true
	}
})() || {};
console.log(data.importantValue); // undefined

Стоит вставить данные, начиная с перевода строки, как вся стройная система рассыпется. Конечно можно организационно решить вопрос, сказав явно программисту, чтобы данные начинались с символа, но это не наш путь, да и организационные методы действуют плохо.

И вот оно, финальное решение: передадим данные аргументом, и вернем их-же.

(function () {
	var data = (function (data) {return data})(/*!DATA_PLACEHOLDER*/) || {};
	var errors = (function (errors) {return errors})(/*!ERRORS_PLACEHOLDER*/) || [];
	var all = {};
		
	all.data = data;
	all.errors = errors;
	window.provide = processData(all);
}());

На выходе — пуленепробиваемая, валидная конструкция для последующего наполнения данными.

2013   по-русски   разработка

Максимальная глубина рекурсивного вызова

Способ пощупать наживую глубину рекурсии, которую поддерживают js движки. По достижению максимальной глубины вложенности вызовов функций интерпретатор генерирует исключение. Значит для замера максимальной глубины достаточно инкрементировать значение счетчика, до тех пор, пока не вывалится ошибка.

var max_depth;

max_depth = 0;
function recursive () {
	max_depth += 1;
	recursive();
}
try {
	recursive()
} finally {
	alert('Max recursion depth is ' + max_depth);
}

Визуализация
Browser call stack limits

Сырые данные

Safari 6.02 43680
Chrome 23 (mac) 24544
Chrome 23 (win) 25021
Opera 12.10 16384
Firefox Aurora 19.0a2 31712
Internet explorer 41153
nodejs 25113

Все браузеры, кроме firefox-а давали ответ практически моментально. А лисица висела минуты 3-4, прежде чем назвать свою цифру.

Из интересных выводов: если при использовании рекурсивных вызовов, можно получить неприятный сюрприз в случаях, близких к граничным. Разработчик, бди.

2013   по-русски   разработка

Синглтон на javascript

Вся красота и гибкость джаваскрипта раскрывается как раз на подобных задачах: реализация функционала, который не заложен в языковые конструкции, но широко применяется в других языках программирования.

Синглтон по определению — объект, который может присутствовать в системе только в единственном числе.

Самое простое, что можно сделать — создать объект, и назвать его условно синглтоном. То есть непрограммными средствами решаем задачу.

window.singletone = {
	// singletone body
};

Такой подход порождает больше вопросов и проблем, чем решает: нет никакой инкапсуляции данных, ссылку на объект необходимо явно прокидывать во все области видимости, где подразумевается его использование. Да и сама форма записи, в случае большого количества методов, сложна для чтения.

Вопрос с инкапусяцией можно решить с помощью замыкания:

var singletone = (function() {
	var private;

	function private_method () {}
	
	return {
		public : function () {
			return private_method(private);
		}
	}
}());

Уже немного лучше. Данные инкапсулированы, но строить хоть какую систему наследования с подобным подходом сложно. Для упрощения процесса наследования, будем работать с функцией – конструктором. Эта функция должна возвращать ссылку на один и тот же объект. Самое простое, что можно сделать во вновь сложившейся ситуации, это сконструировать объект и всегда возвращать ссылку на него

var Singletone;

Singletone = (function () {
	var instance;

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

Этот код больше похож на правду: во-первых мы инкапсулировали данные, во-вторых программно ограничили количество инстансов синглтона до одного. Возможность использования конструктора как с new так и без. Код вполне рабочий, и подходящий для использования в продакшене. Но в нем еще достаточно минусов: объект конструируется в любом случае, даже если конструктор не был ни разу вызван, невозможность использования цепочки прототипов, некорректный конструктор объекта, возвращаемого Singletone.

var singletone,
	another_singletone;

singletone = new Singletone();
another_singletone = Singletone();

console.log(singletone === another_singletone); // true
console.log(another_singletone.constructor === Singletone); // false
console.log(another_singletone.__proto__ === Singletone.prototype); // false

Конечно мы можем вручную установить свойство constructor, __proto__. Правда такой код потеряет в кроссбраузерности и изящности (все равно продолжит работать, что намного важнее красоты). Я же в своих изысканиях ищу максимально изящный вариант.

Судя по предъявляемым требованиям, конструктор синглтона должен создавать и запоминать объект при первом вызове, и возвращать этот объект при последующих. Вспомним, что объект функции это объект, и будем хранить в нем инстанс сконструированного объекта.

function Singletone () {
	if (Singletone.instance) {
		return Singletone.instance
	}
	Singletone.instance = this;
}

Вот она, красота, javascript. Если конструктор явно не возвращает объект, то возвращается конструируемый объект. При первом вызове new Singletone в свойство Singletone.instance запишется конструируемый объект, а при последующих будет возвращаться ссылка на него.

Этот код хорош по нескольким причинам: объект не создается, если он не был сконструирован (в отличии от предыдущих вариантов), объект можно расширять с помощью Singletone.prototype, так-же проверять instanceOf Singletone. С другой стороны, эту функцию необходимо всегда вызывать с ключевым словом new. Не то, чтобы я был противником использования new или сокрытия этого механизма от программиста, использующего мой синглтон, но создание возможности вызова конструктора как с new, так и без, позволит избежать ряда вопросов про использование синглтона. Так же в этом варианте функция выступает в качестве хранилища инстанса объекта. Такой подход имеет право на жизнь, но он слишком прямолинеен и показывает детали реализации наружу, делая код чуть уязвимее.

Возможно я бы не придирался так к коду (кстати, в вики пример реализации синглтона именно в виде, представленном выше), если не бы не знал лучшей реализации.

Мы же пойдем дальше, и создадим реализацию, которая:

  • инкапсулирует данные
  • не конструирует объект, если не конструктор не был вызван
  • позволяет использовать все инструменты прототипного наследования (constructor, Singletone.prototype, instanceOf)
  • может вызываться как с new, так и без
var Singletone = (function () {
	var instance;

	return function Construct_singletone () {
		if (instance) {
			return instance;
		}
		if (this && this.constructor === Construct_singletone) {
			instance = this;
		} else {
			return new Construct_singletone();
		}
	}
}());

Разберем что происходит в возвращаемой функции.
Если есть instance, то возвращаем его. Если функция используется с new или без, все равно мы вернем ссылку на объект, сконструированный этой функцией.

Если функция вызвана с new, то this ссылается на объект, конструируемый этой функцией. This так же может быть определен, если функция вызвана в нестрогом режиме (this === window), или если функция вызвана как метод объекта (this === этот_объект). Второй случай аннигилируется проверкой конструктора у this.

Если this не определен (вызов в строгом), или конструктор у this не текущая функция, возвращаем результат текущей функции с new.

Имеем на выходе пуленепробиваемый конструктор синглтона, который можно где угодно хранить и как угодно вызывать. Такую красоту нельзя не прихватить в тулбокс.

2012   по-русски   разработка
Earlier Ctrl + ↓