Getters \ setters for dummies

127,675

Solution 1

In addition to @millimoose's answer, setters can also be used to update other values.

function Name(first, last) {
    this.first = first;
    this.last = last;
}

Name.prototype = {
    get fullName() {
        return this.first + " " + this.last;
    },

    set fullName(name) {
        var names = name.split(" ");
        this.first = names[0];
        this.last = names[1];
    }
};

Now, you can set fullName, and first and last will be updated and vice versa.

n = new Name('Claude', 'Monet')
n.first # "Claude"
n.last # "Monet"
n.fullName # "Claude Monet"
n.fullName = "Gustav Klimt"
n.first # "Gustav"
n.last # "Klimt"

Solution 2

Getters and Setters in JavaScript

Overview

Getters and setters in JavaScript are used for defining computed properties, or accessors. A computed property is one that uses a function to get or set an object value. The basic theory is doing something like this:

var user = { /* ... object with getters and setters ... */ };
user.phone = '+1 (123) 456-7890'; // updates a database
console.log( user.areaCode ); // displays '123'
console.log( user.area ); // displays 'Anytown, USA'

This is useful for automatically doing things behind-the-scenes when a property is accessed, like keeping numbers in range, reformatting strings, triggering value-has-changed events, updating relational data, providing access to private properties, and more.

The examples below show the basic syntax, though they simply get and set the internal object value without doing anything special. In real-world cases you would modify the input and/or output value to suit your needs, as noted above.

get/set Keywords

ECMAScript 5 supports get and set keywords for defining computed properties. They work with all modern browsers except IE 8 and below.

var foo = {
    bar : 123,
    get bar(){ return bar; },
    set bar( value ){ this.bar = value; }
};
foo.bar = 456;
var gaz = foo.bar;

Custom Getters and Setters

get and set aren't reserved words, so they can be overloaded to create your own custom, cross-browser computed property functions. This will work in any browser.

var foo = {
    _bar : 123,
    get : function( name ){ return this[ '_' + name ]; },
    set : function( name, value ){ this[ '_' + name ] = value; }
};
foo.set( 'bar', 456 );
var gaz = foo.get( 'bar' );

Or for a more compact approach, a single function may be used.

var foo = {
    _bar : 123,
    value : function( name /*, value */ ){
        if( arguments.length < 2 ){ return this[ '_' + name ]; }
        this[ '_' + name ] = value;
    }
};
foo.value( 'bar', 456 );
var gaz = foo.value( 'bar' );

Avoid doing something like this, which can lead to code bloat.

var foo = {
    _a : 123, _b : 456, _c : 789,
    getA : function(){ return this._a; },
    getB : ..., getC : ..., setA : ..., setB : ..., setC : ...
};

For the above examples, the internal property names are abstracted with an underscore in order to discourage users from simply doing foo.bar vs. foo.get( 'bar' ) and getting an "uncooked" value. You can use conditional code to do different things depending on the name of the property being accessed (via the name parameter).

Object.defineProperty()

Using Object.defineProperty() is another way to add getters and setters, and can be used on objects after they're defined. It can also be used to set configurable and enumerable behaviors. This syntax also works with IE 8, but unfortunately only on DOM objects.

var foo = { _bar : 123 };
Object.defineProperty( foo, 'bar', {
    get : function(){ return this._bar; },
    set : function( value ){ this._bar = value; }
} );
foo.bar = 456;
var gaz = foo.bar;

__defineGetter__()

Finally, __defineGetter__() is another option. It's deprecated, but still widely used around the web and thus unlikely to disappear anytime soon. It works on all browsers except IE 10 and below. Though the other options also work well on non-IE, so this one isn't that useful.

var foo = { _bar : 123; }
foo.__defineGetter__( 'bar', function(){ return this._bar; } );
foo.__defineSetter__( 'bar', function( value ){ this._bar = value; } );

Also worth noting is that in the latter examples, the internal names must be different than the accessor names to avoid recursion (ie, foo.bar calling foo.get(bar) calling foo.bar calling foo.get(bar)...).

See Also

MDN get, set, Object.defineProperty(), __defineGetter__(), __defineSetter__()
MSDN IE8 Getter Support

Solution 3

You'd use them for instance to implement computed properties.

For example:

function Circle(radius) {
    this.radius = radius;
}

Object.defineProperty(Circle.prototype, 'circumference', {
    get: function() { return 2*Math.PI*this.radius; }
});

