0

I'm using a function to create other functions that will be used on an document event handler so the signature of the returned functions must match that of the event handler, eg. function (event, ui).

The code is as follows

function createEventHandler(refFn, additionalMods) {
    var createdEvent = function (event, ui) {
        // Merge the properties of additionalMods with an object { event, ui }
        // call function with refFn and the resulting object as parameters
    }
    createdEvent.ready = true;
    return createdEvent;
}

I removed the code of the generated function for clarity but inside the refFn and additionalMods variables are processed inside.

Now when processing the user input I call the following line

var handler = events[i].handler.ready ? 
                   events[i].handler : 
                   createEventHandler(events[i].handler);

Basically process an array of data that each has a property called handler which is either a function or the result of calling createEventHandler.

The bottom line is that if the function in the array has been processed then pass this function 'as is', if not process the function and store the result so in the end all the functions are processed.

Right now i'm attaching a property called ready to signal that the function was processed as it turns out there is no reliable method to obtain the function's name according to this post but this doesn't feel right.

I also tried to use the prototype for comparison but this doesn't work because a new function is created everytime and is inside a scope so I can not get a reference for comparison.

I even tried

events[i].handler.prototype == createEventHandler().prototype   

but of course it didn't work.

Does anyone know how can i generate this functions and have a reliable way to compare them to know if they were generated by my code or not.

{Edit}

To add further clarification

All the code above is under the same scope meaning the code that process the array has visibility over the createEventHandler function. I can modify this code all I want what I cannot modify is the content of the array once is created. I have to iterate over it as it comes and generate or not based on if the work was done already.

The createEventHandler is also exposed to the user throught an API function. Let's say the user calls evt.generate('model') this will generate an event handler that does an specific work using the createEventHandler function under the hoods. If then you can call evt.generate('bind') another will be generated that does another work.

This is a lot of behaviour that is provided to the users by default but they can choose to add they custom behaviour if none of the predefined ones are fit for the task.

All the data is declared once but the content of the array is disparate because I can write the following and is supposed to work. I omitted most of the other irrelevant properties.

var events = [
    { 
        event: 'pageshow', 
        handler: evt.generate('model') 
    },
    { 
        event: 'pagebeforeshow', 
        handler: function (params, last) {
            // My custom handler for this case
        }
    }
];

After looping the array all the handlers are in the same format and ready to be binded. The createEventHandler is necessary in all the cases because I use dependency injection to supply data for those parameters so it's basically "if not called already then call it and do al the dependency injection work" this is why I need to compare the functions.

Community
  • 1
  • 1
devconcept
  • 3,665
  • 1
  • 26
  • 40
  • Probably not the fanciest answer, but you could always use a list of bools, one for each function, and have each function toggle it as a last step, and you check your list of bools simultaneously. – Spacemonkey May 28 '15 at 18:09
  • This doesnt work because what i cannot control the value of the property `handler`. I provide a user a method that will generate this functions or they write the custom code that must be processed after. I cannot force them to write te code the way I want and that it doesn't make any sense to them – devconcept May 28 '15 at 18:15
  • A `.ready` property seems to be just the right solution here. What exactly don't you like about it? – Bergi May 29 '15 at 00:28
  • An user can trick my system and pass it's own ready=true property causing undefined behaviour. :p – devconcept May 29 '15 at 00:31
  • @Bergi Check my post, I'd be happy to read some comments about it. – devconcept May 29 '15 at 13:15
  • 1
    @devconcept: Well a user will *always* be able to trick your system unless you use a [`WeakSet`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet). But really, that's not a problem, most users know what they're doing. It's not your fault if they try - and after all, you're not building a security system of any sort here. – Bergi May 29 '15 at 14:37
  • @Bergi thanks for the comments. I'm not trying to build a fail proof system ;) but a system that is reliable most of the time. Not letting your users banging their heads trying to figure out was went wrong with a code that looks perfectly legal but it doesnt work. – devconcept May 29 '15 at 15:49
  • I'd recommend to use a more unique, usage-descriptive property name for that. Something like `createdEvent.__Libname_isInjectionWrapped`. If users do use such a property, it does not look perfectly legal any more - in contrast to using a standard `.prototype` property like in your answer below. – Bergi May 29 '15 at 18:05

