85 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

Часто отвечают — «Флэнаган.» А вот и нет. Я сам начинал с этой книги. Делал несколько подходов: начинал и бросал. Так и не дочитал ее до конца. Книга описывает javascript с дотошной точностью, без допущений или поблажек к читающему. Но не она нужна для начала. Эти 900 страниц изрядно пугают и демотивируют. После 200 станиц понимаешь, что знаний не хватает для создания чего-то, что имело бы практическую пользу. Она заполняет голову полным списком информации про циклы, все их типы, лейблы и т. д. Для начала в 90% случаев хватает классического for.

Я веду к тому, что серьезные труды, полностью описывающие технологию нужны тогда, когда появилась потребность глубокого понимания. Ведь для того чтобы использовать javascript не обязательно понимать замыкания или прототипное наследование. Лучшим выбором для новичка будет курс, который равномерно осветит базовые возможности языка, даст несколько примеров и достаточную базу для написания «живых» программ.

UPD
Принцип 80/20 тут задействован чуть более, чем полностью.
2012   по-русски   разработка

Генератор обработчиков массивов

Пусть есть функция, которая знает как обрабатывать объект. Генератор вернет такую функцию, которая сможет обрабатывать списки таких объектов. Возьмем для примера функцию hide, которая должна скрывать DOM узлы. 
function add_class (node, class_name) {
    var node_classes;

    node_classes = node.className;
    node.className = (node_classes ? node_classes + ' ' : '') + class_name;
}


function remove_class (node, class_name) {
	node.className = node.className.replace(new RegExp(' *' + class_name + ' *', 'g'), '');
}
И превратим их в обработчкики списков с помощью функции ниже: 
function make_iterable (func) {
	var arr_proto;

	// храним ссылку на прототип массива
	arr_proto = Array.prototype;
	return function (el) {
		var res,
			args,
			i;

		// сюда будут складываться результаты множественных вызовов
		res = [];

		// структура типа [undefined, все_кроме_первого_аргумента]
		args = [undefined].concat(arr_proto.slice.call(arguments, 1));

		// проверка на «массивность». Специально сделана, чтобы корректно
		// обработала коллекции типа document.links, document.images
		if ('length' in el) {
			for (i = 0; i < el.length; i += 1) {
				// подменяем в структуре первый элемент на элемент массива
				// [элемент_массива, все_кроме_первого_аргумента]
				args.splice(0,1,el[i]);

				// запоминаем результат вызова обработчика
				// с этим набором аргументов
				res[i] = func.apply(this, args);
			}
		} else {
			// если передался не массив аргументов, вызываем
			// обработчик с указанными аргументами
			res.push(func.apply(this, arguments));
		}

		// массив результатов вызовов обработчика
		return res;
	};
}
Теперь можно подняться на уровень выше, и мыслить категориями коллекций, а не отдельных нод.
var add_class_to_multiple = make_iterable(add_class);

function hide (selector) {
    add_class_to_multiple(document.querySelectorAll(selector), 'hidden');
}
2012   по-русски   разработка

Пример построения наследования на прототипах в javascript

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

Мы создаем новый класс, а не дописываем необходимые методы в прототип, так как расширение стандартных прототипов допускается в случаях, когда интерпретатор не поддерживает функции нового стандарта. Например Array.prototype.indexOf, Object.create. Если же прототипы стандартных объектов расширяются с другой целью, это может привести к поломке используемых библиотек или недоумению у человека, читающего ваш код.

В общем виде задача сводится к созданию конструктора, прототип которого содержит в __proto__ ссылку на Array.prototype. Обратите внимание, что создаваемых нами объект, хоть и выглядит как массив в консоли браузера, но таковым не является. Это именно объект с набором свойств, которые обновляются методами массива.

Собственно сам закомментариеный пример. 
/*
 Возвращает пустой объект, у которого
 __proto__ ссылается на obj

 Тоже самое делает метод Object.create
 из новой редакции стандарта ECMA. Вместо
 того, чтобы писать такую реализацию, можно
 взять готовую и дополнить ею стандартный Object
*/
function create_object (obj) {
	var F = function () {};
	F.prototype = obj;
	return new F();
}

/*
 Функция, которая будет использоваться как конструктор экземпляров класса, наследующих
 от стандартного Array
*/
function SuperArray () {}

/*
 Экземпляры SuperArray получают в __proto__
 ссылку на Array.prototype. Следовательно,
 наследуют от Array.prototype
*/
SuperArray.prototype = create_object(Array.prototype);
/*
 Так как конструктором Array.prototype является
 Array, подменяем свойство конструктор на тот,
 который будет являться конструктором де-факто.

 Если бы мы этого не сделали, то равеснтво
 s.constructor === SuperArray; // false
 было бы неверным
*/
SuperArray.prototype.constructor = SuperArray;

/*
 Создаем методы в прототипе. Они будут доступны
 для каждого экземпляра класса SuperArray
*/

