0

The exact same question is asked here define property in constructor, but i don't feel that the answers are satisfactory. The code is copied from the book "The Principles of Object Oriented JavaScript", with a slight modification to make the question more clear.

function Person(xname) {
    Object.defineProperty(this, "name", {
        get: function() {
            return xname;
        },
        set: function(newName) {
            name = newName;
        },
        configurable: true,
        enumerable: true
    });
    this.sayName = function() {
        console.log(this.name);
    };
}

var One = new Person("Mark");

One.sayName();
console.log(One.name);
//output:
//Mark
//Mark

I have modified parameter name to xname, because it was even more confusing the use of local variable and object property with the same name.

I doubt that this code is working as intended but it confuses me even the way that it is doing something.

  1. I understand that it is using Object.defineProperty inside the constructor to create a property on the newly created instance called "name".

  2. The getter method returns xname and yet xname goes out scope after the line: var One = new Person("Mark"); How can later in the code object's One getter manage to return the value of xname??

  3. And it is the value of xname, because if i change the getter method to return name, like it should work, the engine reports an error name is not defined. (get: function() {return name;})

  4. Not to mention that the setter method doesn't work at all. If i code One.name = "Mickey", One.name is still set to "Mark".

There is no assignment of xname to .name or there is but I don't see it?!

Community
  • 1
  • 1

1 Answers1

0

The getter method returns xname and yet xname goes out scope after the line: var One = new Person("Mark"); How can later in the code object's One getter manage to return the value of xname??

Because it is still in scope where it's used. Yes, the Person function has returned, but because there are functions that close over the context of the call to Person, the arguments and variables defined within that context are still accessible to those functions.

When you call a function, in specification terms an object is created that contains "bindings" (think of them like properties) for the arguments to the function, the local variables in the call, etc. Any function created during that call to the function maintains a reference to that object, and so the object continues to exist even after the function has returned. That's the basis of closures in JavaScript. And it's why you can keep using xname even after Person has returned from the call creating the getters and setters on that instance.

I have modified parameter name to xname, because it was even more confusing the use of local variable and object property with the same name.

You've only done that in the getter; you need to do it in both the getter and the setter for it to work properly:

function Person(xname) {
    Object.defineProperty(this, "name", {
        get: function() {
            return xname;
        },
        set: function(newName) {
            xname = newName;        // *** Here too
        },
        configurable: true,
        enumerable: true
    });
    this.sayName = function() {
        console.log(this.name);
    };
}

The Object.defineProperty stuff (in fact, even the new) is all somewhat beside the point. The fundamental thing you're seeing is how closures work, so let's look at a simpler example:

function createClosure(arg) {
  return function() {
    console.log(arg);
  };
}
var c1 = createClosure("one");
var c2 = createClosure("two");
c1(); // "one"
c2(); // "two"
c1(); // still "one"
c2(); // still "two"

As you can see, when we call the c1 or c2 function, it uses the arg argument that createClosure received when that function was created, even though createClosure has returned. c1 (and c2) each have a reference to the context in which they were created. They "close over" the arg related to the call where they were created.

More reading:

Community
  • 1
  • 1
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • OK. Thanks. Closures may work in a way that i don't know yet, the book does not mention them so far, but i –  Oct 19 '16 at 22:50
  • OK. Thanks. Closures may work in a way that i don't know yet, the book does not mention them so far, but i don't get the code snippet from the book. He is defining a property of the new Person object that is called "name". Why the setter should assign newName to xname insted of .name? What would be the same code if you don't use defineProperty? Something like this? function Person(name) { this.name = name; this.get = function() {return this.name;}; this.set = function(v) {this.name = v;}; } –  Oct 19 '16 at 23:04
  • @b'stard: If the book hasn't covered closures yet, then it's a bit mind-boggling they should be throwing the code above at you; you might consider a different book. Re `name` vs. `xname`: In the getter/setter code, the name of the argument to `Person` is completely irrelevant; it has no effect whatsoever on what the property is called. The name of the property is determined by the second argument to `defineProperty`. When you use a getter/setter, you're decoupling where you store the data (if you even do) from the property itself. The name of the arg/variable where we store it doesn't matter. – T.J. Crowder Oct 20 '16 at 06:42
  • Thank you for your patience. As a C programmer I never considered the possibility that automatic variables can exists after the function returns. Is this what it is happening: Local variables are not automatically discarded, but the garbage collector is responsible for their lifetime. As long as there is reference to a variable, the GC keeps it alive. In my example object One has a reference to "local" variable xname, so it won't be freed until object One is deleted? –  Oct 20 '16 at 22:44
  • @b'stard: That's it exactly. Conceptually, local variables (and arguments) for a call to a function are held in an object (let's call it an "environment object" -- it's properly the *EnvironmentRecord* of the *LexicalEnvironment* of the *ExecutionContext* for the call); functions created within that call have a reference to that object. If the function continues to exist after the call returns, then the environment object also continues to exist, and so the locals it contains continue to exist. The lifecycles of those objects are handled by GC. – T.J. Crowder Oct 21 '16 at 06:38
  • @b'stard: Naturally, the JavaScript engine is free to optimize where it can, but that's how it works at a specification level. – T.J. Crowder Oct 21 '16 at 06:39
  • I'd say that explains the examle from the book. –  Oct 22 '16 at 19:10