4

How to get an object from a closure, that's confusion with me, here is the question:

var o = function () {
   var person = {
       name: 'jonathan',
       age: 24
   }
   return {
       run: function (key) {
           return person[key]
       }
   } 
}

question: How do i get original person object without changing the source code.

5 Answers5

10

var o = function() {
  var person = {
    name: 'jonathan',
    age: 24
  }
  return {
    run: function(key) {
      return person[key]
    }
  }
}

Object.defineProperty(Object.prototype, "self", {
  get() {
    return this;
  }
});

console.log(o().run("self")); // logs the object

This works as all objects inherit the Object.prototype, therefore you can insert a getter to it, which has access to the object through this, then you can use the exposed run method to execute that getter.

James Coyle
  • 9,922
  • 1
  • 40
  • 48
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
  • Seems I'm good at [interview questions](https://stackoverflow.com/questions/48270127/can-a-1-a-2-a-3-ever-evaluate-to-true/48270313#48270313) :) – Jonas Wilms Feb 21 '19 at 13:37
  • 2
    @adiga it depends how you "source code". If you just mean "don't change the code that defines the closure" i.e., what's quoted in the OP, then defining a getter works. If it means without changing ANY code, then it's not possible by definition because you are not allowed to change anything at all. – VLAZ Feb 21 '19 at 13:41
  • 1
    I'd use a `Symbol` instead of `"self"` so I don't break any object which could have `"self"` (or any other key) defined. – Dmitry Feb 21 '19 at 13:45
  • (@VLAZ I wasn't being serious) – adiga Feb 21 '19 at 13:45
  • @dmitry I wouldn't use this code at all. In reality no one would have a function `o` that returns keys of a person? This is constructed code meant to challenge interviewies. – Jonas Wilms Feb 21 '19 at 13:48
  • @JonasWilms then you'd get more point on the interview for knowing what `Symbol` does :) – Dmitry Feb 21 '19 at 13:51
  • That's a good idea, but i'm sure("without changing source code")the meaning of this sentence whether or not to add this. In addition, is there any other way? – 芝华塔尼欧 Feb 21 '19 at 13:57
  • @芝华塔尼欧 How do you code without writing code? I understand it as "don't modify the code presented, add code" ... If you arent allowed to code at all, then I would answer: "I go to the debugger, place a breakpoint and look at the scope" or "I know that his name is Jonathan, why should I invest so much effort just to find out the obvious?" (That said, this is a horrible interview question) – Jonas Wilms Feb 21 '19 at 14:01
  • @芝华塔尼欧 if you are not allowed to change ANY source code then this task is impossible. Even if `person` were exposed, then you cannot write any code that takes it because you aren't allowed to write any code. If the limitation is to not touch the closure, then changing the `Object` prototype is technically valid. You've not changed any code there. You just changed how all objects work in your codebase which is how prototypes *work* and how they are *supposed* to work. It just happens to be bad practice in general. – VLAZ Feb 21 '19 at 14:07
  • @JonasWilms Yeah, I really don't know, Maybe you are right. – 芝华塔尼欧 Feb 22 '19 at 15:02
  • @VLAZ Yes, that's really one way to changing `Object` prototype. – 芝华塔尼欧 Feb 22 '19 at 15:06
2

You can get the keys by running

o().run("<keyname>"))

Like that:

var o = function () {
   var person = {
       name: 'jonathan',
       age: 24
   }
   return {
       run: function (key) {
           return person[key]
       }
   } 
}