/*
 Принимает аргументом массив или массивоподобный
 объект, элементы массива будут являться ключами
 возвращаемого объекта. Значениями — значения
 экземпляра класса SuperArray
*/
SuperArray.prototype.to_object = function (keys) {
	var res, i;

	for (i = 0; i < keys.length; i += 1) {
		res[ keys[i] ] = this[i];
	}
	return res;
};

/*
 Принимает аргументом функцию. Если для всех элементов экземпляра SuperArray она возвращает
 true, то методы возвращает true.
*/
SuperArray.prototype.all = function (test) {
	var i;

	for (i = 0; i < this.length; i += 1) {
		if (!test(this[i])) {
			return false;
		}
	}
	return true;
};

/*
 Принимает аргументом функцию. Если для одного
 элемента экземпляра SuperArray она возвращает
 true, то метод вернет true
*/
SuperArray.prototype.any = function (test) {
	var i;

	for (i = 0; i < this.length; i += 1) {
		if (test(this[i])) {
			return true;
		}
	}
	return false;
};

var s = new SuperArray();
/*
 Метод push берется из цепочки прототипов.
 Он обновляет значение объекта а так-же его
 свойство length
*/
s.push(5);
s.push(3);
s.push(8);
s.push(4);
s.push(1);
s.push(7);
s.push('foo');

s.all(function (el) {
	return typeof el === 'number';
}); // false

s.any(function (el) {
	return typeof el === 'string';
}); // true

s.any(function (el) {
	return typeof el === 'number';
}); // true

/*
 ВАЖНО:
 созданный объект не является масивом,
 а только наследует его методы. Это
 значит, что для него не сработают
 конструкции, которые бы сработали для
 массива
*/
s.length === 7; // true
/*
 После такого присвоения, длина массива
 изменяется. В нашем случае она остается
 неизменной.
*/
s[99] = true;
s.length === 100; // false
s[99] === true; // true
 
2012   по-русски   разработка

Cнова декоратор

Под впечатлением от этой и этой статей обновил декоратор.

Раньше он позволял только выполнить функцию перед или после другой функцией. Новая, расширенная версия, хоть и наложила ограничения на функции-обертки, имеет больше возможностей:
  1. подмена передаваемых аргументов;
  2. отмена выполнения;
  3. подмена возвращаемого значения.
Сам код, собственно (на github):
function decorate (initial, decorate_before, decorate_after) {
	return function () {
		var initial_call_result,
			updated_params,
			updated_result;

		if (typeof decorate_before === 'function') {
			updated_params = decorate_before.apply(this, arguments);
			if (!updated_params) {
				return;
			}
		}
		initial_call_result = initial.apply(this, updated_params || arguments);
		if (typeof decorate_after === 'function') {
			updated_result = decorate_after.apply(this, arguments);
		}
		return updated_result || initial_call_result;
	};
}
Из изменений: результат первой функции оберки используется как аргументы для вызова оригинальной функции.

Значит можно явно создавать свои варианты биндинга для функций. Например, чтобы при работе с jquery не указывать каждый раз контекст, можно создать обертку, в которой контекст явно задан:
var $iframe;
var iframe_context = $('iframe').eq(0);

$iframe = decorate($, function (selector, context) {
	return [selector, iframe_context];
});

// вместо явного указания:
var images = $('img', iframe_context);

// используем обертку
var images = $iframe('img');
Когда первая обертка не возвращает массив, считается, что оригинальную функцию не надо вызывать. Такой подход можно применять, когда надо подправить поведение плагина, но не хочется влезать в тонкости его реализации. При обновлении плагина не надо будет снова тормошить его внутренности в поисках изменений, так как код реализации и код допиливания будет разделен.

К тому-же некоторые баннерные сети до сих пор используют document.write для вставки ского кода. Если появляется желание обхитрить сеть и вставить их код асинхронно, то реализовать желание можно с помощью переписывания document.write. 
// отмена поведения метода плагина
plugin.method = decorate(plugin.method, function () {
	if (!ok_flag) {
		return;
	} else {
		return arguments;
	}
});

// вставка кода рекламного блока в обход document.write
document.write = decorate(document.write, function () {
	if (is_advert(arguments)) {
		on_advert_embeding.apply(this, arguments);
	} else {
		return arguments;
	}
});
Результат работы удобно подменять удобно при отладке кода. Когда часть системы уже готова, и известно как будет работать работать вторая часть, можно эмулировать ее поведение, подменой результатов ответа. Или же для юнит-тестирования частей системы.
get_products = decorate(get_products, function () {
	if (debug) {
		return;
	}
}, function () {
	if (debug) {
		return ['1111', '2222', '3333', '4444'];
	}
});
2012   по-русски   разработка

Прототипное наследование

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

1. Каждый объект имеет скрытое* свойство __proto__, которое ссылается на некий другой объект (исключение — объект Object.prototype, у которого __proto__ ссылается на null). Это и называется цепочкой прототипов.
var arr = [],
    obj = {};
