不使用eval,按字符串名称访问命名空间的javascript对象

时间:2011-02-13 00:42:09

标签: javascript namespaces eval

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

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

具体而言,我尝试将eval([name])替换为window[name],以通过全局对象vs 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与命名空间字符串一起使用吗?

8 个答案:

答案 0 :(得分:22)

您可以在.上拆分并依次解决每个属性。只要字符串中没有任何属性名称包含.字符,这就没问题了。

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

答案 1 :(得分:6)

只是想我分享这个,因为我前几天做了这个。我甚至没有意识到JS中有可用的减少!

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

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

答案 2 :(得分:2)

我想出了这个:

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

答案 3 :(得分:2)

我使用递归编写了不同的解决方案。我用它来命名输入元素的name属性。举个例子,如下:

<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数组的末尾开始,并创建一个具有一个属性的对象,其名称是{的最后一个插槽中的任何内容{1}},其值为namespaceArray。然后它会递归,将该对象包装在其他对象中,直到obj中的名称用完为止。 namespaceArraypos数组的长度。你可以在第一个函数调用中解决它,比如namespaceArray,但是每次函数递归时你都会有一个额外的语句来评估。

首先,我们将if ( typeof(pos) === "undefined" ) pos = namespaceArray.length - 1的{​​{1}}属性拆分为命名空间分隔符周围的数组,并获取输入的值:

name

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

input

myObj现在看起来像这样:

var namespaceArray = inputElement.name.split("."),
    obj = inputElement.value;

// Usually you'll want to do some undefined checks and whatnot as well

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

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

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

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

答案 4 :(得分:1)

我知道提问者已经回答这个问题,但我最近遇到了这个问题,但是写了这个问题。我想出了自己的静态类来处理基于字符串路径的读写。默认路径分隔符为.,但您可以将其修改为任何内容,例如/。代码也会被评论,因为你想知道它是如何工作的。

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

答案 5 :(得分:0)

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

答案 6 :(得分:0)

可以使用诸如

之类的函数编程技术来完成此操作
(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" }

答案 7 :(得分:-1)

以下假设strObjName的部分由.分隔,并从窗口开始循环,直到它变为您想要的函数:

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