Object.defineProperty(Circle.prototype, 'area', {
    get: function() { return Math.PI*this.radius*this.radius; }
});

c = new Circle(10);
console.log(c.area); // Should output 314.159
console.log(c.circumference); // Should output 62.832

(CodePen)

Solution 4

Sorry to resurrect an old question, but I thought I might contribute a couple of very basic examples and for-dummies explanations. None of the other answers posted thusfar illustrate syntax like the MDN guide's first example, which is about as basic as one can get.

Getter:

var settings = {
    firstname: 'John',
    lastname: 'Smith',
    get fullname() { return this.firstname + ' ' + this.lastname; }
};

console.log(settings.fullname);

... will log John Smith, of course. A getter behaves like a variable object property, but offers the flexibility of a function to calculate its returned value on the fly. It's basically a fancy way to create a function that doesn't require () when calling.

Setter:

var address = {
    set raw(what) {
        var loc = what.split(/\s*;\s*/),
        area = loc[1].split(/,?\s+(\w{2})\s+(?=\d{5})/);

        this.street = loc[0];
        this.city = area[0];
        this.state = area[1];
        this.zip = area[2];
    }
};

address.raw = '123 Lexington Ave; New York NY  10001';
console.log(address.city);

... will log New York to the console. Like getters, setters are called with the same syntax as setting an object property's value, but are yet another fancy way to call a function without ().

See this jsfiddle for a more thorough, perhaps more practical example. Passing values into the object's setter triggers the creation or population of other object items. Specifically, in the jsfiddle example, passing an array of numbers prompts the setter to calculate mean, median, mode, and range; then sets object properties for each result.

Solution 5

Getters and setters really only make sense when you have private properties of classes. Since Javascript doesn't really have private class properties as you would normally think of from Object Oriented Languages, it can be hard to understand. Here is one example of a private counter object. The nice thing about this object is that the internal variable "count" cannot be accessed from outside the object.

var counter = function() {
    var count = 0;

    this.inc = function() {
        count++;
    };

    this.getCount = function() {
        return count;
    };
};

var i = new Counter();
i.inc();
i.inc();
// writes "2" to the document
document.write( i.getCount());

If you are still confused, take a look at Crockford's article on Private Members in Javascript.

Share:
127,675
Admin
Author by

Admin

Updated on September 10, 2021

