5

Just to be clear, a class that inherits DynamicObject (in C# of course) is not the same concept as JavaScript's variables being dynamic. DynamicObject allows the implementer to programmatically determine what members an object has, including methods.

Edit: I understand that JavaScript objects can have any members added to them at run time. That's not at all what I'm talking about. Here's a C# example showing what DynamicObject does:

public class SampleObject : DynamicObject
{
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = binder.Name;
        return true;
    }
}

dynamic obj = new SampleObject();
Console.WriteLine(obj.SampleProperty);
//Prints "SampleProperty".

When a member of obj is accessed, it uses the TryGetMember to programmatically determine whether the member exists and what its value is. In short, the existence of a member is determined when it's requested, not by adding it before hand. I hope this clarifies the question a little. In case you're wondering, I'm trying to determine if it's possible to make an object in JavaScript, that when the function call syntax is used on it like so:

myAPI.uploadSomeData(data1, data2)

The uploadSomeData call goes to a "TryGetMember" like function, which performs an $.ajax call using the name "uploadSomeData" to generate the URL, and the return value is the result.

ThinkingStiff
  • 64,767
  • 30
  • 146
  • 239
Brent
  • 4,153
  • 4
  • 30
  • 63
  • 12
    Unless I misunderstand your question, Javascript objects all act this way. At any time, you can create new properties and functions on any Javascript object. – Ethan Brown Aug 27 '12 at 22:36
  • 2
    `for (var property in myobject) { console.log(property); }` – Andreas Aug 27 '12 at 22:37
  • 1
    I have some related questions regarding JavaScript which may be of some use to you. – Matthew Layton Aug 27 '12 at 22:38
  • I think you're looking for [ES-Harmony Proxies](http://wiki.ecmascript.org/doku.php?id=harmony:direct_proxies), which is (unfortunately) an experimental API. – Mattias Buelens Aug 27 '12 at 22:39
  • code!! code. show us the code that doesn't work. Alternatively phrased: what have you tried? – Dan Davies Brackett Aug 27 '12 at 23:25
  • I've edited the question with a bit more information. – Brent Aug 28 '12 at 02:03
  • Your particular JavaScript task still is not clear. Can you clarify it better? The sample use case, requirements, expected behavior. – Eugene Naydenov Aug 28 '12 at 03:41
  • I'm certain that if one takes a moment to study an [example of C#'s DynamicObject](http://blogs.msdn.com/b/csharpfaq/archive/2009/10/19/dynamic-in-c-4-0-creating-wrappers-with-dynamicobject.aspx), that the idea is relatively simple. I've explained as much as I can without writing a blog post. I'm going to have to assume that there is no equivalent in JavaScript. – Brent Aug 28 '12 at 05:40

4 Answers4

2

Amazing the after only a few weeks of occasional JavaScript studies I've managed to find two answers:

ES6 Proxies

noSuchMethod

Brent
  • 4,153
  • 4
  • 30
  • 63
1

JavaScript allows to iterate through object's properties and methods in for...in loop. You can determine is it a method using typeof operator.

HTML:

<ul id="console"></ul>​

JavaScript:

var Application = (function(app) {
    app.constants = app.constants || {};
    app.constants.PRINT_OBJ_TITLE = '[Printing an object]';

    app.console = {
        constants: {
            SEPARATOR: new Array(50).join('-')
        },
        output: document.getElementById('console'),
        log: function() {
            var li = document.createElement('li');
            li.innerHTML = Array.prototype.join.call(arguments, '');
            this.output.appendChild(li);
        },
        printSeparator: function() {
            this.log(this.constants.SEPARATOR);
        }
    };

    app.printObj = function(obj) {
        this.console.log(app.constants.PRINT_OBJ_TITLE);
        for(var prop in obj) {
            if(obj.hasOwnProperty(prop)) {
                var propType = (typeof obj[prop] == 'function')
                    ? 'function'
                    : 'property';
                this.console.log(propType, ': ', prop);
            }
        }
        this.console.printSeparator();
    };

    return app;
})(Application || {});


var obj = {},
    app = Application;

obj.foo = function() {
    alert("I'm a foo function!");
};

obj.bar = "I'm just a property";

app.printObj(obj);​

DEMO

UPDATE

So you should not expect from JavaScript such a lot of reflection tools as Java or C# have. You can emulate such behavior in some way, but anyway you'll not be able to just create your special JavaScript object and call its non-existing property. The only way is to create a special functions which will implement switches, facades, maps, etc. - any constructions that allows to make the flow more dynamic.

Something like this exmaple.

You can improve this logic and add additional properties and functions to that app.Stuff on-the-fly depending to user's actions, incoming data, etc. So overall you can build smart and flexible system using some kind of meta-programming in JavaScript.

Eugene Naydenov
  • 7,165
  • 2
  • 25
  • 43
1

I stacked with similar question and wrote this code :

   const dynamicFunc = (input) => {
        const handler = {
            get: (obj, prop) => {

                // edge case when .toString() is calling for dynamic prop - just return any string
                if (typeof prop === 'symbol') {
                    return () => "custom_dynamic_str";
                }

                const isPropExist = typeof obj[prop] !== 'undefined';

                if (isPropExist) {
                    const val = obj[prop];

                // if null or undefined was set
                if (val == null || typeof val === 'undefined') {
                     return val;
                }

                // if value was created not by this method - return value
                if (!val.__isDynamic) {
                    return val;
                }

                return dynamicFunc(val);

          }
                obj[prop] = () => dynamicFunc({});
                obj[prop].__isDynamic = true;
                return dynamicFunc(obj[prop]);
            },
            set(target, prop, value) {
                //  if our custom function was set to dynamic
                if (typeof value === 'function') {
                    // wrap it to unwrap in get
                    target[prop] =
                        {
                            __isSetAsFunction: true,
                            func: value
                        };
                }
                else {
                    target[prop] = value;
                }
                return true;
            },
            apply: function (target, thisArg, argumentsList) {
                return dynamicFunc({});
            }
        };

        let proxy = new Proxy(input, handler);
        return proxy;
    };
    return dynamicFunc(baseObj);

i use Proxy class as a base solution here, so in two words - every time you are trying to access some property it creates this property if it's not already there. it's time to time edited, because i stacked with issues and it's was need to solve them, so as is :)

Sample:

let ob = dynamic();
ob.test1.test2.test3('hello from Belarus!','qwerty').test5.t('test');

//we could assign properties on the fly : 
ob.myProp.pyProp2 = 2;

console.log(ob.myProp.pyProp2) // "1" will be outputed

// some tests using chai and chai-spies: 
    dynamic().genericProperties.anyFunc().myprop.test();
    let obj = dynamic({ predefinedObjValue: 3 });
    obj.prop1.prop2.test = 1;
    obj.prop1.prop3.test = 2;
    obj.prop2.myfunc = () => { };

    expect(obj.prop1.prop2.test).to.be.equal(1);
    expect(obj.prop1.prop3.test).to.be.equal(2);
    expect(obj.predefinedObjValue).to.be.equal(3);

    const myFuncSpy = chai.spy.on(obj.prop2, 'myfunc');
    obj.prop2.myfunc();
    expect(myFuncSpy).to.have.been.called();

and it will no cause any errors also, you can define your own base object and it will not be overrided :

let ob = dynamic({mycustomProp : 1});
ob.test1.test2.test3('asdasd','tt').test5.t('test'); //without error

console.log(ob.mycustomProp) // "1" will be outputed    

Working sample : https://codesandbox.io/s/cranky-liskov-myf65

Nigrimmist
  • 10,289
  • 4
  • 52
  • 53
0

Edit 1: You can check the existence of a method by using typeof: If(typeof(obj.methodName)!= "undefined"){ //call methodName } If you want to specifically check for a function, its type will return "function". however, I don't know of any way to get the signature of the function, though JavaScript is fairly forgiving if your function handles nulls well.

Original answer I believe Ethan Brown had this right, in that all Javascript objects are basically just name-value pairs of variables, which are sometimes functions. Try the following code to alert a list of all properties and methods for any given object:

function obj(prop){
    this.prop = prop;
    this.setProp = function setProp(prop){this.prop = prop};
                                         }
        var myObj = new obj("something");
    myObj.setProp("anything else");
var getKeys = function(obj){
    var keys = [];
   for(var key in obj){
      keys.push(key);
   }
   return keys;
}
alert(getKeys(myObj));

Note that this is the same procedure used in How to list the properties of a JavaScript object by Slashnick

Community
  • 1
  • 1
MaxPRafferty
  • 4,819
  • 4
  • 32
  • 39
  • Sorry, I've done my best to clarify the question, in case you'd like to give it another go. – Brent Aug 28 '12 at 02:09