That code won't even run. When test.constructor(); is invoked, test[sibling] =... is encountered, the JS engine will attempt to resolve what the sibling variable evaluates to, however sibling is not defined anywhere and an error is thrown.
But, to your specific question about how variables are resolved. It can be easy to understand, provided you understand lexical scope. In a nutshell, think of a function as a "bubble". Functions can be nested inside other functions, which creates bubbles inside of other bubbles. A function always has access to its own variables and the variables of its parent bubbles, but not sibling bubbles.
Whenever a function is nested inside another, a closure is formed. The child function "closes" around the parent's variables. The real trick is what happens when a child bubble (function) persists longer than its parent function. Normally, a function's variables are garbage collected when the function completes, but if, during the course of the parent function executing, a child function is defined and somehow returned from the parent (via a return statement or by assignment as a callback to another object, etc.), the child function will need those parent function variables at some future point in time, meaning that the parent's variables won't be garbage collected when the parent function completes. This is the nature of closures.