2

I got a quiz about function writing like this:

/* ====== can not modify this line below till next dividing line ========= */
function foo() {
    var obj = {
        x : 1,
        y : 2,
        bar : bar()
    }

    function bar() {
/* ====== can not modify this line above ================================= */

        /* so how can I get obj.x and obj.y here and returns their sum ..? */

/* ====== can not modify this line below till next dividing line ========= */
    }
    return obj;
}

console.log( foo().bar ); // expected 3 
/* ====== can not modify this line above ================================= */

I found two ways myself, one is get foo.toString() and do some REGEX magic.

the other is register a global variable like window.run to control foo() runs only once.

However I am wondering is there any other ways can solve this?

Thank you for your reply~

15
  • So you want the third property as a function who call the others property ? Commented Jan 9, 2017 at 13:41
  • when you write bar.x and bar.y, I assume you mean package.x and package.y ? Also, since you expect 3, I guess you want bar() to return their sum? Commented Jan 9, 2017 at 13:42
  • @Aaron Sorry about my typo, it should be package.x and package.y. I have fixed it~ thank you~ Commented Jan 9, 2017 at 13:44
  • 1
    @Alexis Yup, that is what I mean~ Commented Jan 9, 2017 at 13:45
  • 3
    If the code is never modified, return 3 solves your problem :-) Commented Jan 9, 2017 at 13:59

3 Answers 3

5

You cannot. bar is called before the object is constructed, obj will be undefined and the already-evaluated values of the previous properties (1 and 2) are somewhere inaccessible in memory only. See also Self-references in object literal declarations.

Given you found this question in a quiz with pretty arbitrary restrictions, they seem to expect a trick answer. There are several ways:

  • Access to source code and evaluate the object literal yourself
  • Simply return a constant, given that obj.x and obj.y are constant in the given code as well
  • Overwrite console.log to do your bidding, for example

    function bar() {
        var log = console.log.bind(console);
        console.log = function(p) {
            log(p.valueOf());
        };
        return {
            valueOf: function() {
                return obj.x + obj.y;
            }
        };
    }
    

    Doesn't work unfortunately due to console.log being dereferenced before foo() is called.
    A similar approach does work in environments where the console.log behaviour can be customized without needing to overwrite anything:

    function bar() {
        return {
            inspect: function() {
                return String(obj.x + obj.y);
            }
        };
    }
    
  • Just call foo() yourself to get the values, but don't recurse infinitely on bar:

    function bar() {
        if (foo.stop) return null;
        foo.stop = true;
        var res = foo().x + foo().y;
        foo.stop = false;
        return res;
    }
    
Sign up to request clarification or add additional context in comments.

1 Comment

I have learned so much from this answer, I am really really appreciated ~
1

If it's really "locked up" for modification use the following "magic"(for test case):

function foo() {
  var obj = {
    x : 1,        
    y : 2,
    bar : bar()
  }

  function bar() {
    var magic_sum = foo.toString().match(/var obj =\s*?\{\s*?x\s*?:\s*?(\d+),\s*?y\s*?:\s*?(\d+),/)
                    .slice(1,3).map(Number);

    return magic_sum[0] + magic_sum[1];
  }
  return obj;
}

console.log(foo().bar);

The algorithm:

foo.toString() - getting function text representaion

.match(/var obj =\s*?\{\s*?x\s*?:\s*?(\d+),\s*?y\s*?:\s*?(\d+),/) - matching x and y property values

.slice(1,3) - getting values from captured groupes(for x, y properties)

.map(Number) - casting each value to Number type

2 Comments

It is really the "REGEX magic" I have metioned in the main topic ~
@NotUser9123, do you like it?
1
foo.callingTime = foo.callingTime || 1;
    if(foo.callingTime === 1){
      foo.callingTime++;
      foo.tempObj = foo();
      return foo.tempObj.x+foo.tempObj.y;
    }
    else if(foo.callingTime === 2){
      foo.callingTime = 1;
      return;
    }

2 Comments

remember functions are objects in Javascript
using callingTime is also good! My way is global variable, calling time and foo property are both better than mine ~

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.