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~

NotUser9123
  • 143
  • 1
  • 1
  • 9
  • So you want the third property as a function who call the others property ? – Alexis Jan 09 '17 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? – Aaron Jan 09 '17 at 13:42
  • @Aaron Sorry about my typo, it should be package.x and package.y. I have fixed it~ thank you~ – NotUser9123 Jan 09 '17 at 13:44
  • 1
    @Alexis Yup, that is what I mean~ – NotUser9123 Jan 09 '17 at 13:45
  • According to your comment on Alexis' answer (and also to the console.log line), you don't want the third property to be a function, but rather the result of its execution – Aaron Jan 09 '17 at 13:49
  • `package` is a reserved word in "strict mode". You should assign another name – RomanPerekhrest Jan 09 '17 at 13:52
  • @Aaron @Alexis My fault, what I mean is how can I get `obj` properties within `bar()` function, without change the code outside `bar()`... Sorry about my poor english... – NotUser9123 Jan 09 '17 at 13:53
  • @RomanPerekhrest O.K. I changed `package` into `obj`, will it seems nicer now? – NotUser9123 Jan 09 '17 at 13:54
  • No problem, I think it's quite clear now, and that Alexis had understood once you commented on his question. Still, it's better to comment about it in case people trying to help you have misunderstood. I don't know how to solve your problem myself, so I'll only be able to wish you good luck ! – Aaron Jan 09 '17 at 13:57
  • 3
    If the code is never modified, `return 3` solves your problem :-) – Bergi Jan 09 '17 at 13:59
  • @Aaron That's fine~ thank you all the same~ :-) – NotUser9123 Jan 09 '17 at 14:03
  • @Bergi is there any way besides `return 3` or overwrite `console.log()`? of course `return 1+2` or `return Math.floor( 3.1415926535 )` is not included~ However it is still a good joke ~ :-) – NotUser9123 Jan 09 '17 at 14:05
  • Due to the order of execution, overwriting `console.log` is not as simple a solution as i first thought, that's why i deleted the comment soon after. – ASDFGerte Jan 09 '17 at 14:08
  • Can you please post a link to the quiz where you found this? – Bergi Jan 09 '17 at 14:40
  • I found this in a group-chat of _weixin_ which is an instant messenger, it dose not have a link...... – NotUser9123 Jan 09 '17 at 14:57

3 Answers3

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;
    }
    
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
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

RomanPerekhrest
  • 88,541
  • 4
  • 65
  • 105
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;
    }
Pranay Kumar
  • 2,139
  • 1
  • 15
  • 15