3

I'm trying to create a read only property on a constructor function with Javascript. I've remixed the syntax I've found on MDN but this doesn't work:

function Person(data) {
  var self = this;

  self.firstName = data.firstName || "John";
  self.lastName = data.lastName || "Doe";

  get fullName() { return self.firstName + " " + self.lastName; }
}

var mike = new Person({ firstName: "Mike", lastName: "Michaelson" });

document.write(mike.fullName);

This will throw an exception:

Uncaught SyntaxError: Unexpected identifier

I've tried other options, including:

{ get fullName() { return self.firstName + " " + self.lastName; } }

And:

get this.fullName() { return self.firstName + " " + self.lastName; } 

And:

get self.fullName() { return self.firstName + " " + self.lastName; } 

But none of those options work.

This one does work:

function Person(data) {
  var self = this;

  self.firstName = data.firstName || "John";
  self.lastName = data.lastName || "Doe";
  
  Object.defineProperty(self, "fullName", { get: function () { return self.firstName + " " + self.lastName; } });
}

var mike = new Person({ firstName: "Mike", lastName: "Michaelson" });

document.write(mike.fullName);

But obviously this way with defineProperty feels clunky.

What is the correct syntax for defining get-only properties inside constructor functions?

3
  • You can either put it in the prototype (object.prototype = { get x(){ return this._x; }} - but you can only do this once when declaring the full prototype) or use defineProperty. If you are making a read-only property, you should actually use defineProperty to make it non-writeable. These are the only valid syntactical ways to do this. Commented Dec 30, 2015 at 14:55
  • @Jeroen are you looking to have read/write access for first/last name, but read-only access to fullName? If so, please re-read my answer, as I've updated it to accommodate for such a case. Commented Dec 31, 2015 at 15:42
  • @Droogans Thanks for your ideas and thoughts. You've assumed correctly about read/write acces, though I was already able to extrapolate from your original answer to those cases. I've upvoted your answer, it's useful, but it's not quite the answer to my question as your Person function is / can be no longer used as a constructor function as it breaks inheritance. - Guess I'm just spoiled with C#'s property syntax... Commented Dec 31, 2015 at 16:47

1 Answer 1

1

I'd recommend making it a POJO with options passed in all the way, but you'll still have that "clunkiness" of having Object.defineProperties, just without the ceremony of actually calling it (I like the shorthand way better, personally).

var Person = function (data) {
    if (data === undefined) {
        data = {};
    }

    data = _.defaults(data, {
        firstName: 'John',
        lastName: 'Doe'
    });

    return {
        get firstName() { return data.firstName; },
        get lastName() { return data.lastName; },
        get fullName() { return data.firstName + ' ' + data.lastName; }
    }
}

EDIT: Now that I've thought about it more, I think the reason you want to include the defineProperty syntax at all is because you want to have read/write access to the first and last name, but read-only access to the fullName. If this assumption is correct, this is a pattern that I'll typically use.

var Person = function (data) {
    if (data === undefined) {
        data = {};
    }

    data = _.defaults(data, {
        firstName: 'John',
        lastName: 'Doe'
    });

    return {
        get firstName() { return data.firstName; },
        set firstName(name) { data.firstName = name; },

        get lastName() { return data.lastName; },
        set lastName(name) { data.lastName = name; },

        get fullName() { return data.firstName + ' ' + data.lastName; }
    }
}

p = Person();
p.fullName === 'John Doe'; // true
p.firstName = 'Jane';
p.fullName === 'Jane Doe'; // true

However, there are some caveats to be aware of, mainly with object references.

data = { firstName: 'Jane' };
p = Person(data);
p.fullName === 'Jane Doe'; // true
data.lastName = 'Goodall';
p.fullName === 'Jane Doe'; // false!
console.log(p.fullName); // 'Jane Goodall'

I typically prefer having arguments passed in as something immutable, or freezing those traits by copying them out of the object and into something immutable so that updates via an object reference don't cause subtle bugs in your behaviors.

Sign up to request clarification or add additional context in comments.

5 Comments

So bascially "it's not possible to use the shorthand inside a constructor function itself"? On a side note, in your example, the prototype of returned POJOs would be Object, not Person, right?
If you run this, and ask Person.prototype, it'll say it's a Person object. You can also new it if you want. It should behave exactly as your code sample does in your question highlighted above.
(new Person()) instanceof Person; is false in combination with the Person function you posted, as opposed to combining it with the question's version of Person. I'm probably misunderstanding something though?
No you are not misunderstanding this, the returned object is actually just the {} object. The only way to allow easy definition is to use the prototype properly and add the getters and setters there or use defineProperty. You are looking for syntactic sugar that does not exist in Javascript.
@Jeroen you're right! I didn't realize that instanceof wouldn't work. That appears to be a difference between the two implementations.

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.