2

Let's start from the code:

function say(name) {
    var ghost=function () {
        function ghost() {
            alert('!');
        };

        return body;
    };

    eval("var body=''+"+name+';');
    eval(name+('=('+ghost).replace('body', body)+')();');
    eval(name+'();');
}

function Baal() {
    if ('undefined'===typeof ghost) {
        say('Baal');
        return;
    }

    ghost();
}

say('Baal'); // or just Baal();

Looks like that saying the devil's name invoke his presence (well, maybe he needs somebody for spiritual possession) ..

As you can see the ghost doesn't exist along with Baal, but we can invoke it since there're evals in say(name).

say(name) reassigns Baal to its code body as a closure and makes it captured a ghost method, that's how things work. But I'm trying to avoid eval ..

So .. let me reword the question:

How do I make a nonexistent(and not a member or global) method invocable without using eval?

Dave Newton
  • 158,873
  • 26
  • 254
  • 302
Ken Kin
  • 4,503
  • 3
  • 38
  • 76
  • 8
    It'd be nice to use a title that made sense w/o reading the question. – Dave Newton Sep 30 '13 at 18:46
  • 1
    Is there a reason you’re doing `eval("var body=''+"+name+';');`? – Ry- Sep 30 '13 at 18:50
  • 1
    Can you pick an example that makes sense in the real world, please? – Ry- Sep 30 '13 at 18:53
  • 2
    I tried to make sense of this... I could not. Sorry. – Niet the Dark Absol Sep 30 '13 at 18:53
  • 1
    The whole `Baal()` thing is pointless, by the way. `ghost` is never defined. I think you think this thing is doing something other than it is. – Ry- Sep 30 '13 at 18:54
  • What your `eval` is doing is just rewriting the `ghost` function. You don't need to do that. The `ghost` function already has access to the `name` variable. – gen_Eric Sep 30 '13 at 18:54
  • 1
    @minitech: No, I believe that he thinks that it does exactly what he thinks it does. ;) (Sorry, ot, but couldn't help myself.) – Guffa Sep 30 '13 at 18:56
  • 2
    @Guffa: I think I thought about that less than I now think I should have thought to think about it. – Ry- Sep 30 '13 at 18:57
  • 1
    I think there is too much thinking going on. – Patrick Evans Sep 30 '13 at 18:57
  • I don't think I understand this either. What are you trying to do? `ghost` only exists inside `say`, maybe you need to just make that global? – gen_Eric Sep 30 '13 at 18:58
  • I've previously asked http://stackoverflow.com/questions/18753802/how-to-achieve-pseudo-classical-inheritance-right-on-the-class-declaration and I'm trying to make a method invocable only in the private scope, such as a emulation of `base`(though I did not create objects in this question). – Ken Kin Sep 30 '13 at 19:00
  • 1
    @KenKin: Don't do that, you're doomed if you try to extend the capabilities of the language. There are enough usable patterns available that provide privacy. Use them, don't try to be clever. – Bergi Sep 30 '13 at 19:07
  • @Guffa: Thank you for good understanding of my question! – Ken Kin Sep 30 '13 at 19:16
  • @Dave Newton: Thank you for the revision although I feel the original is more interesting .. – Ken Kin Sep 30 '13 at 20:07
  • @KenKin Question titles should be descriptive, not interesting. – Dave Newton Sep 30 '13 at 20:25
  • @DaveNewton: You are correct, I'll stay with it. – Ken Kin Sep 30 '13 at 20:35

4 Answers4

2

Let me rephrase your question, just to make sure I’ve got it. Given a function, you want to put a new variable in its scope, without that scope being the global scope or a scope shared between the caller and the subject, without using eval (or the equivalent new Function and other hacks depending on the environment).

You can’t.

In the case you just mentioned, you could define one function, base(), that uses arguments.callee.caller.

Don’t do that.

Ry-
  • 218,210
  • 55
  • 464
  • 476
  • Is this saying that there's no way except `eval` and `new Function`? – Ken Kin Sep 30 '13 at 19:07
  • 1
    @KenKin: This is saying that there’s no way. `eval` and `new Function` are broken in their own right, especially in terms of what you’re aiming to do here. – Ry- Sep 30 '13 at 19:08
  • @KenKin: Not really. It means that by design, functions can't muck around with each other's innards -- which, overall, is very *good* news. (Among other things, it means that you don't have to worry about your functions being corrupted by foreign code.) You need to take a step back, look at the logical problem rather than the syntactic one, and figure out what you're trying to accomplish by doing this; i guarantee you there's a cleaner way to do it, but the specifics of that way depend entirely on what you needed to do in the first place. – cHao Sep 30 '13 at 19:33
  • @cHao: I'm not sure what do you mean the logical problem, mind to elaborate? – Ken Kin Sep 30 '13 at 20:00
  • @KenKin: Looking at what you need to do rather than just one way to accomplish it, I think. – Ry- Sep 30 '13 at 20:41
  • Not yet have a good idea .. what I'm trying to do is much like to emulate reserved words, just to emulate .. – Ken Kin Sep 30 '13 at 20:46
  • minitech's explanation is almost exactly what i meant. Zoom out and look at the bigger picture -- as in, not just what you are trying to do here, but what overall made what you're trying to do seem like the right answer. You've painted yourself into a corner somewhere along the way, and the big-picture view is what will help you out of it. – cHao Sep 30 '13 at 21:27
  • @cHao: Ah .. too many `what`, I'm not good at English parsing .. hmm, the first one I'd like to understand of the `what` sentences would be `what overall made what you're trying to do seem like the right answer`. Does that mean what I've done with the code is the right way as my attempt? – Ken Kin Sep 30 '13 at 21:42
  • @KenKin: No, it means that something you've done before now is wrong, and that wrongness is now making you try to do something impossible. Go back and rethink the decisions that led to this point; there will be a better answer that works within JavaScript. – cHao Oct 01 '13 at 03:53
1

The short answer: You don't.

That scope is not available. If you were to attach the scope then it would be available inside of the scope used. You could then access the method handles. I assume this is not what you were looking for, but here is what that would look like. demo

function say(name){
 var methods = {};   
 methods.Baal = function(){
  alert("!");  
 };

 return methods[name];//this could invoke as well: methods[name]()
}

var handle = say('Baal');
handle();

What your evals break down to is something along these lines (although with dynamic content from string building - this is the end result)

function say(name) {
 var Baal = (function () {
    function ghost() {
        alert('!');
    };

    return function(){ 
     if ('undefined'===typeof ghost) {
      say('Baal');
      return;
     }
     ghost();
   }
 })();
 Baal();
}

say('Baal'); // or just Baal();

Note that the meat of what happens here is from the function Baal, namely that it calls a hardcoded ghost() which in turn calls a hardcoded alert. Why go through all of this trouble to access a hardcoded function?

A better way would be to inject this function as a callback which expects some parameters to be injected.

jsFiddle Demo

function say(callback){
 var params = "!";
 if( typeof callback == "function" ){
  callback(params);   
 }
}

say(function(params){
 alert(params);  
});
Travis J
  • 81,153
  • 41
  • 202
  • 273
  • Thank you, especially for the expansion of evaluated code. One thing I have to mention is that the hardcoded part is just for example, it in fact will not be like this. – Ken Kin Sep 30 '13 at 20:22
0

So if I understand, it seems like you want to create an alias of eval: Something like

#Note this code is not intended as a solution, but demonstrates
#an attempt that is guaranteed to fail.
#
function myAlias(ctx) {
eval.call(ctx, 'var ghost = 42');
}

myAlias(this);
alert(ghost);

Javascript allows many funky sleight-of-hand tricks especially with closures, but this is maybe the one impossible thing that javascript cannot do. I've tried at length to do this exact same thing, and I can tell you that you'll run into nothing but complaints from the browser, saying that eval cannot be re-contexted or aliased in any way.

CodeOwl
  • 662
  • 2
  • 9
  • 23
  • `eval.call` does make any difference. Do not use it. [Understand `eval`](http://perfectionkills.com/global-eval-what-are-the-options/). – Bergi Sep 30 '13 at 20:27
  • That's exactly what I was saying. eval.call is impossible. Do not use it. – CodeOwl Sep 30 '13 at 20:38
  • Oh, sorry. You should've marked it more clearly that you do *not* propose that code (maybe an inline comment?). I can't remove my downvote until you edit, though. – Bergi Sep 30 '13 at 21:42
0

It's very difficult for me to read through your code and figure out what you are trying to accomplish with it, but it appears that you are trying to introduce a variable into the current scope so that you can call it. You cannot do this in javascript with the method that you demonstrated. Scoping only ever "flows down". By that I mean that a variable or function defined within a function will only be available to that function and any other functions defined therein. Your function named ghost will only ever be available within the function where it is defined, regardless of when that function is evaluated.

What you can do, however, is write a function that returns a function. You can then call that function and assign the result to a variable in the scope where you want to expose functionality. Doing that would look something like this.

function defineSpecialAlert() {
    return function(name) {
        alert(name + "!");
    };
}

var newlyDefinedMethod = defineSpecialAlert();

newlyDefinedMethod("Baal");
TwentyMiles
  • 4,063
  • 3
  • 30
  • 37
  • `defineSpecialAlert` is a similar thing as `var ghost=function () { .. ` in my code, but the difference is I declared a `ghost` method which only exists in `Baal`. – Ken Kin Sep 30 '13 at 19:38
  • The difference is that in your method, you actually evaluate the method, in this one, I return the method directly. There are two big differences. 1) I return the defined method from the function. In yours you simply define it and then never use it again. 2) I do not evaluate anything within the children's scope. I simply return a function object so that it can be evaluated later. – TwentyMiles Sep 30 '13 at 19:47
  • The returning function may not be my code but they may invoke the ghost method which is not declared elsewhere except my code .. – Ken Kin Sep 30 '13 at 19:56