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 ~

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.