Mykhaylo Adamovych
Mykhaylo Adamovych

Reputation: 20956

javascript eval how to

var obj = {x : 2}; eval.call(obj, 'x'); // ReferenceError: x is not defined How to change last line to get value by variable name?

Clarification
'x' - any expression in some obj context, possibly not a obj parameter

Original problem

/**
 * Guards 'this' when delegating method as a callback to others. Arguments aren't known beforehand and will be provided by consumer.
 */
delegate = function(context, func) {
    return function() {
        var args = [];
        for ( var i = 0; i < arguments.length; i++)
            args.push(arguments[i]);
        func.apply(context, args);
    };
};

reference = function (context, 'expression to evaluete to get reference value') {
    ... TODO
};

'delegate' - delegates function call, how to 'delegate a reference' with possibility to 'dereference' in different context?

Updates
here is 2 possibilities to use eval:
1. eval.call(null, 'evaluate expression in global context');
2. eval('evaluate expression in context where eval function was called')
3. how to eval in some predefined 'context' ?

I want to mimic C++ reference when you can put it in any context and 'dereference' to value (but when in moment you dereference it value could be changed and you should get new value). Like in javascript you put object as function argument and anytime you get obj.someproperty you'll get newest someproperty value (if it was changed). But this will not work when you will try to pass a primitive or what if whole object will be changed by some other. How to 'pass a reference' to 'whole object' to some other context?

Got clear understanding what I want :)
javascript how to create reference

What I was looking for:

createReference = function(context, prop) {
    return function() {
        return context[prop];
    };
};

Provider = function() {
};
Provider.prototype.x = 5;
Provider.prototype.getXRef = function() {
    return createReference(this, 'x');
};
Provider.prototype.incrementX = function() {
    this.x = this.x + 1;
};

var provider = new Provider();
var refX = provider.getXRef();
provider.incrementX();
alert(refX());

Tanks Brianpeiris!

Upvotes: 3

Views: 921

Answers (4)

T.J. Crowder
T.J. Crowder

Reputation: 1073968

Update: I may understand now, or at least I have two guesses. See Update 1 and Update 2 below.


Original answer:

You almost never need eval. But just so you know, you're not alone, there are a lot of people who think you have to use eval for this. It's actually a lot easier than that.

If you want to access a property in an object, you can use either dotted notation (obj.x) or bracketed notation (obj['x']). Note the quotes in the latter: All property names are strings, and the bracket notation lets us use actual strings when looking up properties. So given this object:

var obj = {foo: 42};

all of these put 42 in x:

x = obj.foo;
x = obj['foo'];
x = obj['f' + 'o' + 'o'];
y = 'foo'; x = obj[y];   // Using the "foo" string from variable `y`
x = obj[getTheAnswer()]; // Using the return value of a function (`getTheAnswer` returns "foo")

As you can see, you can use any expression to come up with the name of the property (as a string), and then use that property name string in the brackets.


Update 1: In your reference function, if you literally want to accept a string containing an expression you know nothing about, evaluate the string, and use the result to look up a property on the object, here's how you do that — I want to be clear that I would strongly recommend solving this problem a different way, but without knowing more about the actual problem, I couldn't say how. So here it is:

var reference = function(context, expressionString) {
    return context[eval(expressionString)];
};

So for example:

var obj = {p5: 42};
alert(reference(obj, "'p' + (4 + 1)")); // alerts 42

Live example

Apologies if I've guessed wrong.


Update 2:

From the fact that you're giving delegate and reference together, and the comment "... how to 'delegate a reference' with possibility to 'dereference' in different context?..." it sounds like you're trying to find a way to find out what the original function was, if you only have the delegate. E.g.:

function foo() { ... }
var fooDelegate = delegate(someObject, foo);
var original = reference(someObject, fooDelegate);
alert(original === foo); // alerts true

If that's what you're trying to do, you can't do it without setting something up in delegate that you later look for in reference. Fortunately, that's easy:

// Create a function that, when called, will always call the given
// function with the given context, passing along the call arguments.
delegate = function(context, func) {
    var newFunc = function() {
        return func.apply(context, arguments);
    };
    newFunc.__delegate__original = func;
    return newFunc;
};

// If given a function created by `delegate`, returns the original
// underlying function. If the given function is not from `delegate`,
// returns `func` unchanged.
reference = function(func) {
    return func.__delegate__original || func;
};

Note that you don't need the context parameter on reference.

And again, apologies if I'm guessing wrong.


Off-topic: Re your delegate function, you'll be glad to know that you don't have to copy the arguments before you pass them into apply. (You do probably want to return the return value of the target function, though.) This works fine cross-implementation:

delegate = function(context, func) {
    return function() {
        return func.apply(context, arguments);
    };
};

...because apply is carefully defined in the specification to allow any array-like object, it doesn't require an actual Array instance. See Section 15.3.4.3 of the spec for details.

Upvotes: 4

pkaeding
pkaeding

Reputation: 37633

To extend @brianpeiris's answer, you can decide what parameter you want to read from the object dynamically, at runtime. For example:

var obj = {x: 2);
var field = 'x'; // or do some other operation to figure out the field you want
var value = obj[field];
if (value) {
  // the field exists and has a true value
} 

Upvotes: 1

Justin Pearce
Justin Pearce

Reputation: 5097

Or, if you absolutely have to use eval:

var obj = {x : 2};
var myVar = eval(obj.x);
alert(myVar);

Overkill, I know.

Upvotes: 0

brianpeiris
brianpeiris

Reputation: 10795

This should do it, no need for eval:

var obj = {x: 2};
var value = obj['x'];

Upvotes: 5

Related Questions