Получите доступ к объекту javascript с пространством имен по имени строки без использования eval - PullRequest
13 голосов
/ 13 февраля 2011

Я столкнулся с ситуацией, когда мне нужен доступ к объекту 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 со строками из пространства имен?

Ответы [ 8 ]

22 голосов
/ 13 февраля 2011

Вы можете разделить на . и разрешить каждое свойство по очереди.Это будет хорошо, если ни одно из имен свойств в строке не содержит символ .:

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);
6 голосов
/ 18 апреля 2014

Просто подумал, что поделюсь этим, потому что я сделал это на днях.Я даже не догадывался, что сокращение было доступно в 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 голосов
/ 01 мая 2012

Я написал другое решение, используя рекурсию. Я использовал это для именования атрибута 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 и создает объект с одним свойством, именем которого является то, что находится в последнем слоте в namespaceArray, и значение которого составляет obj. Затем он повторяется, оборачивая этот объект в дополнительные объекты, пока у него не закончатся имена в namespaceArray. pos - длина массива namespaceArray. Вы можете просто обработать это при первом вызове функции, что-то вроде if ( typeof(pos) === "undefined" ) pos = namespaceArray.length - 1, но тогда у вас будет дополнительный оператор для оценки каждый раз, когда функция повторяется.

Сначала мы разбиваем атрибут name input на массив вокруг разделителей пространства имен и получаем значение ввода:

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 без фреймворка, и существует множество существующего кода , который хорошо справляется с работой.

Я уверен, что есть более эффективные способы сделать это, но этот метод хорошо отвечает моим потребностям.

2 голосов
/ 13 февраля 2011

Я придумал это:

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
1 голос
/ 18 февраля 2012

Я знаю, что этот вопрос был удовлетворительно отвечен на вопрос, но у меня недавно была эта проблема, но с НАПИСАНИЕМ на значение.Я придумал свой собственный статический класс для обработки чтения и записи на основе строкового пути.Разделитель пути по умолчанию - ., но вы можете изменить его на что угодно, например /.Код также комментируется, если вам интересно, как он работает.

(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;
    }
})();
0 голосов
/ 22 декабря 2017

Это можно сделать, используя методы функционального программирования, такие как

(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" }
0 голосов
/ 14 ноября 2013

Мой образец на пастбине: 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);
0 голосов
/ 13 февраля 2011

Далее предполагается, что части strObjName разделены . и циклически начинается с окна, пока оно не перейдет к нужной функции:

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