console.log(o().run("name"));
console.log(o().run("age"));
NullDev
  • 6,739
  • 4
  • 30
  • 54
  • That's meaningless if you don't know the property names. – Nisarg Shah Feb 21 '19 at 13:28
  • @NisargShah I can look at the code and see the property names, or maybe they are documented... lol – epascarello Feb 21 '19 at 13:29
  • @NisargShah As far as I know it would be impossible to return the whole person object without changing the code. And since OP said that was an interview question, I believe this is the way he was supposed to do it. – NullDev Feb 21 '19 at 13:30
  • @epascarello I'm pretty sure that the interviewer is not expecting that answer. :) The expected answer would be "You can't". – Nisarg Shah Feb 21 '19 at 13:30
  • @JonasWilms Jup, just saw your answer. Wasn't aware of that. +1 from me! :) – NullDev Feb 21 '19 at 13:36
  • I used to give interviews.... I expect any answer.... better than no answer. And since the interviewer is not here, than we can only GUESS what they were looking for. – epascarello Feb 21 '19 at 13:41
  • @epascarello out of curiosity - would you prefer an answer of "it can't be done" or what Jonas Wilms did? Because I've also given interviews (fora short time) and personally can't say if any of the two is preferable because I wouldn't even ask the question. Not in its current form, at least - I'd ask more generally about closures. – VLAZ Feb 21 '19 at 13:44
  • @VLAZ agreed. Being a good developer doesn't mean that you have to know how to break some constructed code. I recently had a great interview, and we didn't talk about code at all. – Jonas Wilms Feb 21 '19 at 13:50
  • 2
    All three answers "can't", "read props", and "define/eval" all show the basic concept that they know that the person is not accessible directly. For no, I would follow up with another answer on how can you "recreate" with the available method. For the method one, I would have followed up with why can't you get "person" directly just so I hear the words I think they know. And for the other one.... that guy is crazy. – epascarello Feb 21 '19 at 13:51
  • This question means to get original `person` object inside this `o` function, so it's not what you think. – 芝华塔尼欧 Feb 21 '19 at 14:03
  • @芝华塔尼欧 Well, you commented at JonasWilms answer, that you don't want to change, nor _add_ code. My approach uses the provided code but doesn't add any functionality. As JonasWilms suggested, the only way around is using a debugger. – NullDev Feb 21 '19 at 14:06
1

Could just toString the function, pull out the part you need, and eval it to get it as an object. This is pretty fragile though so getting it to work for different cases could be tough.

var o = function () {
   var person = {
       name: 'jonathan',
       age: 24
   }
   return {
       run: function (key) {
           return person[key]
       }
   } 
}

var person = eval('(' + o.toString().substr(30, 46) + ')')

console.log(person)
James Coyle
  • 9,922
  • 1
  • 40
  • 48
  • 3
    Disclaimer, this is a super ugly hack and using it in production code is not advisable. It might have adverse effect on your health. From your coworkers hitting you. – VLAZ Feb 21 '19 at 13:34
  • I mean it's terrible but shows you can think outside the box. Definitely not something I'd use in production. :P – James Coyle Feb 21 '19 at 13:37
  • To be fair, this interview question is pretty bad. So, as they say - ask stupid questions, get stupid answers. – VLAZ Feb 21 '19 at 13:39
  • Exactly. In what situation would you need to get this without being able to change the source code? You should never need to access something that is in a closure from outside of it. – James Coyle Feb 21 '19 at 13:41
  • you know what - I actually thought of a good reason to. Well, not the best, to be honest - it's still relatively bad all things considered but here it goes - testing. If you want to make a [spy](https://sinonjs.org/releases/latest/spies/) for unit testing purpose you might need to "break into" a closure. It's still better to 1. use a dedicated library 2. write unit-testable code, however, this is still a more legitimate reason I can think of. Everything else is related to bad architecture, bad design, bad coding, or multiple of these. – VLAZ Feb 21 '19 at 14:37
-2

o().run("name") It will be return "jonathan".

RexHuynh
  • 7
  • 1
-2

Simply you can make this

<script type="text/javascript">
 var o = function () {
  var person = {
   name: 'jonathan',
   age: 24
  }
  return {
   run: function (key) {
       return person[key]
   }
  } 
 }
let a = new o;
alert(a.run('name'));
</script>
Cb Pcbrain
  • 16
  • 7