Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chris Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Functions that only object detect once 1

Status
Not open for further replies.

ESquared

Programmer
Dec 23, 2003
6,129
US
I have this somewhat unformed idea about how random javascript code I see in the wild could change for the better. I'm not sure if there's any real performance improvement to be had, but this idea appeals to me really strongly from an elegance and efficiency perspective. And I admit I think it's cool.

Here's some code from a random site online:

Code:
function cancelEvent(e) {
  e = e ? e : window.event;
  if(e.stopPropagation) e.stopPropagation();
  if(e.preventDefault) e.preventDefault();
  e.cancelBubble = true;
  e.cancel = true;
  e.returnValue = false;
  return false;
}
Now what if we changed that:

Code:
function cancelEvent(e) {
  if(!e) {
    cancelEvent = {body(window.event);};
  } else {
    cancelEvent = body;
  }
  return cancelEvent(e);
  function body(e) = {
    if(e.stopPropagation) e.stopPropagation();
    if(e.preventDefault) e.preventDefault();
    e.cancelBubble = true;
    e.cancel = true;
    e.returnValue = false;
    return false;
  }
}
This exact syntax may not actually work because window.event might be evaluated at first execution once rather than each execution of the final redefined function.

But the idea here is that the object detect of e (to see if the browser has passed the eventing object in the first parameter) only occurs once. On subsequent calls to the function, no detect is done anymore, because the function has redefined itself. Now that is efficient!

But once I try to do this for more than one detect, I run into problems. Now I need a version split for each detect. With 3 object detects, I get 8 potential versions, and that's not the kind of coding I'm looking for.

Now, I know that a function can have things added to it. It's just an object, after all.

Code:
function cry(){alert('boo hoo');}

