Доступ к имени javascript-объекту по имени строки без использования 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
с именами строк?
Ответы
Ответ 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);
Ответ 2
Просто подумал, что я поделюсь этим, потому что я сделал это на днях. Я даже не понял, что сокращение было доступно в JS!
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
. 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 и существует много существующего кода, который хорошо выполняет свою работу.
Я уверен, что есть более эффективные способы сделать это, но этот метод хорошо удовлетворяет мои потребности.
Ответ 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 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 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 (fooobar.com/questions/395659/...) и @Alnitak (fooobar.com/questions/5853/...).
И вот что у меня есть, просто используйте его:
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 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 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]];
}