2 Answers2

1

I found an elegant solution and I post it here in case someone runs into the same problem.

The problem with my code is that an user car write a function with a property named ready which is a common name and a value of true which is also a common value and the processing will fail.

Maybe the user didn't write the property, maybe is present because is inherited from it's prototype. The goal is to try to be as certain as possible that the code you are processing was the output or your own functions or not, which in javascript is almost an impossible task.

The most accurate way that I found was when I was reading about Equality comparisons and sameness which tells me that an object is only equal to itself when you use the === equality operator and is not a primitive object. That is

undefined === undefined  => true
null === null            => true
"foo" === "foo"          => true
0 === 0                  => true

But

{a:1} === {a:1}          => false

So you can write a property ready which is equal to an object and as you hold the reference to that object all the comparissons will fail if this property was not set by you.

This is good but it feels bad to have an extra property called ready with a random object just to compare, maybe there is a better way and yes, there is.

In javascript there are no classes but there is prototype inheritance so you can write a function and use one of the patterns of inheritance to set this function as the ancestor of yours and use that for comparisons.

var parentEvent = function () {};
// This is the function that will be used as parent
function createEventHandler(refFn, additionalMods) {
    var createdEvent = function (event, ui) {
        // Merge the properties of additionalMods with an object { event, ui }
        // call function with refFn and the resulting object as parameters
    }
    //createdEvent.ready = true This is no longer necessary

    // This is the "sharing prototype" inheritance pattern
    createdEvent.prototype = parentEvent.prototype
    return createdEvent;
}

Now the prototype of your returned function is pointing to a function that you hold in a variable. Then you can compare them using

// Replace the property comparison with the prototype comparison
var handler = events[i].handler.prototype === parentEvent.prototype ? 
               events[i].handler : 
               createEventHandler(events[i].handler);

This is not fail proof, I know, but is good enough for most cases.

{Edit}

Thank's to @Bergi for pointing out that this is not inheritance in the strict sense of the word. The reason for that is that most javascript inheritance patterns demand that you use constructor functions and I'm using a factory function here. To make it work you have to write something like this

function createEventHandler(refFn, additionalMods) {
    // Same code as before
    createdEvent.prototype = parentEvent.prototype
    return new createdEvent();
}

And the comparison is done with

events[i].handler.__proto__ === parentEvent.prototype

Note the difference in the way the function is returned ant the way the new prototype property is accessed. This is good when you do have other properties that you want to return that are in the parent function.

Community
  • 1
  • 1
devconcept
  • 3,665
  • 1
  • 26
  • 40
  • 1
    Well in fact you're not using inheritance here, you just changed the name of that property from `ready` to `prototype` which is even more common. You can only hope that is will not get messed with (which is unlikely though). However, +1 for the object identity token idea! – Bergi May 29 '15 at 14:33
  • I borrowed the pattern from the Stefanov's Javascrip Patterns book but you are right, inheritance is not working. The problem is that i'm not using constructor functions that is required for this pattern to work. I'm using a factory function wich messes the whole prototype chain. To make it work i've to use `return new createdEvent();` but this is not necessary because y only need identity for my purposes. +1 for the comment. – devconcept May 29 '15 at 14:59
0

Do the contents of the events array change during the execution of your program, aside from replacing them with the converted versions?

If not, a simple solution is just to make a copy of the handlers before you start converting them, and use that for the comparison:

// keep this somewhere that you can access it later
var origHandlers = events.map(function (e) { return e.handler; });

var handler = events[i].handler === origHandlers[i]     ? 
                  createEventHandler(events[i].handler) :
                  events[i].handler;
JLRishe
  • 99,490
  • 19
  • 131
  • 169