【问题标题】:Access namespaced javascript object by string name without using eval在不使用 eval 的情况下通过字符串名称访问命名空间的 javascript 对象
【发布时间】:2011-06-26 06:48:50
【问题描述】:

我遇到了需要从服务器访问 javascript 对象的情况。服务器返回函数或对象的字符串名称,并根据其他元数据,我将对对象进行不同的评估。

最初我正在评估 (eval([string])),一切都很好。最近我正在更新函数以不使用eval 以确保安全,但我遇到了命名空间对象/函数的问题。

具体来说,我尝试将eval([name]) 替换为window[name],以通过全局对象与eval 中的方括号语法访问对象。

但是我遇到了命名空间对象的问题,例如:

var strObjName = 'namespace.serviceArea.function';
// if I do
var obj = eval(strObjName); // works

// but if I do
var obj = window[strObjName]; // doesn't work

谁能想出一个好的解决方案来避免将eval 与命名空间字符串一起使用?

【问题讨论】:

    标签: javascript namespaces eval


    【解决方案1】:

    您可以拆分. 并依次解析每个属性。只要字符串中的任何属性名称都不包含. 字符,这就可以了:

    var strObjName = 'namespace.serviceArea.function';
    var parts = strObjName.split(".");
    for (var i = 0, len = parts.length, obj = window; i < len; ++i) {
        obj = obj[parts[i]];
    }
    alert(obj);
    

    【讨论】:

    • @Tim 这就是我的想法,但你在我之前就明白了! :)
    • ¬¬ 你先得到它? :(哈哈+1
    • @TimDown 提到使用这样的命名空间并没有真正得到原生支持,并且可能导致代码变得非常混乱且难以维护。当有更好的选择时,感觉就像有人将样式从其他语言移植到 JavaScript。
    • @JCOC611: SO 提高了我的打字速度:)
    • @Raynos:我认为您的评论已经足够了。我确实同意深度嵌套的“命名空间”对象在 JS 中不是一个好主意,但我不确定两个级别是否一定如此糟糕,尤其是在不知道上下文的情况下。
    【解决方案2】:

    只是想我会分享这个,因为我前几天做了这个。我什至没有意识到在 JS 中可以使用 reduce!

    function getByNameSpace(namespace, obj) {
        return namespace.split('.').reduce(function(a,b) {
                if(typeof a == 'object') return a[b];
                else return obj[a][b]; 
        });
    }
    

    希望有人觉得这很有用..

    【讨论】:

    【解决方案3】:

    我想出了这个:

    function reval(str){
       var str=str.split("."),obj=window;
       for(var z=0;z<str.length;z++){
         obj=obj[str[z]];
       }
       return obj;
    }
    eval("window.this.that");
    reval("this.that"); //would return the object
    reval("this.that")(); //Would execute
    

    【讨论】:

      【解决方案4】:

      我使用递归编写了一个不同的解决方案。我正在使用它来命名输入元素的名称属性。举个例子:

      <input type="number" name="testGroup.person.age" />
      

      假设我们将该输入的值设置为“25”。我们的代码中还有一个对象:

      var data = {
          testGroup: {
              person: {
                  name: null,
                  age: null,
                  salary: null
              }
          }
      };
      

      所以这里的目标是自动将输入元素中的值放入该数据结构中的正确位置。

      我编写了这个小函数来根据您传递给它的命名空间数组创建一个嵌套对象:

      var buildObject = function ( obj, namespaceArray, pos ) {
          var output = {};
      
          output[namespaceArray[pos]] = obj;
      
          if ( pos > 0 ) {
              output = buildObject(output, namespaceArray, pos-1);
          }
      
          return output;
      };
      

      它是如何工作的? 它从namespaceArray 数组的末尾开始,并创建一个具有一个属性的对象,该对象的名称是namespaceArray 中最后一个槽中的任何内容,其值为obj。然后它递归,将该对象包装在其他对象中,直到它用完 namespaceArray 中的名称。 posnamespaceArray 数组的长度。你可以在第一个函数调用中解决它,比如if ( typeof(pos) === "undefined" ) pos = namespaceArray.length - 1,但是每次函数递归时你都会有一个额外的语句来评估。

      首先,我们将inputname属性拆分成一个围绕命名空间分隔符的数组,并获取输入的值:

      var namespaceArray = inputElement.name.split("."),
          obj = inputElement.value;
      
      // Usually you'll want to do some undefined checks and whatnot as well
      

      然后我们只需调用我们的函数并将结果分配给某个变量:

      var myObj = buildObject(obj, namespaceArray, namespaceArray.length - 1);
      

      myObj 现在看起来像这样:

      {
          testGroup: {
              person: {
                  age: 25
              }
          }
      }
      

      此时我使用 jQuery 的扩展函数将该结构合并回原始数据对象:

      data = $.extend(true, data, myObj);
      

      然而,在无框架的 JavaScript 中合并两个对象并不是很困难,并且有很多 existing code 可以很好地完成这项工作。

      我确信有更有效的方法来完成这项工作,但这种方法很好地满足了我的需求。

      【讨论】:

        【解决方案5】:

        我知道这个问题的回答让提问者满意,但我最近遇到了这个问题,但写入值。我想出了自己的静态类来处理基于字符串路径的读写。默认路径分隔符为.,但您可以将其修改为任何内容,例如/。如果您想知道它是如何工作的,还会对代码进行注释。

        (function(){
            var o = {}, c = window.Configure = {}, seperator = '.';
        
            c.write = function(p, d)
            {
                // Split the path to an array and assaign the object
                // to a local variable
                var ps = p.split(seperator), co = o;
        
                // Iterate over the paths, skipping the last one
                for(var i = 0; i < ps.length - 1; i++)
                {
                    // Grab the next path's value, creating an empty 
                    // object if it does not exist
                    co = (co[ps[i]])? co[ps[i]] : co[ps[i]] = {};
                }
        
                // Assign the value to the object's last path
                co[ps[ps.length - 1]] = d;
            }
        
            c.read = function(p)
            {
                var ps = p.split(seperator), co = o;
                for(var i = 0; i < ps.length; i++)
                {
                    co = (co[ps[i]])? co[ps[i]] : co[ps[i]] = {};
                }
                return co;
            }
        })();
        

        【讨论】:

          【解决方案6】:

          我在 pastebin 上的示例: http://pastebin.com/13xUnuyV

          我喜欢你的代码,我用 PHP 做了很多类似的事情。 但在这里这很难,因为我虽然...... 所以我使用了@LordZardeck (https://stackoverflow.com/a/9338381/1181479) 和@Alnitak (https://stackoverflow.com/a/6491621/1181479) 的答案。

          我有什么,就用它吧:

              var someObject = {
                  'part1' : {
                      'name': 'Part 1',
                      'size': '20',
                      'qty' : '50'
                  },
                  'part2' : {
                      'name': 'Part 2',
                      'size': '15',
                      'qty' : '60'
                  },
                  'part3' : [
                      {
                          'name': 'Part 3A РУКУ!!!',
                          'size': '10',
                          'qty' : '20'
                      }, {
                          'name': 'Part 3B',
                          'size': '5',
                          'qty' : '20'
                      }, {
                          'name': 'Part 3C',
                          'size': '7.5',
                          'qty' : '20'
                      }
                  ]
              };
          
              //var o = {}, c = window.Configure = {}, seperator = '.';
          
              var c = function(){
                  this.o = {};
                  this.seperator = ".";
                  this.set = function(obj){
                      this.o = obj;
                  }
                  this.write = function(p, d) {
                      p = p.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
                      p = p.replace(/^\./, ''); // strip leading dot
                      // Split the path to an array and assaign the object
                      // to a local variable
                      var ps = p.split(this.seperator), co = this.o;
          
                      // Iterate over the paths, skipping the last one
                      for(var i = 0; i < ps.length - 1; i++)
                      {
                          // Grab the next path's value, creating an empty 
                          // object if it does not exist
                          co = (co[ps[i]])? co[ps[i]] : co[ps[i]] = {};
                      }
          
                      // Assign the value to the object's last path
                      co[ps[ps.length - 1]] = d;
                  }
                  this.read = function(p) {
                      p = p.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
                      p = p.replace(/^\./, ''); // strip leading dot
                      var ps = p.split(this.seperator), co = this.o;
                      /*
                      for(var i = 0; i < ps.length; i++)
                      {
                          co = (co[ps[i]])? co[ps[i]] : co[ps[i]] = {};
                      }
                      */
                      while (ps.length) {
                          var n = ps.shift();
                          if (n in co) {
                              co = co[n];
                          } else {
                              return;
                          }
                      }
                      return co;
                  }
          
              };
          
              var n = new c();
              n.set(someObject);
              console.log('whas');
              console.log('n.read part.name', n.read('part1.name'));
              n.write('part3[0].name', "custom var");
              console.log('part1.name now changed');
              n.write('part1.name', "tmp");
              console.log('n.read part.name', n.read('part1.name'));
              console.log('----');
              console.log('before', someObject);
              console.log('someObject.part1.name', someObject.part1.name);
              console.log('someObject.part3[0].name', someObject.part3[0].name);
          

          【讨论】:

            【解决方案7】:

            可以使用函数式编程技术来做到这一点,例如

            (function (s) {return s.split('.').reduce(function(p,n) { p[n] = p[n] || {}; return p[n];},window);})("some.cool.namespace");
            

            这可以分配给一个全局函数以供重复使用

            window.ns = (function (s) {return s.split('.').reduce(function(p,n) { p[n] = p[n] || {}; return p[n];},window);})
            

            那么我们可以做如下操作

            ns("some.cool").namespace = 5;
            
            if (5 != ns("some.cool.namespace")) { throw "This error can never happen" }
            

            【讨论】:

              【解决方案8】:

              以下假设strObjName 的各个部分由. 分隔,并从窗口开始循环直到找到您想要的功能:

              var strObjParts = strObjName.split('.');
              var obj = window;
              for(var i in strObjParts) {
                obj = obj[strObjParts[i]];
              }
              

              【讨论】:

              • for...in 通常不适用于需要顺序的示例,因为不能保证 for...in 的枚举顺序。使用普通的for 循环。
              • 不要使用 for-in 来循环 Javascript 中的数组。这是邪恶的。
              • @Tim:谢谢。我没有意识到订单没有保证。 @missingno:我不确定你所说的邪恶是什么意思。
              猜你喜欢
              • 2022-01-15
              • 1970-01-01
              • 2023-01-22
              • 1970-01-01
              • 1970-01-01
              • 2013-05-10
              • 2018-06-25
              • 2023-01-09
              • 1970-01-01
              相关资源
              最近更新 更多