Thursday, March 28, 2024

Object Reflection in JavaScript

Object Reflection in JavaScript

The Java language has a really great feature called reflection. It provides a mechanism for a Java class to self-inspect and manipulate its member attributes and methods. Using reflection, it’s possible for a Java class to query another class for the names of all its members and display them. The ability to examine and manipulate a Java class from within itself may not sound like such a feat, but until Java, there was no such feature in programming languages. Since then, other languages, such as PHP and .NET have implemented their own Reflection APIs. JavaScript, Java’s little cousin, does provide some – albeit limited – amount of Reflective functionality. In today’s article, we’re going to see exactly what it can do.

 

Uses for Reflection

Sometimes, there simply is no way of knowing what you’d like to do until you get there. In such a situation, it is highly advantageous to be able to query an object and get an interface of its attributes and services. Think of it as ordering from a menu. Traditionally, reflection was/is used to load modules that are listed an assembly manifest or XML file, locate a class from this assembly, and create an instance of it. Without reflection, such a process would take many more lines of code. It could also be used to check for public property access modifiers.

Closer to home, it’s easy to envision some uses for reflection in the realm of Web transactions. There is a compelling example on the codeutopia.net site that uses PHP reflection to get the list of methods in the form model via each field’s public getter accessor:

//create the reflection class
$cls = new ReflectionClass('NewsPost');
//use reflection to get all the NewPost class's methods
$methods = $cls->getMethods();

//loop through the methods and set the field names based on
//the getters - aka, get[FieldName]
$fields = array();
foreach($methods as $method) {
  if(substr($method->getName(), 0, 3) == 'get') {
    //Just take everything after get as the field name
    $fields[] = substr($method->getName(), 3);
  }
}

The above field names can then be used to generate a form with minimal HTML coding:

<?php foreach($fields as $field): ?>
  <p>
    <label for="<?php echo $field; ?>"><?php echo $field; ?></label>
    <input type="text" name="<?php echo $field; ?>" id="<?php echo $field; ?>" />
  </p>
<?php endforeach; ?>

The lesson here is that having access to reflection makes a program far more dynamic than it otherwise could be.

 

Getting an Object’s Public Attributes

You may notice that I use the terms class and object interchangeably. There is a fundamental difference between Java and JavaScript in that Java is made up of classes whereas JavaScript is made up of objects; there really are no classes as such. Having said that, in trying to emulate a Java feature, it helps to think of a JavaScript object as a class of sorts; it just doesn’t automatically incorporate all the natural traits of true Object-Oriented classes, such as encapsulation.

Here is a JavaScript object that you could think of as a Person class. It has two internal (private-ish) properties, one getter and setter, two public properties, as well as two public methods:

var Person = function() { 
    //defaults
    var _age  =  0,
        _name = 'John Doe';

    this.initialize = function(name, age) {
      _name = _name || name;
      _age  = _age  || age;
    };
    
    if (arguments.length) this.init();
    
    //public properties. no accessors required
    this.phoneNumber = '555-224-5555';
    this.address     = '22 Acacia ave. London, England';
    
    //getters and setters
    this.getName     = function()      { return _name; };
    this.setName     = function (name) { _name = name; };
    
    //public methods
    this.addBirthday = function()      { _age++; };
    this.toString    = function()      { return 'My name is "+_name+" and I am "_age+" years old.'; };
}; 

//create an instance of a person
var rob = new Person('Rob', 29); //still 29! (I wish!)

The Reflector class takes a class instance as constructor argument (you can’t use the Person class template – it does not contain any properties because it’s a function and not an object). After that, you can call the Reflector’s methods, or rather, method at this point.

var Reflector = function(obj) {
  this.getProperties = function() {
    var properties = [];
    for (var prop in obj) {
      if (typeof obj[prop] != 'function') {
        properties.push(prop);
      }
    }
    return properties;
  };
}

var reflector = new Reflector(rob);
document.write('<p>Person class properties:</p>');
document.write(reflector.getProperties().join('<br/>'));

The above code produces the following:

Person class properties:

phoneNumber
address

 

Getting All the Public Methods of a Class

By “all methods”, I am including overridden methods, as well as the Person class’s own methods. That process is almost identical to the previous one, except that this time we want members who’s type is “function”:

this.getAllMethods = function() {
  var methods = [];
  for (var method in obj) {
    if (typeof obj[method] == 'function') {
      methods.push(method);
    }
  }
  return methods;
};

//...
document.write('<p>Public Methods:</p>');
document.write(reflector.getAllMethods().join('<br/>'));

Here’s what that produces:

Public Methods:

initialize
getName
setName
addBirthday
toString

 

Getting an Object’s Own Public Properties

The Object.hasOwnProperty() method returns true if a method was created by the object which houses it, and is not overridden from an ancestor. Including it in the if statement will thusly weed out all inherited methods:

this.getOwnMethods = function() {
  var methods = [];
  for (var method in obj) {
    if (  typeof obj[method] == 'function'
       && obj.hasOwnProperty(method)) {
      methods.push(method);
    }
  }
  return methods;
};


//...
document.write('<p>Own Public Methods:</p>');
document.write(reflector.getOwnMethods().join('<br/>'));

This time, the overridden toString() method is absent from the list:

Own Public Methods:

initialize
getName
setName
addBirthday

Here is today’s demo file. It includes the additional fix for the JScript “DontEnum” bug that affects IE 8.

 

Conclusion

In an upcoming article, we’ll see how to glean more information such as method arguments, types and of course, how to call a method once we find one that we want to utilize.

 

Robert Gravelle
Robert Gravelle
Rob Gravelle resides in Ottawa, Canada, and has been an IT guru for over 20 years. In that time, Rob has built systems for intelligence-related organizations such as Canada Border Services and various commercial businesses. In his spare time, Rob has become an accomplished music artist with several CDs and digital releases to his credit.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Popular Articles

Featured