当前位置: 首页 > 面试题库 >

将点表示法的JavaScript字符串转换为对象引用

宿建本
2023-03-14
问题内容

给定一个JS对象

var obj = { a: { b: '1', c: '2' } }

和一个字符串

"a.b"

如何将字符串转换为点表示法,以便我可以

var val = obj.a.b

如果字符串是正义的'a',我可以使用obj[a]。但这更复杂。我想有一种简单的方法,但是目前可以逃脱。


问题答案:

最近的说明: 虽然我很高兴这个答案获得了很多好评,但我还是有些恐惧。如果需要将点符号字符串(例如“
xabc”)转换为引用,则可能(可能)是表明发生了非常错误的信号(除非您正在执行一些奇怪的反序列化)。

也就是说,寻找答案的新手必须问自己一个问题:“我为什么要这样做?”

情况1 :作为处理数据的主要方法(例如,作为应用程序传递对象并取消引用对象的默认形式)。就像问“如何从字符串中查找函数或变量名”一样。

* 这是不好的编程习惯(特别是不必要的元编程,并且这违反了函数的无副作用编码风格,并且会对性能造成影响)。在这种情况下发现自己的新手,应该考虑使用数组表示形式,例如[‘x’,’a’,’b’,’c’],或者如果可能的话甚至使用更直接/简单/简单的方法:例如不丢失首先跟踪引用本身(如果仅是客户端或服务器端,则是最理想的选择),等等。(预先存在的唯一ID很难添加,但如果规范另外要求它可以使用是否存在。)

情况2 :使用序列化数据或将显示给用户的数据。就像使用日期作为字符串“
1999-12-30”而不是使用Date对象一样(如果不小心,可能会导致时区错误或增加序列化复杂性)。

  • 这也许很好。请注意,没有点串“。” 在您清理过的输入片段中。

如果您发现自己一直在使用此答案并在字符串和数组之间来回转换,则可能情况很糟糕,应考虑使用其他方法。

这是一个优雅的单缸套,比其他解决方案短十倍:

function index(obj,i) {return obj[i]}
'a.b.etc'.split('.').reduce(index, obj)

[edit]或在ECMAScript 6中:

'a.b.etc'.split('.').reduce((o,i)=>o[i], obj)

(并不是我认为eval总是像其他人认为的那样总是很糟糕(尽管通常是这样),尽管如此,那些人会为这种方法不使用eval而感到高兴。上面的内容将找到obj.a.b.etc给定的obj和字符串"a.b.etc"。)

为了回应那些仍然担心使用reduceECMA-262标准(第5版)的人,这里有两行递归实现:

function multiIndex(obj,is) {  // obj,['1','2','3'] -> ((obj['1'])['2'])['3']
    return is.length ? multiIndex(obj[is[0]],is.slice(1)) : obj
}
function pathIndex(obj,is) {   // obj,'1.2.3' -> multiIndex(obj,['1','2','3'])
    return multiIndex(obj,is.split('.'))
}
pathIndex('a.b.etc')

根据JS编译器所做的优化,您可能需要确保没有通过常规方法在每次调用时都重新定义任何嵌套函数(将它们放在闭包,对象或全局名称空间中)。

编辑

要在评论中回答一个有趣的问题:

您如何将其转换为二传手?不仅通过路径返回值,而且如果将新值发送到函数中,还要设置它们?–斯瓦德6月28日21:42

(sidenote:可悲的是无法返回带有Setter的对象,因为这将违反调用约定;注释者似乎是指具有副作用的通用setter样式函数,如index(obj,"a.b.etc", value)doing obj.a.b.etc = value。)

reduce样式确实不适合该样式,但是我们可以修改递归实现:

function index(obj,is, value) {
    if (typeof is == 'string')
        return index(obj,is.split('.'), value);
    else if (is.length==1 && value!==undefined)
        return obj[is[0]] = value;
    else if (is.length==0)
        return obj;
    else
        return index(obj[is[0]],is.slice(1), value);
}

演示:

> obj = {a:{b:{etc:5}}}

> index(obj,'a.b.etc')
5
> index(obj,['a','b','etc'])   #works with both strings and lists
5

> index(obj,'a.b.etc', 123)    #setter-mode - third argument (possibly poor form)
123

> index(obj,'a.b.etc')
123

…虽然我个人建议还是做一个单独的功能setIndex(...)。最后,我想补充一下,问题的原始提出者可以(应该?)使用索引数组(可以从中获取.split),而不是字符串。尽管便利功能通常没有错。

有评论者问:

数组呢?像“ ab [4] .cd [1] [2] [3]”之类的东西?–AlexS

