Сегодня поговорим об одном из них, который я решил назвать "самонастраивающейся функцией". Он служит в тех ситуациях, когда заранее предсказать, какая понадобится функция затруднительно, и гораздо легче это сделать на этапе первого вызова. Т.е. для обеспечения правильной работы функции ей нужно принять первый вызов и только по нему она сможет понять, как ей работать.
Конечно, можно сделать в этой функции большой оператор ветвления if
с тем, что бы в случае одних параметров выполнять одну логику, а в случае других - другую. Но эта операция проверки будет отнимать дополнительное время, так что это приемлемо лишь для не очень критичной к скорости работы логики.
Именно с такой задачей я имел дело при написании второй версии своей библиотечки. Мне нужна была функция, которой передавался бы объект pos
, имеющий два свойства - 'x' и 'y' и от неё требовалось, что бы она проставила в них значения x и y-координат курсора мыши. Я назвал её refreshMousePos
.
Саму эту функцию должен вызывать обработчик того или иного события, который в W3C-совместимых браузерах получает ссылку на объект Event
, соответствующий данному событию. Для вычисления координат курсора мыши этот объект нужен, так что в этом случае ссылку на него нужно передать в refreshMousePos
вторым аргументом. В случае же W3C-несовместимого браузера (в основном, IE6-8), обработчику не передаётся этот объект, по-этому он и не может быть передан в функцию refreshMousePos
.
Эта функция вызывается очень-очень часто по ходу работы библиотеки, ведь каждый раз при перемещении курсора мыши в процессе перемещения нужно знать его координаты. Так что эта функция должна быть настолько быстро-выполнимой, насколько это возможно и каждый раз выполняющаяся проверка в выражении if
здорово бы замедлила работу библиотеки, особенно если речь идёт о слабых компьютерах.
Но заранее определить точно, будет или не будет передан объект Event
в функцию, затруднительно - гораздо удобнее это сделать в процессе работы, по факту проверив содержимое переданной переменной. При чём, это достаточно сделать один раз и сразу будет понятно, как функция должна выполняться впоследствии.
Сам IE меняет в соответствии с обрабатываемым событием объект event, который всегда находится у него в глобальном контексте. И интерфейс этого объекта несколько иной, что проявляется в логике вычисления координат курсора мыши.
Так что я сделал разделение логики следующим образом:
/** Обновить координаты мыши.
* @param {Position} pos координаты курсора мыши
* @param {Event} [evt] объект события */
function refreshMousePos(pos, evt) {
return (refreshMousePos = typeof evt !== 'undefined' ?
function(pos, evt) { //W3C realization
var body = document.body;
pos.x = evt.clientX + body.scrollLeft - body.clientLeft;
pos.y = evt.clientY + body.scrollTop - body.clientTop;
return pos;
} :
function(pos) { //IE realization
pos.x = event.pageX;
pos.y = event.pageY;
return pos;
}
)(pos, evt); //вызываем тот вариант функции, которую присвоили и возвращаем результат выполнения
};
Как видим, здесь переменной, которая содержит главную функцию, в зависимости от содержания ссылки evt
присваивается различное значение-функция, при этом выполняющаяся в данный момент функция автоматически затирается. Т.е. функция, будучи вызванной, как бы более тонко настраивает себя под конкретную среду в которой оказалась для более производительной работы в ней.
P.S. Спросите, зачем я возвращаю объект pos
, ведь его поля итак уже изменены и значит, необходимый внешний эффект достигнут? Отвечу - для того, что бы можно было после вызова функции сразу же в той же конструкции обратиться к объекту pos
:
alert(
refreshMousePos(pos, evt).x
);
2 комментария:
Может я чего-то не понял, но в твоём решении условие проверяется каждый раз, поскольку выполняемая функция "не затирается".
Посмотри код:
[code]
console.clear();
var q=1
function t(a){
console.log('invoked');
return a
}
function r(pos, evt) {
return (t(q) == 1
? function(pos, evt) {
return pos;
}
: function(pos) {
return pos;
}
)(pos, evt);
};
console.log(r(1));
console.log(r(2));
console.log(r(3));
[/code]
Одно из решений - каррирование функции "на месте".
[code]
var r = (function(){
return (t(q)==1 ?
function(pos, evt) {
return pos;
} :
function(pos) {
return pos;
}
);
})()
console.log(r(1));
console.log(r(2));
console.log(r(3));
[/code]
Самое простое решение:
[code]
var r = (t(q) == 1)
? function(pos, evt) {
return pos;
} :
function(pos) {
return pos;
}
console.log(r(1));
console.log(r(2));
console.log(r(3));
[/code]
Восьменог, так ты в этом коде, действительно, не затираешь функцию. Ведь, затирка происходит по её имени, при присвоении переменной другого значения. В данном случае вот как твой первый код нужно изменить, что бы затирка произошла (нужное место я выделил жирным):
var q=1;
function t(a)
{
console.log('invoked');
return a;
};
function r(pos, evt)
{
return (r = t(q) == 1 ?
function(pos, evt) {
return pos;
} :
function(pos) {
return pos;
}
)(pos, evt);
};
console.log(r(1));
console.log(r(2));
console.log(r(3));
Что же касается решения во втором участке кода, то оно хорошее, но не подъодит для джанного случая - я же и пишу, что не всегда мы сначала можем правильно понять, что нам нужно - моё решение как раз для случая, когда мы можем понять, какая функция нужна, лишь на этапе первого её вызова. :)
Отправить комментарий