Comments

  • Admin
    Admin almost 3 years

    I've been trying to get my head around getters and setters and its not sinking in. I've read JavaScript Getters and Setters and Defining Getters and Setters and just not getting it.

    Can someone clearly state:

    1. What a getter and setter are meant to do, and
    2. Give some VERY simple examples?
  • Admin
    Admin about 15 years
    Ok, I think I'm starting to get it. I'm trying to assign a getter to the length property of an array object but getting an error: "Redeclaration of var length" And the code looks like this: obj = []; obj.__defineGetter__('length',function(){ return this.length; });
  • Montre
    Montre about 15 years
    That's because Array objects already have a builtin length property. If the redeclaration was allowed, calling the new length would recurse infinitely. Try calling the property "my_length" or some such.
  • Matthew Crumley
    Matthew Crumley over 13 years
    @Akash: No, although, Internet Explorer 9 does support the newer Object.defineProperty function that can define getters and setters.
  • Akash Kava
    Akash Kava over 13 years
    Isnt it really painful that MS does not support JS correctly and they do not make their silverlight run everywhere, so I have to program everything twice, one for SL and one for rest of the world :)
  • devios1
    devios1 about 13 years
    I disagree. Getters and setters are also very useful for encapsulating information whose definition may not just be a simple variable. It can be handy if you need to change the behavior of a system that previously used simple properties and which other things may depend on. Furthermore, your example only demonstrates "pseudo getters" which are just functions. Real JavaScript getters appear as simple values (and are accessed without function notation), hence the real power of them.
  • Martin Konicek
    Martin Konicek almost 13 years
    How do you make first and last private?
  • Matthew Crumley
    Matthew Crumley almost 13 years
    @Martin: You could make them private by using the same technique as in John's answer. If you want to use real getters/setters, you would have to use this.__defineGetter__ or the newer Object.defineProperty function.
  • xchg.ca
    xchg.ca almost 13 years
    Only one problem with approach listed above, if you want to add getters and setters for already existing class it will override prototypes, and original methods will not be accessible.
  • Michael Scott Asato Cuthbert
    Michael Scott Asato Cuthbert almost 11 years
    the point was to be able to change an attribute to something fancier without needing to change the public interface. Adding a call () tag changes it.
  • r0estir0bbe
    r0estir0bbe about 8 years
    Doesn't this approach overwrite Name.prototype.constructor? Seems like a bad alternative to millimoose's Answer.
  • r0estir0bbe
    r0estir0bbe about 8 years
    In order to define both getters in one statement, use Object.defineProperties.
  • AgmLauncher
    AgmLauncher almost 8 years
    Not sure if I would call that powerful. Something appearing as X but is really Y is not necessarily clear. I would absolutely NOT expect var baz = foo.bar to have a full blown set of hidden behavior behind it. I would expect that from foo.getBar(), however.
  • Jessica
    Jessica almost 8 years
    @MatthewCrumley Is this es6 syntax?
  • Matthew Crumley
    Matthew Crumley almost 8 years
    @Jessica Yes, although it was actually introduced in ES5.
  • Andreas
    Andreas over 7 years
    I still don't understand the benefit of using get and set vs creating a getMethod or setMethod. Is the only benefit that you can call it without () ? There must be another reason it's added to javascript.
  • rojo
    rojo over 7 years
    @Andreas Getters and setters behave like properties when called, which can help articulate their intended purpose. They don't unlock otherwise missing abilities, but their use can help you organize your thoughts. That's the real benefit. As a practical example, I used to use a getter to extend a Google Maps object. I needed to calculate the camera roll angle so I could rotate map tiles flat to the horizon. Google does this automatically on the back end now; but at the time it was helpful to me to retrieve maps.roll as a property rather than maps.roll()'s return val. It's just a preference.
  • Andreas
    Andreas over 7 years
    so it's just syntactic sugar to make code look cleaner without the (). I can't see why you couldn't your example with maps.roll()
  • rojo
    rojo over 7 years
    @Andreas Who says I couldn't? Like I say, it's just a way to help me keep my thoughts organized. Coding is an art. You don't ask Bob Ross why he had to use burnt ochre when he could've used orange. You may not see a need now, but one day when you decide your painting needs a little burnt ochre, it'll be on your palette.
  • Andreas
    Andreas over 7 years
    :) one thing that i see that get and set syntax does, is being auto-run if it's used as a property of a property.
  • dystopiandev
    dystopiandev over 7 years
    In the more compact approach, this[ '_' + name ] = value; could be this[ '_' + name ] = arguments[1]; and there would be no need to specify value arg.
  • uzay95
    uzay95 over 6 years
    This is just creating new getName, and setName methods. These are not related to create property!
  • nevf
    nevf about 6 years
    The example: var foo = { bar : 123, get bar(){ return bar; }, set bar( value ){ this.bar = value; } }; foo.bar = 456; Raises an exception: Uncaught RangeError: Maximum call stack size exceeded at Object.set bar [as bar] (<anonymous>:4:32) at Object.set bar [as bar] (<anonymous>:4:32) at Object.set bar [as bar] (<anonymous>:4:32) at Object.set bar [as bar] (<anonymous>:4:32) at Object.set bar [as bar] (<anonymous>:4:32) at Object.set bar [as bar] (<anonymous>:4:32)
  • nevf
    nevf about 6 years
    The set/get name must be different to the property name. So instead of bar: 123 and this.bar = value etc. change these to _bar for example. See: hongkiat.com/blog/getters-setters-javascript
  • RegarBoy
    RegarBoy almost 6 years
    Can't you just make { "area": function () {return ...} } ? simply assign it as an object property
  • Montre
    Montre almost 6 years
    @developer That’s not a Javascript getter as defined by the language, that’s just a function. You have to call it to get the value, it doesn’t overload access to the property. Also there’s a special circle of hell reserved for people who invent their own broken object systems in JS instead of building on the one it already has.
  • Beejor
    Beejor over 4 years
    @nevf Thanks for the correction! Yes, typically with computed properties the "real" internal one is named like _foo or mFoo. If it's the same as the getter/setter, it will cause an infinite loop due to recursion and then a Stack Overflow™ ;-) because when you say a = b, it calls a.get(b) which itself calls a = b, which calls a.get(b), ...
  • Stephan Luis
    Stephan Luis almost 3 years
    Aren't backing fields required to avoid an stack overflow/ infinite loop?
  • Shani Kehati
    Shani Kehati almost 3 years
    They might be -- I haven't really tried using a setter before. I'll update my answer and address that issue