Object Reflection in JavaScript by Sample

Overview

  • Javascrript has an API to use Reflection similar to  JAVA
  • You can follow the Prototypal Chain to figure out the Object Properties and Functions
  • Of course you can use Chrome Dev Tools  but if you need a programatically approach continue reading
  • The code may not be prefect – any comments/improvements are appreciated
  • checkProperties(MammalObj) is used to walk down the Prototype Chain

JavaScript Code for Object inheritance

var Mammal = {
    type: 'Mammal',
    objectName : 'Mammal BASE Object',
    getDetails: function () {
        return("Type: " + this.type + " - ObjectName:  "  + this.objectName);
    }
};

function runObjectTest9() {
    logMessage('--- Prototypal Inheritance via Object.create [ for details check JavaScript console ] ---');
    logMessage("Global Mammal Object created via String Literals:: type: " + Mammal.type + " - getDetails(): " + Mammal.getDetails());
    var MammalObj = Object.create(Mammal, {
        _objectName : { value: 'INITIAL MammelObj', writable: true},
        objectName: {
            configurable: true,
            get: function getObjectName () {
                logMessage('[MammalObj objectName get]:: ' + this._objectName );
                return this._objectName;
            },
            set: function setObjectName (value) {
                oldVal = this._objectName;
                this._objectName = value;
                logMessage("[MammalObj objectName set]:: NEW Value: " + this._objectName + " - Old Value: " + oldVal );
            }
         }
    });

    logMessage('[MammalObj.objectName]:: ' + MammalObj.objectName);
    MammalObj.objectName = "MammalObj";
    MammalObj.isWalking = false;
    logMessage("[MammalObj.getDetails()]:: " + MammalObj.getDetails());
    logMessage("[MammalObj.type]:: " + MammalObj.type);
    checkProperties(MammalObj);
    logMessage('------------------------------------------------------------------------');
}
Output: 
--- Prototypal Inheritance via Object.create [ for details check JavaScript console ] ---
Global Mammal Object created via String Literals:: type: Mammal - getDetails(): Type: Mammal - ObjectName:  Mammal BASE Object
[MammalObj objectName get]:: INITIAL MammelObj
[MammalObj.objectName]:: INITIAL MammelObj
[MammalObj objectName set]:: NEW Value: MammalObj - Old Value: INITIAL MammelObj
[MammalObj objectName get]:: MammalObj
[MammalObj.getDetails()]:: Type: Mammal - ObjectName:  MammalObj
[MammalObj.type]:: Mammal
[MammalObj objectName get]:: MammalObj

 

Implementation of Reflection Code

function checkProperties(o) {
    level =1 ;
    checkPropertiesPrototype(o,level);
}
var endOfPrototypeChainReached = false;

