Javascript, passing 'e' to new Function, scope - javascript

I have a function block which is dynamic and I need to call with either eval or new Function (preferably the latter). I want to pass in the event it was raised from
function MyFunc(e)
{
new Function("OtherFunc(e, 'abcde')");
}
I can't see how to do this, I have tried a few things such as bind(this), and with(this) but no joy. It's an unusual thing to want to do hence my confusion.
NB I can see it works with eval but new Function would be better if possible and I get the impression it should be, e.g.
How to use scope in JavaScript for Function constructor? (second answer)
Any suggestions? Thanks
(Added: Why I want to do this)
I'm using Kendo mobile buttons. I'm moving from this:
<button onclick="MyFunc(e)"/>
to this
<button data-click="Call" data-func="MyFunc(e)"/>
this is because onclick is not recommended with Kendo UI on iPhones

Don't use the function constructor. Really, really don't. It is eval by another name. Use a function declaration instead. That won't break scope or expect to be built up out of strings.
function MyTest(e) {
function callOtherFunc() {
OtherFunc(e, "abcde");
}
return callOtherFunc;
}

So basically, you have buttons right now with
onclick="MyFunc(e); OtherFunc('a')"
...and you want to change those to
data-click="Call" data-func="MyFunc(e); OtherFunc('a')"
...and you're trying to figure out how to write your Call function without any significant refactoring, continuing to use the strings as you have them now in the onclick.
I'm a bit confused by your use of e within onclick rather than event. As far as I'm aware, there's no e in-scope for onXyz handlers; the event is available as event, though. In the answer below, I've assumed event in onclick but e everywhere else; adjust as necessary.
Within those constraints, eval and new Function are indeed pretty much your only option. It's not more evil than onclick (which is also eval in disguise); eval used with strings you control isn't necessarily evil, it's just usually a last resort (kind of like with).
Based on the documentation, looks like your Call would look something like this:
function Call(e) {
var code = this.element.prop("data-func");
var f = new Function("e", code);
f.call(this, e);
}
That ends up running the code with this being the element that was clicked, and with e in scope to the code in the generated function.
I do not recommend this except perhaps as a temporary measure during proper refactoring, but within the constraints you've given, that's how I see it working. One reason I don't recommend it is that, as with onclick, all of your functions have to be globals (because other than the args you pass it, new Function only has access to globals), and globals are best avoided like the plague.
Live example (with some workaround for the fact I didn't include Kendo):
// Kendo calls the data-click function with this being something
// other than the element; but the element is available as `this.element`
function fakeKendo(e) {
Call.call({element: this}, e);
}
function Call(e) {
// (Using getAttribute instead of Kendo's prop here)
var code = this.element.getAttribute("data-func");
var f = new Function("e", code);
f.call(this, e);
}
function MyFunc(e) {
snippet.log("MyFunc: e.type = " + e.type);
}
function OtherFunc(arg) {
snippet.log("OtherFunc: arg is " + arg);
}
<p onclick="fakeKendo.call(this, event)" data-func="MyFunc(e); OtherFunc('a')">
Click me
</p>
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

Related

Get Javascript function name

Is there a way to obtain function's name from outside of it?
Lets say there is a js script on web page that we cannot modificate, just read. The script contains object, which contains objects and functions. Lets say that we want to find function named "HelloWorld".
With firebug, we loop through these objects and methods with a script, which looks something like this
// Parameter is target object.
function getFunctionNames(obj) {
// For each objects / functions
for (var id in obj) {
// Focus only on functions
if (typeof(obj[id]) == "function") {
// Get name of the function.
// console.log("Function: " + obj[id].toString());
// Code above returns a block of code without the name. Example output:
// Function: function(name) { alert("Hello World! Hello " + name + "!"); }
//
// Expected output would be
// Function: HelloWorld
}
}
}
obj[id].toString() returns a block of code instead of a name.
obj[id].name returns an empty string. Anonymous function(?).
I cannot use arguments.callee.name because I cannot modify the target code.
I could just browse objects and functions in firebug or just read source code, but I'm looking a way to do it with Javascript.
Edit
For real world example, head to Youtube and try to get the name of function "setMsg()" from "yt" object via Javascript.
Edit2
Accepting Simon's answer for being kinda closest what I was looking for. It appears that I was seeking variable name, rather than function name. While answer didn't help me on original problem, it surely answered to original question. Paul Draper's comments helped me to right direction.
Thanks!
Use obj.name
Note that arguments.callee returns a function. name is property on every function (though it's empty for anonymous functions), so that's why arguments.callee.name works.
This works for webkit (Chrome and Safari), Firefox, and possibly others. It does not work for IE: function.name not supported in IE.
As mentioned, the function doesn't have any intrinsic name other than the "" it gets from being an anonymous function. Some browsers (Firefox, probably Chrome, maybe others) do however perform some limited form of static analysis to figure out names of declared functions, to help with error stack traces. You can get to it in an relatively cross-browser way by getting setMsg to throw an exception and then parse exc.stack:
// cheat with .% in Firebug; there might be other ways of doing this, I dunno:
yt.setMsg.%m.za.__defineSetter__('a', function() { throw new Error(); });
try { yt.setMsg('a', 'a'); }
catch(e) { alert(e.stack.split('\n')[2].split('#')[0]); }
... On the other hand, this is a pretty terrible hack and dependent on the actual function involved (and if you know the function, you probably know its name already). It does work a bit more reliably when done from inside the function.
If you restrict yourself to just Firefox and are doing this for debug purposes, there are better ways of getting to it. Set devtools.chrome.enabled to true in about:config, open a Scratchpad (Shift+F4), set it to environment: browser, and run the following:
Components.utils.import("resource://gre/modules/jsdebugger.jsm");
window.addDebuggerToGlobal(window);
dbg = new Debugger();
dw = dbg.addDebuggee(content);
f = content.wrappedJSObject.yt.setMsg;
name = dw.makeDebuggeeValue(f).displayName;
dbg.removeDebuggee(content);
alert(name);
In both cases, you will note that it alerts "m.ya" instead of "setMsg", and indeed this is because the function was originally declared as m.ya = function() { ...; }. There is no reason why "setMsg" would be a preferable name, from the point of the browser.

re: jQuery; binding global ajax events to non-DOM objects

Basic module question here: I can get the global handlers to work fine if I abandon my module pattern, but this test code doesn't work and I'm having great difficulty figuring out why:
var MyModule = (function($, my) {
$(my).ajaxStart(function() { window.alert("ajax start"); });
$(document).ready(function() {
$.ajax(SOME_AJAX_SETTINGS_HERE);
});
return my;
}(jQuery, MyModule || {}));
The handler attached to the MyModule instance never runs when the ajax call executes.
If I instead attach the handler to $(document) it works just fine, but i'm curious why that global event doesn't seem to be binding correctly to the module object.
EDIT: Here's an even simpler example:
var x = {};
$(x).ajaxStart(function() { window.alert("foo"); } );
$.ajax(SOME_AJAX_SETTINGS);
If I replace $(x) with $(document), it works... it just seems that I can't bind ajaxStart (or the other global ajax events) to non-DOM objects?
#Nucleon, this doesn't seem completely correct to me, and here's an example of why:
var item = {foo: function() { window.alert("shorthand"); } };
$(item).bind("foo", function () { window.alert("longhand"); } );
window.alert("about to trigger (locally)");
$(item).trigger("foo");
window.alert("now we triggerHandler (locally)");
$(item).triggerHandler("foo");
here, the (custom) event "foo" is bound via two patterns, and the different trigger methods show the pattern specificity... you'll get the "longhand" alert twice and the "shorthand" alert once.
now, you can also fire off a global event with $.event.trigger("foo") (BUT, no equivalent global triggerHandler exists).
the problem is if you add this code to the end:
window.alert("now we trigger (globally)");
$.event.trigger("foo");
neither handler is run for this global event triggering.
IF, however, you replace item with some DOM-type element -- as you suggest above -- then both the local AND global events are handled, with the caveat that the "shorthand" handler now won't work*. so it appears that while local handling works with non-DOM objects, it's specifically global handling that doesn't work.
-m
*the shorthand handler stops working because the shorthand pattern ONLY applies to non-DOM objects. i personally think this "shorthand" behavior of non-DOM objects should be removed from jQuery. very few people know about it, and it's only documented in one obscure sentence on the global jQuery object page... it's behavior could drive people who aren't aware of it crazy since it implicitly does a binding step that folks are likely not aware of.
ah, okay... the $.event.trigger functionality isn't public, though it's used by the underlying event-firing mechanisms. i guess i just shouldn't be using it.
turns out this behavior has been challenging for the jQuery devs to implement correctly, and it looks like they're abandoning it completely (or that's what i gathered from this bug report being marked "fixed": DEPRECATE AJAX "GLOBAL" EVENT BEHAVIOR).
but i'm still a bit confused about .trigger being documented as behaving the same way for DOM or non-DOM objects, as only one of the following alerts are run:
var plain = {foo: function() { window.alert("plain"); } };
var dom = $("<div></div>");
dom.foo = function() { window.alert("dom"); };
console.log(plain.foo); // make sure we see function foo on the plain object.
console.log(dom.foo); // now make sure we see function foo on the DOM object.
$(plain).trigger("foo"); // raises the "plain" window alert.
$(dom).trigger("foo"); // raises no alert.
i guess this means the foo property set on dom isn't actually a "proper" JS property in the traditional sense (despite it showing up correctly when inspecting the object on the console)?
note: this is all now for the sake of knowledge -- as i'll just take the advice of the "fix" in the bug link above and just attach all my AJAX (and other global) events to document, since it appears it'll be the enforced behavior in jQuery 1.9.
but i've already taken the blue pill and feel like i'm too far down the rabbit hole to climb back out.
Simply replace
var x = {};
with
var x = $("<div></div>");
Javascript events require binding to valid DOM objects (not just javascript objects). Now, it is not required that the DOM objects are actually attached anywhere. Javascript's event system is based off of bubbling events up and down a hierarchy chain of the available and relevant DOM objects. A simple {} does not qualify.

jQuery Event handlers for Javascript functions

The question title is a bit obscure so let me explain.
A requirement that was only explained to me recently is the use of jQuery in my project. But it has been noted that my functions are fine as is and can be reused as long as they contain some jQuery.
So I'm exploring the world of event listeners for the first time (js side not in the HTML)
A standard Jquery onclick event:
referenceToElement.onclick = function () { alert('here'); };
One thing I notice is that the function doesn't actually have a name. Is there any clean way of doing something like:
referenceToElement.onclick = myOldJavascriptFunction();
function myOldJavascriptFunction()
{
//blahblahblah
}
Is this good practice or is there a better way to do it. Will this even work now that I think of it?
Even if the question is actually worth a downvote, since you could easily answer all those questions by searching, I'll give you a headsup.
That
referenceToElement.onclick = function () { alert('here'); };
is for sure no jQuery standard thing. It's pure Javascript, adding a property to a DOM reference, in this case an anonymous function. However, you basically asked two questions now.
can we give that anonymous function a name ? => YES
can we reference a function which is defined somewhere else ? => YES
To give it a name, we can just create a named function expression like this
referenceToElement.onclick = function myFunctionName() { alert('here'); };
To reference a function, we just pass in it's name
referenceToElement.onclick = myOldJavascriptFunction;
Finally, jQuery's syntax to add the same event listener would look like this:
$( referenceToElement ).click( myOldJavascriptFunction );
Yes, you were very nearly right:
referenceToElement.onclick = myOldJavascriptFunction;
Note the lack of parentheses. This passes a reference to the function to the onclick event. Also note that this is plain old JavaScript, not jQuery. The jQuery way of doing this is more like:
$(referenceToElement).click(myOldJavascriptFunction);
Your first example there is plain and normal javascript, nothing to do with jQuery at all. If you were using jQuery, the line would look like this:
$(referenceToElement).click(function () { ... });
But in any case, it seems like you question is about anonymous functions. You can assign the function to a variable name, or use a function declaration and still reference that function by name:
function myFunction () { ... }
$(referenceToElement).click(myFunction);

MOOTOOLS variable scope

I'm using mootools:
I can't figure out how to use a variable when using an addEvent.
I want to use a for next loop to set values in a loop:
for (x=0;x<num;x++){
var onclickText = 'function (){onclick="addPageMoveEvent('+x+'"); }';
$('pageNum'+x).addEvent('click', onclickText);
}
>
I've search forums but not found any help.
Any help would be great.
Thanks
The addEvent method in MooTools accepts two arguments:
myElement.addEvent(type, fn);
Arguments:
type - (string) The event name to monitor ('click', 'load', etc) without the prefix 'on'.
fn - (function) The function to execute.
It does not take a string and passing a string such as "myFunction()" or "function() { myFunction(); }" will not work.
Since you are inside a loop, and the variable x will share the environment, you need to wrap its value inside another closure. One way is to use an additional closure:
$("pagenum" + x).addEvent("click", (function(value) {
return function() { addPageMoveEvent(value); }
})(x));
See all questions on StackOverflow regarding this particular problem of creating closures within loops.
Also worth checking out is this MDC article - Creating closures in loops: A common mistake
Warning: this first example will not work! Read on for an explanation.
You are confusing onclick HTML syntax with the MooTools addEvent. Try
for (var x=0;x<num;x++){
$('pageNum'+x).addEvent('click', 'addPageMoveEvent('+x+');');
}
This is simpler and cleaner, but might still not do what you want. This code will call the function addPageMoveEvent every time the link is clicked... is that what you want?
Since MooTools doesn't allow the above method, you must use the following:
A programmatically more interesting and less hazardous way to do the same would be:
factory = function (x) { return function() { addPageMoveEvent(x); }; };
for (var x=0;x<num;x++){
$('pageNum'+x).addEvent('click', factory(x));
}
This uses a factory for creating closures that hold your values of x... rather complex code, but it's the purist way. It also avoids using the scary eval that occurs because you feed addEvent a string. (It seems that MooTools doesn't like the other option anyway.)
That a use case for mootools pass method.
for (x=0;x<num;x++){
$('pageNum'+x).addEvent('click', addPageMoveEvent.pass(x));
}
Pass internally creates a closure that holds x in the his scope, so when the click event is fired it has the right value cause its not the same from the for loop.

