【问题标题】:JavaScript equivalent of C#'s DynamicObject?JavaScript 等价于 C# 的 DynamicObject?
【发布时间】:2012-08-27 22:34:50
【问题描述】:

需要明确的是,继承 DynamicObject 的类(当然是在 C# 中)与 JavaScript 的动态变量的概念不同。 DynamicObject 允许实现者以编程方式确定对象具有哪些成员,包括方法。

编辑:我知道 JavaScript 对象可以在运行时添加任何成员。这根本不是我要说的。下面是一个 C# 示例,展示了 DynamicObject 的作用:

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".

当访问 obj 的成员时,它使用 TryGetMember 以编程方式确定该成员是否存在以及它的值是什么。简而言之,成员的存在是在请求时确定的,而不是通过事先添加来确定的。我希望这能稍微澄清一下这个问题。如果您想知道,我正在尝试确定是否可以在 JavaScript 中创建一个对象,当函数调用语法用于它时,如下所示:

myAPI.uploadSomeData(data1, data2)

uploadSomeData 调用转到类似“TryGetMember”的函数,该函数使用名称“uploadSomeData”执行 $.ajax 调用以生成 URL,返回值是结果。

【问题讨论】:

  • 除非我误解了您的问题,否则 Javascript 对象都以这种方式运行。在任何时候,您都可以在任何 Javascript 对象上创建新的属性和函数。
  • for (var property in myobject) { console.log(property); }
  • 我有一些关于 JavaScript 的相关问题,可能对你有用。
  • 我认为您正在寻找 ES-Harmony Proxies,这是(不幸的是)一个实验性 API。
  • 代码!!代码。向我们展示不起作用的代码。换种说法:你试过什么?

标签: c# javascript


【解决方案1】:

令人惊讶的是,仅仅几个星期的 JavaScript 研究后,我设法找到了两个答案:

ES6 Proxies

noSuchMethod

【讨论】:

    【解决方案2】:

    JavaScript 允许在for...in 循环中遍历对象的属性和方法。 您可以确定它是使用typeof 运算符的方法。

    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

    更新

    因此,您不应期望 JavaScript 提供 Java 或 C# 那样的大量反射工具。 您可以以某种方式模拟此类行为,但无论如何您将无法创建您的特殊 JavaScript 对象并调用其不存在的属性。唯一的方法是创建一个特殊的函数来实现开关、外观、地图等 - 任何允许使流程更加动态的结构。

    类似exmaple.

    您可以根据用户的操作、传入的数据等即时改进此逻辑并为 app.Stuff 添加其他属性和功能。因此,总体而言,您可以使用某种元编程构建智能且灵活的系统在 JavaScript 中。

    【讨论】:

      【解决方案3】:

      我堆积了类似的问题并写了这段代码:

         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);
      

      我在这里使用 Proxy 类作为基本解决方案,所以用两个词来说 - 每次您尝试访问某个属性时,如果该属性不存在,它就会创建该属性。是时候编辑了,因为我遇到了很多问题,需要解决它们,所以现在是:)

      示例:

      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();
      

      它不会导致任何错误 此外,您可以定义自己的基础对象,它不会被覆盖:

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

      工作样本:https://codesandbox.io/s/cranky-liskov-myf65

      【讨论】:

        【解决方案4】:

        编辑 1: 您可以使用 typeof 检查方法是否存在: 如果(typeof(obj.methodName)!=“未定义”){ //调用方法名 } 如果你想专门检查一个函数,它的类型将返回“function”。但是,我不知道有什么方法可以获取函数的签名,尽管如果你的函数能很好地处理空值,JavaScript 是相当宽容的。

        原始答案 我相信 Ethan Brown 有这个权利,因为所有 Javascript 对象基本上只是变量的名称-值对,有时是函数。尝试以下代码来提醒任何给定对象的所有属性和方法的列表:

        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));
        

        请注意,这与 Slashnick 在How to list the properties of a JavaScript object 中使用的过程相同

        【讨论】:

        • 抱歉,我已尽力澄清问题,以防您想再试一次。
        猜你喜欢
        • 2011-05-23
        • 2023-04-11
        • 1970-01-01
        • 1970-01-01
        • 2021-07-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-12-15
        相关资源
        最近更新 更多