14

How can I programmatically identify getter and setter properties in ES5?

var o, descriptor, descriptorGetter, descriptorSetter;

o = { 
  foo: 'foo',
  get bar() {
    return 'bar';
  },
  set bam(value) {
    this._bam = value;
  },
};

descriptor = Object.getOwnPropertyDescriptor(o, 'foo');
descriptorGetter = Object.getOwnPropertyDescriptor(o, 'bar');
descriptorSetter = Object.getOwnPropertyDescriptor(o, 'bam');

console.log(JSON.stringify(descriptor));
console.log(JSON.stringify(descriptorGetter));
console.log(JSON.stringify(descriptorSetter));

Prints:

{"value":"foo","writable":true,"enumerable":true,"configurable":true}
{"enumerable":true,"configurable":true}
{"enumerable":true,"configurable":true}
Ben Aston
  • 53,718
  • 65
  • 205
  • 331
  • `!({a:1}).__lookupGetter__("a")` will be false since `a` not a getter. If it was the function would coerce into true, so the end result is like hasOwnProperty(), but for getters... – dandavis Jun 09 '15 at 20:59

4 Answers4

10

When you are stringifying, you will lose all the undefined and the Function objects. Instead, you can check if the returned property descriptor object has a non-undefined get or set properties and decide like this

  1. If the property descriptor has a value property, it is a normal data property.

  2. If the property descriptor has get and set properties, and both have functions as values, then it is an accessor property.

  3. If the property descriptor has get's value as a function, then it is a getter property.

  4. Otherwise, a setter property.


Since value is there, it is a normal data property:

descriptor.hasOwnProperty('value');
// true

Here, value is not there, but get property is a function. So a getter property:

descriptorGetter.hasOwnProperty('value');
// false
typeof descriptorGetter.get === 'function';
// true
typeof descriptorGetter.set === 'function';
// false

Here also, value is not there, but set property is a function. So a setter property:

descriptorSetter.hasOwnProperty('value');
// false
typeof descriptorSetter.get === 'function';
// false
typeof descriptorSetter.set === 'function';
// true

Apart from that, if you had an accessor property, like this

var o = {
    get cabbage() {
        return 'cabbage';
    },
    set cabbage(value) {
        this._cabbage = value;
    },
};

descriptorCabbage = Object.getOwnPropertyDescriptor(o, 'cabbage');

console.log(descriptorCabbage.hasOwnProperty('value'));
// false
console.log(typeof descriptorCabbage.get === 'function');
// true
console.log(typeof descriptorCabbage.set === 'function');
// true

You can write this as a function, like this

function getTypeOfProperty(object, property) {
    var desc = Object.getOwnPropertyDescriptor(object, property);

    if (desc.hasOwnProperty('value')) {
        return 'data';
    }

    if (typeof desc.get === 'function' && typeof desc.set === 'function') {
        return 'accessor';
    }

    return typeof desc.get === 'function' ? 'getter' : 'setter';
}

console.log(getTypeOfProperty(o, 'foo'));
// data
console.log(getTypeOfProperty(o, 'bar'));
// getter
console.log(getTypeOfProperty(o, 'bam'));
// setter
console.log(getTypeOfProperty(o, 'cabbage'));
// accessor
frzsombor
  • 2,274
  • 1
  • 22
  • 40
thefourtheye
  • 233,700
  • 52
  • 457
  • 497
1

You are using JSON.stringify, which is what makes this difficult to see. Getter and setters are functions, which can't be serialized as JSON, so they don't show up. I'd simply do this:

if ('value' in descriptor){
  // The descriptor is for a data property.
  // Read 'descriptor.value' in here.
} else {
  // The descriptor is for an accessor property.
  // Read 'descriptor.get' and 'descriptor.set' in here.
}
loganfsmyth
  • 156,129
  • 30
  • 331
  • 251
1

jsFiddle Demo

According to Object.getOwnPropertyDescriptor() MDN

A property descriptor is a record with some of the following attributes:

  • get
    A function which serves as a getter for the property, or undefined if there is no getter (accessor descriptors only).
  • set
    A function which serves as a setter for the property, or undefined if there is no setter (accessor descriptors only).

As a result, if you use this on a property which is a get or set function then it should have those be defined (as opposed to undefined). This can be seen with

console.log(descriptorGetter.get);//function bar()
console.log(descriptorSetter.set);//function bam(value)

enter image description here

From your shown code where .get shows the function bar() and .set shows the function bam(value).

jsFiddle Demo

A simple way to examine this in a helper function could be

function isGet(obj,prop){
 return toString.call(Object.getOwnPropertyDescriptor(obj, prop).get) == "[object Function]";    
}
function isSet(obj,prop){
 return toString.call(Object.getOwnPropertyDescriptor(obj, prop).set) == "[object Function]";    
}
Community
  • 1
  • 1
Travis J
  • 81,153
  • 41
  • 202
  • 273
0

My guess is that the setter should at least receive one parameter and the getter shouldn't use any parametter.

However probably that won't always the case. To find out how many parameters a function requires you can use this

var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
var ARGUMENT_NAMES = /([^\s,]+)/g;
function getParamNames(func) {
  var fnStr = func.toString().replace(STRIP_COMMENTS, '');
  var result = fnStr.slice(fnStr.indexOf('(')+1, fnStr.indexOf(')')).match(ARGUMENT_NAMES);
  if(result === null)
     result = [];
  return result;
}

Example usage:

getParamNames(getParamNames) // returns ['func']
getParamNames(function (a,b,c,d){}) // returns ['a','b','c','d']
getParamNames(function (a,/*b,c,*/d){}) // returns ['a','d']
getParamNames(function (){}) // returns []
Community
  • 1
  • 1
Gonzalo.-
  • 12,512
  • 5
  • 50
  • 82