Javascript plugins design pattern like jQuery

Could someone write down a very simple basic example in javascript to conceptualize (and hopefully make me understand) how the jQuery plugin design pattern is done and how it works?
I'm not interested in how creating plugin for jQuery (so no jQuery code here at all).
I'm interested in a simple explanation (maybe with a bit of Javascript code) to explain how it is done the plugin concept.
Plz do not reply me to go and read jQuery code, I tried, but I it's too complex, otherwise I would have not post a question here.
Thanks!
jQuery has a library of functions stored in an internal object named fn. These are the ones that you can call on every jQuery object.
When you do $("div.someClass") you get a jQuery object containing all <div> elements of that class. Now you can do $("div.someClass").each( someFunction ) to apply someFunction to each of them. This means, that each() is one of the functions stored in fn (a built-in one in this case).
If you extend (add to) the internal fn object, then you automatically make available your custom function to the same syntax. Lets assume you have a function that logs all elements to the console, called log(). You could append this function to $.fn, and then use it as $("div.someClass").log().
Every function appended to the fn object will be called in such a way that inside the function body, the this keyword will point to the jQuery object you've used.
Common practice is to return this at the end of the custom function, so that method chaining does not break: $("div.someClass").log().each( someFunction ).
There are several ways to append functions to the $.fn object, some safer than others. A pretty safe one is to do:
jQuery.fn.extend({
foo: function() {
this.each( function() { console.log(this.tagName); } );
return this;
}
})
Tomalak already posted almost everything You need to know.
There is one last thing that helps jQuery do the trick with the this keyword.
it's amethod called apply()
var somefunction=function(){
alert(this.text);
}
var anObject={text:"hello"};
somefunction.apply(anObject);
//alert "hello" will happen
It really helps in creating abstractions so that framework/plugin users would just use this as intuition tells them, whatever there is inside Your code
It works, as many other js frameworks, using javascript prototype orientation.
For instance you can declare a simple function
var alertHelloWorld = function() {
alert('hello world');
}
And then tie it to an existing object (including DOM nodes)
document.doMyAlert = alertHelloWorld;
If you do this
document.doMyAlert();
The alertHelloWorld function will be executed
You can read more about javascript object prototyping here

Categories