function checkPropertiesPrototype(oProto,level) {
        // limit the Prototype Chain depth
    if ( level === 10) {
        logMessage("Prototype Chain > 10 : termination - Check for CodeBUGS !");
        return;
    }
    /*
     * Abort walking down the Property Chain action when we reach the  Object.prototype object
     * Note:  Object.getPrototypeOf returns null when we reach the TOP Level Object.prototype object
     */
    var o = oProto;
    var prefix = getPrefix(level*2) + " [" + o.objectName + ", level:" + level + "]";
    var oProtoNext = Object.getPrototypeOf(oProto); 
    if ( !oProtoNext) {
            // o.objectName is undefined for  Object.prototype  - Fix this
        prefix = getPrefix(level*2) + " [Object.prototype, level:" + level + "]";
           // logMessage(prefix + " End of protoype Chain reached: Level: " + level);
           // Mark that we have reached the END of our Prototype Chain
        endOfPrototypeChainReached = true;
        return;
       }     
    logMessage("--- Detailed ProtoType Function walking down the Prototype Chain - Level:  " + level + '---');
    Object.getOwnPropertyNames(o).forEach(function(val) 
    {
        var objRef =  o[val];
        var foundIt = "";
        var p =   Object.getPrototypeOf(objRef);
        /*
            JavaScript has 5 primitives: Number, String, Boolean ,Undefined, Null [  Anything else is considered an object ]
            Object.getPrototypeOf(objRef) returns NULL for Primitives like  Number, String, Boolean ,Undefined, Null
        */
        var foundFunc = false;
        if ( typeof(objRef) === 'function') {
            foundIt = "[Found a FUNCTION]";
            foundFunc = true;
        }
        else if ( typeof(objRef) === 'object') {
            foundIt = "[Found an OBJECT]";
        }

        /*
        Object Properties:
            Data Descriptor     : configurable, enumerable, value, writable
            Accessor descriptor : configurable, enumerable, set, get
        */
        var propRes = "Oops- check getOwnPropertyDescriptor Code";
        d = Object.getOwnPropertyDescriptor(o, val);
        if ( d ) {
            if ( !d.get  || !d.set ) {
                propRes = " - Data Descriptor: Configurable: " +  d.configurable + " - Enumerable: " + d.enumerable +
                    " - Writable: " + d.writable + " - Value: " + d.value;
            }
            else {
                propRes = " - Accessor Descriptor: Configurable " +  d.configurable + " - Enumerable " + d.enumerable +
                    " - Set: " + d.set + " - Get: " +  d.get;
            }
        }
        if (foundFunc) {
            logMessage(prefix + foundIt + " - Function Name: " + val + " - Type: " + typeof(objRef)
                + " - getPrototypeOf: " + p  + " - Constructor: " + objRef.constructor + "\n" + prefix +  propRes);
        }
        else {
            logMessage(prefix + foundIt + " - Property Name: " + val + " - Value: " + objRef + " - Type: " + typeof(objRef)
                + " - getPrototypeOf: " + p  + " - Constructor: " + objRef.constructor + "\n" + prefix +  propRes);
        }
        
        var oProto = Object.getPrototypeOf(o);
            // Dont walk down the Prototype Chain more than ONCE
        if ( !endOfPrototypeChainReached)
            checkPropertiesPrototype(oProto,level+1);
    });
   level++;
}

function getPrefix(len)
{
var eStr = '';
while( eStr.length < len )
    {
        eStr = eStr + ' ';
    }        
return eStr;
}

 

Output from Reflection Script

--- Detailed ProtoType Function walking down the Prototype Chain - Level:  1---

   [MammalObj, level:1] - Property Name: _objectName - Value: MammalObj - Type: string - getPrototypeOf:  - Constructor: function String() { [native code] }
   [MammalObj, level:1] - Data Descriptor.: Configurable: false - Enumerable: false - Writable: true - Value: MammalObj

--- Detailed ProtoType Function walking down the Prototype Chain - Level:  2---

     [Mammal BASE Object, level:2] - Property Name: type - Value: Mammal - Type: string - getPrototypeOf:  - Constructor: function String() { [native code] }
     [Mammal BASE Object, level:2] - Data Descriptor.: Configurable: true - Enumerable: true - Writable: true - Value: Mammal

     [Mammal BASE Object, level:2] - Property Name: objectName - Value: Mammal BASE Object - Type: string - getPrototypeOf:  - Constructor: function String() { [native code] }
     [Mammal BASE Object, level:2] - Data Descriptor.: Configurable: true - Enumerable: true - Writable: true - Value: Mammal BASE Object

     [Mammal BASE Object, level:2][Found a FUNCTION] - Function Name: getDetails - Type: function - getPrototypeOf: function () {} - Constructor: function Function() { [native code] }
     [Mammal BASE Object, level:2] - Data Descriptor.: Configurable: true - Enumerable: true - Writable: true - Value: function () {
                                     return("Type: " + this.type + " - ObjectName:  "  + this.objectName);  }
                                     
   [MammalObj, level:1] - Property Name: objectName - Value: MammalObj - Type: string - getPrototypeOf:  - Constructor: function String() { [native code] }
   [MammalObj, level:1] - Accessor Descriptor: Configurable true - Enumerable false - Set: function setObjectName(value) {
                oldVal = this._objectName;
                this._objectName = value;
                logMessage("[MammalObj objectName set]:: NEW Value: " + this._objectName + " - Old Value: " + oldVal );
            } - Get: function getObjectName() {
                logMessage('[MammalObj objectName get]:: ' + this._objectName );
                return this._objectName;
            }
   [MammalObj, level:1] - Property Name: isWalking - Value: false - Type: boolean - getPrototypeOf: false - Constructor: function Boolean() { [native code] }
   [MammalObj, level:1] - Data Descriptor.: Configurable: true - Enumerable: true - Writable: true - Value: false

Leave a Reply

Your email address will not be published. Required fields are marked *