var f = {abc: 123, dosomething: function(){alert('hello!');}
f.dosomethingelse = function{alert('bye!');};
f.sound = cry;

die = New Function() {alert('auuurrrrgh');}; //not sure of this syntax
f.deathsequence = function() {cry(); dosomethingelse(); die()}();

f.deathsequence();
f["deathesquence"](); // do it again another way
By the way, deathsequence gets executed 3 times, not 2. Not sure of syntax or if everything I tried in there actually will work. But it's theoretically sound. :)

Okay, this is all cool. You can manipulate function objects just like other objects, and you can call them as you instantiate them, just like you can do Object.ObjectReturningMethod.NewMethodOnTheReturnedObject in VB. And if that doesn't work you can force evaluation of the object with parentheses and then do your stuff to it, like in the javascript [tt]alert(("a literal string").toUpperCase());[/tt].

But where do a function's successive procedural statements live? They are separated by semicolons, not commas as in the function literal.

Code:
function abc() {
   alert('first');
   alert('second');
}
Can I reach in from outside the function and only modify the first statement? That would be perfect.

In the meantime, this is the closest I can think of. I am relatively new to javascript, so please forgive the syntax errors, I know I could eventually work this out and solve the weird closure and object reference issues (maybe some thises are appropriate):

Code:
function cancelEvent(e) {
  cancelEventFunc = {
    1: function() {e = window.event;},
    2: function() {e.stopPropagation();},
    3: function() {e.preventDefault();},
  }
  if(e) cancelEventFunc[1] = null;
  if(!e.stopPropagation) cancelEventFunc[2] = null;
  if(!e.preventDefault) cancelEventFunc[3] = null;
  cancelEvent = function(e){
    var statement;
    for (statement in cancelEventFunc) {
      cancelEventFunc[statement]();
    }
    e.cancelBubble = true;
    e.cancel = true;
    e.returnValue = false;
    return false;
  };
  return cancelEvent(e);
}
The closure and reference stuff is complicated and might kill everything. But then again, maybe not.

Do you see what I'm trying to do? What do you think? Ideas?

[COLOR=black #e0e0e0]For SQL and technical ideas, visit my blog, Squared Thoughts.

The best part about anything that has cheese is the cheese.[/color]
 
There's a lot of information in that post for what seemed to be a relatively small question. So, I'll pull out what I got from the post:
Can I reach in from outside the function and only modify the first statement? That would be perfect.
Yup, that's possible. Here's an extremely dirty version that will work in IE7 and FF (not tested in anything else). It's not foolproof since splitting the string on ; alone is not reliable. However, I'm fairly certain that you are familiar with regexp enough to come up with something more sophisticated. Really, the split isn't even needed - but it is nice to be able to break the function down to an array, line by line:

Code:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "[URL unfurl="true"]http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">[/URL]
<html xmlns="[URL unfurl="true"]http://www.w3.org/1999/xhtml">[/URL]
<head>
<title>test</title>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<script type="text/javascript">

function blah() {
   alert("1");
   alert("2");
}

window.onload = function () {
   //call the function initially
   blah();
   //modify the first line of the function to alert 3 instead of 1
   var b = blah.toString();
   b = b.substring(b.indexOf("{") + 1, b.lastIndexOf("}") - 1).split(/\;/);
   b[0] = 'alert("3")';
   blah = new Function(b.join(";"));
   //call the new function
   blah();
};

</script>
<style type="text/css"></style>
</head>
<body>
</body>
</html>

-kaht

Lisa, if you don't like your job you don't strike. You just go in every day and do it really half-assed. That's the American way. - Homer Simpson

[small]<P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <.</B>[/small]
 
Oh yeah! You can convert function text to a string and then basically eval() it! Thank you!

I know that in some books eval is evil, but I also think doing an object detect potentially thousands of times is evil. Let's say you're doing some dragging and dropping. Every mousemove event goes through the same object detect, potentially tens of thousands of times in the course of the web page's life. It would be nice to be able to avoid that.

Hmmmmmm. As long as the eval is done only on code supplied by the programmer and not on user input, then I don't see a problem. When the javascript interpreter reads your code it is already doing an eval on text. Doing an eval on quoted text instead of direct text, once, only as needed... that doesn't seem so bad to me.

I wonder if there is a clean, practical way to do this. :)
 
Well, declaring a new function using the Function object constructor is not quite the same as using an eval command. We're essentially doing the same thing here by passing a string to the constructor for execution later. But, using the eval() command executes the string interpretation of the commands immediately. I'd venture to say that there's a little bit more safety in this method.

I'd say the only difficult part would be determining when one line of code starts, and the next begins. Splitting on the semicolon will likely not be very reliable.

Since you're a veteran to the site and familiar with the way it works, I'll just say that I'm glad you were able to get something constructive from my [purple]valuable post[/purple].

-kaht

Lisa, if you don't like your job you don't strike. You just go in every day and do it really half-assed. That's the American way. - Homer Simpson

[small]<P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <.</B>[/small]
 
ha ha ha ha :p

I was not done with this thread and I *always* fulfill my obligations.
 
[smile]

-kaht

Lisa, if you don't like your job you don't strike. You just go in every day and do it really half-assed. That's the American way. - Homer Simpson

[small]<P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <.</B>[/small]
 
ha ha ha ha :p

I was not done with this thread and I *always* fulfill my obligations.

Well, declaring a new function using the Function object constructor is not quite the same as using an eval command. We're essentially doing the same thing here by passing a string to the constructor for execution later. But, using the eval() command executes the string interpretation of the commands immediately. I'd venture to say that there's a little bit more safety in this method.
At first I was confused but I found this online:

[ul]Function objects created with the Function constructor are evaluated each time they are used. This is less efficient than declaring a function and calling it within your code, because declared functions are parsed only once.[/ul]

Given this, my idea for putting individual functions in an array seems to make a certain sort of sense. Now the actual function body of each tiny subfunction is not evaluated at runtime, but this array of functions is stepped through (I used an object but perhaps an array is better) and executed.

I agree that splitting on the semicolon may not be very reliable (I immediately thought of a literal ';').

So given these things I sort of like my original idea. Here it is again slightly modified:

Code:
function execFuncList(list) {
  for (var i in list) list[i]();
}

function cancelEvent(e) {
  var funcs = [];
  if(!e) funcs[funcs.length] = function() {e = window.event;}
  if(e.stopPropagation) funcs[funcs.length] = function() {e.stopPropagation();}
  if(e.preventDefault) func[funcs.length] = function() {e.preventDefault();}
  cancelEvent = function(ev){ // this new cancelEvent should replace the original... right?
    execFuncList(funcs);
    ev.cancelBubble = true;
    ev.cancel = true;
    ev.returnValue = false;
    return false;
  };
  return cancelEvent(e);
}
Again I am nearly certain that I have reference or closure problems but it seems like these could be resolved by someone who really knows javascript. Hmmm I just changed e to ev on the inner function which highlights a problem I do have. The e referenced in the function snippets isn't the right one, it would be the event passed to the function the first time it was run, not on subsequent executions. I don't know how to fix this.

My knowledge (aka crazy ideas) of what's possible outstrips my ability to make it work! Argh! No, I'm not a pirate.

Wait, an idea. Let me try again:

Code:
function execFuncList(list) {
  for (var i in list) list[i]();
}

function cancelEvent(e) {
  function prefunc(){
    var funcs = [];
    if(!e) funcs[funcs.length] = function(){e = window.event;}
    if(e.stopPropagation) funcs[funcs.length] = function() {e.stopPropagation();}
    if(e.preventDefault) func[funcs.length] = function() {e.preventDefault();}
    prefunc = function(){execFuncList(funcs);}
    prefunc.funcs = funcs; // not needed?
    prefunc(); //execute it once which replaces it
  };
  prefunc();
  e.cancelBubble = true;
  e.cancel = true;
  e.returnValue = false;
  return false;
}
There. All references to e should be the right one. Wild and crazy, but if it works it's super nifty!
 
How'd I post that same thing twice? Weird.
 
Your second example neglects to overwrite the cancelEvent function.

-kaht

Lisa, if you don't like your job you don't strike. You just go in every day and do it really half-assed. That's the American way. - Homer Simpson

[small]<P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <B> <P> <.</B>[/small]
 
That's right, it overwrites the prefunc function instead.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top