typeof arr.__proto__ === 'object'; // true
typeof obj.__proto__ === 'object'; // true
arr.__proto__ === Array.prototype // true. Экземпляр класса "Массив" наследует от Array.prototype
arr.__proto__.__proto__ === Object.prototype // true он-же наследует от Object.prototype
arr.__proto__.__proto__.__proto__ // null Последнее звено цепи прототипов
2. При чтении свойства в объекте, оно ищется непосредственно в объекте. Если в объекте не находится, то интерпретатор ищет свойство в __proto__. Поиск останавалиается при первом нахождении или когда ссылка __proto__ ведет на null;
function F() {}
F.prototype.state = false; // состояние по умолчанию
F.prototype.state_on = function () {
	this.state = true; // запись свойства в эжкземпляр класса
}
F.prototype.reset_state = function () {
	delete this.state; // удаление свойства из экземпляра класса
}

var f = new F;
f.state;         // false
f.state_on();    // добавили свойство в экземпляр класса
f.state;         // true
f.reset_state(); // удаление свойства из объекта
f.state;         // false (значение взялось из прототипа)
f.test;          // undefined (свойства нет в объекте и цепочке прототипов)
3. __proto__ устанавливает в момент создания объекта, и ссылается туда-же, куда и Конструктор_объекта.prototype;

4. Каждая функция при объявлении получает свойство prototype.
function F () {};
typeof F.prototype === 'object'; // true
var f = new F;
f.__proto__ === F.prototype; // true
2012   по-русски   разработка

Вызов. Апдейт массива

В комментариях — все возможные варианты добавления элемента в начало и конец массива.

// в начало
var arr;
var temp = [insert];
while (arr.length) {
	temp.push(arr.shift());
}
arr = temp;

arr = arr.concat([insert]); // в конец
arr = arr.slice(1); // с начала
arr = arr.slice(0, arr.length - 1); // с конца

Skripatch

a.push(element)
a.unshift(element)
a.splice(0, 0, element)
a.splice(a.length, 0, element);

Илья Панасенко

a[a.length] = element; // в конец

Neklesa

a = a.concat([element]); // в конец
a.reverse().push(element).reverse(); // в начало
2012   по-русски   разработка

Как дождаться выполнения асинхронных функций

Состояние, когда такое надобится попахивает промахом в проектировании (или специфическими задачами). Однако проблема есть и требует решения.

Идея

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

Реализация

Обертки создаются с помощью декорирования коллбеков. Ключом к набору асинхронных функций и их состояний будет функция коллбек (реализуется через два массива). Смотреть можно на гитхабе или ниже, но с комментариями.
function decorate (initial, decorate_before, decorate_after) {
	return function () {
		var initial_call_result;

		if (typeof decorate_before === 'function') {
			if (!!decorate_before.apply(this, arguments) === false) {
				return;
			}
		}
		initial_call_result = initial.apply(this, arguments);
		if (typeof decorate_after === 'function') {
			decorate_after.apply(this, arguments);
		}
		return initial_call_result;
	};
}


function chain (func, callback) {
	var callbacks,
		func_states;

	// Массив для коллбеков. Индекс коллбека соответствует
	// индексу набора состояний
	callbacks = [];
	// Состояния асинхронных функций
	func_states = [];
	chain = function (func, callback) {
		var callback_index,
			chained,
			func_index;

		// получаем объект состояний
		callback_index = callbacks.indexOf(callback);
		if (callback_index === -1) {
			// если его нет, создаем новый
			chained = {
				func : [],
				state : [],
				// если все функции выполнились, запускаем коллбек
				test : function () {
					for (var i = 0; i < this.func.length; i += 1) {
						if (!this.state[i]) {
							return;
						}
					}
					callback();
					this.state = [];
				}
			};
			callbacks.push(callback);
			func_states.push(chained);
		} else {
			chained = func_states[callback_index];
		}

		// оборачиваем функцию, чтобы она обноавляла состояние
		func_index = chained.func.push(func) - 1;
		func = decorate(func, null, function () {
			chained.state[func_index] = true;
			chained.test();
		});
		return func;
	};
	return chain.apply(this, arguments);
}
И пример использования:
function on_all_async_ready () {
	console.log(‘all ready’);
}

var on_2_of_3_ready = function () {
	console.info(‘1 and 2 ready’);
}

var async_1 = chain(function () {
	console.info(‘first async’);
}, on_all_async_ready);

async_1 = chain(async_1, on_2_of_3_ready);


var async_2 = chain(function () {
	console.info(‘second_async’);
}, on_all_async_ready);

async_2 = chain(async_2, on_2_of_3_ready);


var async_3 = chain(function () {
	console.info(‘third async’);
}, on_all_async_ready);


setTimeout(function () {
	async_1();
	setTimeout(async_2, 1000);
	setTimeout(async_3, 2000);
}, 2000);
2012   по-русски   разработка
Earlier Ctrl + ↓