Javascript是一种很奇怪的语言。通常,对象只能将字符串作为其属性键,因此,例如,如果x是一个通用对象,例如x={}x[1]则将变成x["1"]…您没看错…是的…

JavaScript数组(本身就是Object的实例)特别鼓励使用整数键,即使您可以执行类似的操作x=[]; x["puppy"]=5;

但通常(并且有例外)x["somestring"]===x.somestring(允许时;您不能这样做x.123)。

(请记住,如果使用的JS编译器可以证明不会违反规范,则可能会选择将其编译为更精巧的表示形式。)

因此,问题的答案取决于您是否假设这些对象仅接受整数(由于问题域中的限制)。让我们假设不是。那么有效的表达式是基本标识符加上一些.identifiers加上一些["stringindex"]s
的串联

那将等于a["b"][4]["c"]["d"][1][2][3],尽管我们可能也应该支持a.b["c\"validjsstringliteral"][3]。您必须检查有关字符串文字的ecmascript语法部分,以了解如何解析有效的字符串文字。从技术上讲,您还需要检查(与我的第一个答案不同)a有效的javascript标识符。

但是, 如果您的字符串不包含逗号或方括号 ,那么一个简单的答案就是匹配长度在1 ,[以上的字符序列,而不是set 或or ]

> "abc[4].c.def[1][2][\"gh\"]".match(/[^\]\[.]+/g)
// ^^^ ^  ^ ^^^ ^  ^   ^^^^^
["abc", "4", "c", "def", "1", "2", ""gh""]

如果您的字符串不包含转义字符或"character,并且由于IdentifierNames是StringLiterals的子语言(我认为是???),则可以先将点转换为[]:

> var R=[], demoString="abc[4].c.def[1][2][\"gh\"]";
> for(var match,matcher=/^([^\.\[]+)|\.([^\.\[]+)|\["([^"]+)"\]|\[(\d+)\]/g; 
      match=matcher.exec(demoString); ) {
  R.push(Array.from(match).slice(1).filter(x=>x!==undefined)[0]);
  // extremely bad code because js regexes are weird, don't use this
}
> R

["abc", "4", "c", "def", "1", "2", "gh"]

当然,请务必小心,不要信任您的数据。在某些用例中可行的一些坏方法还包括:

// hackish/wrongish; preprocess your string into "a.b.4.c.d.1.2.3", e.g.: 
> yourstring.replace(/]/g,"").replace(/\[/g,".").split(".")
"a.b.4.c.d.1.2.3"  //use code from before

特别2018编辑:

让我们全力以赴,并为语法 纯洁的
f谐性做一个我们可以想出的最低效,可怕的,过度编程的解决方案。使用ES6代理对象!…我们还要定义一些属性(imho很好,但是很不错),它们可能会破坏编写不正确的库。如果您关心表现,理智(您或他人的),工作等,则可能应该谨慎使用此功能。

// [1,2,3][-1]==3 (or just use .slice(-1)[0])
if (![1][-1])
    Object.defineProperty(Array.prototype, -1, {get() {return this[this.length-1]}}); //credit to caub

// WARNING: THIS XTREME™ RADICAL METHOD IS VERY INEFFICIENT,
// ESPECIALLY IF INDEXING INTO MULTIPLE OBJECTS,
// because you are constantly creating wrapper objects on-the-fly and,
// even worse, going through Proxy i.e. runtime ~reflection, which prevents
// compiler optimization

// Proxy handler to override obj[*]/obj.* and obj[*]=...
var hyperIndexProxyHandler = {
    get: function(obj,key, proxy) {
        return key.split('.').reduce((o,i)=>o[i], obj);
    },
    set: function(obj,key,value, proxy) {
        var keys = key.split('.');
        var beforeLast = keys.slice(0,-1).reduce((o,i)=>o[i], obj);
        beforeLast[keys[-1]] = value;
    },
    has: function(obj,key) {
        //etc
    }
};
function hyperIndexOf(target) {
    return new Proxy(target, hyperIndexProxyHandler);
}

演示:

var obj = {a:{b:{c:1, d:2}}};
console.log("obj is:", JSON.stringify(obj));

var objHyper = hyperIndexOf(obj);
console.log("(proxy override get) objHyper['a.b.c'] is:", objHyper['a.b.c']);
objHyper['a.b.c'] = 3;
console.log("(proxy override set) objHyper['a.b.c']=3, now obj is:", JSON.stringify(obj));

console.log("(behind the scenes) objHyper is:", objHyper);

if (!({}).H)
    Object.defineProperties(Object.prototype, {
        H: {
            get: function() {
                return hyperIndexOf(this); // TODO:cache as a non-enumerable property for efficiency?
            }
        }
    });

console.log("(shortcut) obj.H['a.b.c']=4");
obj.H['a.b.c'] = 4;
console.log("(shortcut) obj.H['a.b.c'] is obj['a']['b']['c'] is", obj.H['a.b.c']);

输出:

obj是:{“ a”:{“ b”:{“ c”:1,“ d”:2}}}

(代理覆盖获取)objHyper [‘abc’]为:1

(代理替代设置)objHyper [‘abc’] = 3,现在obj是:{“ a”:{“ b”:{“ c”:3,“ d”:2}}}

(在幕后)objHyper是:代理{a:{…}}

(快捷方式)obj.H [‘abc’] = 4

(快捷方式)obj.H [‘abc’]为obj [‘a’] [‘b’] [‘c’]为:4

效率低下的想法:您可以根据输入参数修改以上内容以进行分派;要么使用.match(/[^\]\[.]+/g)方法来支持obj['keys'].like[3]['this'],要么如果instanceof Array接受,那么就接受Array作为输入keys = ['a','b','c']; obj.H[keys]

每个建议,也许您想以一种“更软”的NaN风格处理未定义的索引(例如,index({a:{b:{c:...}}}, 'a.x.c')返回未定义而不是未捕获的TypeError)…:

1)从一维索引情况({})[‘eg’] ==
undefined中“应该返回未定义而不是抛出错误”的角度来看,这是有意义的,因此“我们应该返回未定义而不是抛出错误。错误”。

2)从我们所做的角度来看,这是 没有 意义的,x['a']['x']['c']在上面的示例中,它会因TypeError而失败。

就是说,您可以通过以下任一方法来替换约简函数,从而完成这项工作:

(o,i)=>o===undefined?undefined:o[i](o,i)=>(o||{})[i]

(您可以通过使用for循环并在未定义要进入下一个索引的子结果时中断/返回来提高效率,或者如果您希望这种故障很少发生,则可以使用try-
catch来提高效率。)



 类似资料:
  • 问题内容: 考虑一下javascript中的此对象, 给定字符串“ obj.ab”,如何获取该对象所指向的对象,以便可以更改其值?即我希望能够做类似的事情 其中“ obj.ab”和“obj.ac”是字符串(不是obj引用)。我碰到了,在这里我可以得到点符号字符串指向obj的值,但我需要的是一种可以获取对象本身的方法? 对象的嵌套可能比这更深。即也许 问题答案: 要获得该值,请考虑: 或更花哨的方式

  • 问题内容: 将字符串表示形式转换为对象,但我要相反。对象要转换为JSON字符串,我有一个链接http://www.devcurry.com/2010/03/convert- javascript-object-to-json.html, 但是它需要json2.js jQuery是否具有本机功能方法来做到这一点? 问题答案: jQuery只会在调用本机浏览器方法之前进行一些正则表达式检查。如果不可用

  • 问题内容: 如何使用JavaScript(或jQuery)将描述对象的字符串转换为JSON字符串? 例如:转换这个( 不是 有效的JSON字符串): 到这个: 如果可能,我希望避免使用。 问题答案: 如果字符串是来自可靠来源 ,你可以使用然后的结果。像这样: 请注意,当您使用对象文字时,必须将其括在圆括号中,否则将花括号解析为块而不是对象。 我也同意以下问题的评论,那就是最好以有效的JSON开始编

  • 问题内容: 您如何使JS认为字符串是JSON? 我有一个仅在将JSON对象传递给它的情况下才起作用的函数。如果我以与JSON相同的格式将字符串传递给它,则它将不起作用。因此,我想让该函数认为传递给它的字符串是JSON。该字符串确实采用JSON格式。 我还尝试了以下方法。我通过Ajax输入了字符串,参数“ handle as”为“ JSON”,然后将结果传递给函数。 所以我推断出问题不在弦上。如何将

  • 问题内容: 我已经编写了一个android程序来将值从Web服务加载到表行。但是值变为null,因此我需要将其转换为字符串。有人可以告诉我这样做的方法吗? 现在从Web服务获取空值,因此我需要将其转换为string 。 LogCat: 问题答案: 用这个,

  • 问题内容: 我想知道如何使用python的反射功能将python’type’对象转换为字符串。 例如,我想打印一个对象的类型 问题答案: 如果那不适合您,请使用此: 例: 另外,使用新样式的类和旧样式的类(即从继承)之间似乎也存在差异。对于新样式的类,返回名称,对于旧样式的类,返回。