编辑:在评论中回答一个有趣的问题:你如何将它也变成一个二传手?不仅按路径返回值,而且在将新值发送到函数时设置它们?– 斯瓦德 6 月 28 日 21:42(旁注:遗憾的是不能返回带有 Setter 的对象,因为这会违反调用约定;评论者似乎指的是具有副作用的一般 setter 样式函数,例如 index(obj,abetc", value) 做 obj.abetc = value.)reduce 风格不太适合这种情况,但我们可以修改递归实现:function index(obj,is, value) {if (typeof is == 'string')return index(obj,is.split('.'), value);else if (is.length==1 && value!==undefined)返回 obj[is[0]] = 值;否则如果(is.length==0)返回对象;别的返回索引(obj[is[0]],is.slice(1), value);}演示:>obj = {a:{b:{etc:5}}}>索引(对象,'a.b.etc')5>index(obj,['a','b','etc']) #适用于字符串和列表5>index(obj,'a.b.etc', 123) #setter-mode - 第三个参数(可能是糟糕的形式)123>索引(对象,'a.b.etc')123...虽然我个人建议创建一个单独的函数 setIndex(...).我想以旁注结束,问题的原始提出者可以(应该?)使用索引数组(它们可以从 .split 获得),而不是字符串;尽管便利功能通常没有问题.评论者问道:数组呢?诸如a.b[4].c.d[1][2][3]"之类的东西?——亚历克斯Javascript 是一种非常奇怪的语言;通常对象只能将字符串作为它们的属性键,例如,如果 x 是一个像 x={} 这样的通用对象,那么 x[1] 会变成 x["1"]...你没看错...是的...Javascript 数组(它们本身是 Object 的实例)特别鼓励整数键,即使您可以执行类似 x=[]; 的操作.x[小狗"]=5;.但总的来说(也有例外),x[somestring"]===x.somestring(当它被允许时;你不能做 x.123).(请记住,无论您使用的是什么 JS 编译器,如果可以证明它不会违反规范,都可能会选择将这些编译器编译为更合理的表示.)因此,您的问题的答案将取决于您是否假设这些对象仅接受整数(由于您的问题域的限制).让我们假设不是.那么一个有效的表达式是一个基本标识符加上一些 .identifier 加上一些 [stringindex"] 的串联.让我们暂时忽略我们当然可以在语法中合法地做其他事情,例如 identifier[0xFA7C25DD].asdf[f(4)?.[5]+k][false][null][未定义][NaN];整数不是(那个)特殊".评论者的声明将等同于 a[b"][4][c"][d"][1][2][3],尽管我们可能还应该支持 ab["c"validjsstringliteral"][3].您必须查看 关于字符串文字的 ecmascript 语法部分 查看如何解析有效的字符串文字.从技术上讲,您还想检查(与我的第一个答案不同)a 是否有效 javascript 标识符.不过,对您的问题的简单回答是,如果您的字符串不包含逗号或括号,则只是匹配长度为 1+ 的字符序列不在集合 中, 或 [ 或 ]:>"abc[4].c.def[1][2]["gh"]".match(/[^][.]+/g)//^^^ ^ ^ ^^^ ^ ^ ^^^^^[abc"、4"、c"、def"、1"、2"、gh"]如果您的字符串不包含转义字符或 " 字符,并且因为 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]);//非常糟糕的代码,因为 js 正则表达式很奇怪,不要使用它}>电阻[abc"、4"、c"、def"、1"、2"、gh"]当然,请始终小心谨慎,切勿相信您的数据.一些可能适用于某些用例的糟糕方法还包括://hackish/wrongish;将您的字符串预处理为a.b.4.c.d.1.2.3",例如:>yourstring.replace(/]/g,"").replace(/[/g,".").split(".")a.b.4.c.d.1.2.3"//使用之前的代码2018 年特别为了语法purityhamfistery 的利益,让我们转一圈,做我们能想出的最低效、最可怕的过度元编程解决方案.使用 ES6 代理对象!...让我们也定义一些属性(恕我直言很好,但是)可能会破坏不正确编写的库.如果您关心绩效、理智(您的或他人的)、您的工作等,您或许应该谨慎使用它.//[1,2,3][-1]==3 (或者只使用 .slice(-1)[0])如果 (![1][-1])Object.defineProperty(Array.prototype, -1, {get() {return this[this.length-1]}});//归功于caub//警告:这种 XTREME™ 激进方法非常低效,//特别是如果索引到多个对象,//因为您不断地动态创建包装对象,并且,//更糟糕的是,通过代理,即运行时 ~reflection,这会阻止//编译器优化//代理处理程序覆盖 obj[*]/obj.* 和 obj[*]=...var hyperIndexProxyHandler = {得到:函数(对象,键,代理){return key.split('.').reduce((o,i)=>o[i], obj);},设置:函数(对象,键,值,代理){var keys = key.split('.');var beforeLast = keys.slice(0,-1).reduce((o,i)=>o[i], obj);beforeLast[keys[-1]] = 值;},有:函数(对象,键){//等等}};功能 hyperIndexOf(目标){返回新代理(目标,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("(在幕后) objHyper 是:", objHyper);如果 (!({}).H)Object.defineProperties(Object.prototype, {H: {得到:函数(){返回 hyperIndexOf(this);//TODO:cache 作为一个不可枚举的属性来提高效率?}}});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['a.b.c'] 是:1(代理覆盖集)objHyper['abc']=3,现在 obj 是:{a":{b":{c":3,d":2}}}(幕后)objHyper 是:代理{a:{…}}(快捷方式)obj.H['a.b.c']=4(快捷方式)obj.H['a.b.c'] 是 obj['a']['b']['c'] 是:4低效的想法:可以修改上面的,根据输入参数进行调度;要么使用 .match(/[^][.]+/g) 方法来支持 obj['keys'].like[3]['this']code>,或者如果 instanceof Array,则只接受一个数组作为输入,例如 keys = ['a','b','c'];obj.H[keys].根据建议,也许您想以更软"的 NaN 样式方式处理未定义的索引(例如 index({a:{b:{c:...}}}, 'axc') 返回 undefined 而不是未捕获的 TypeError)...:从我们应该返回 undefined 而不是抛出错误"的角度来看,这是有道理的.在一维索引情况下 ({})['e.g.']==undefined,所以我们应该返回 undefined 而不是抛出错误";在 N 维情况下.从我们正在做的x['a']['x']['c']的角度来看,这没有有意义,它在上面的例子中会因 TypeError 而失败.也就是说,您可以通过以下任一方式替换您的减少功能来完成这项工作:(o,i)=>o===undefined?undefined:o[i], 或(o,i)=>(o||{})[i].(您可以通过使用 for 循环并在下一个索引的子结果未定义时中断/返回来提高效率,或者如果您预计此类失败非常罕见,则使用 try-catch.)>Given a JavaScript object,var obj = { a: { b: '1', c: '2' } }and a string"a.b"how can I convert the string to dot notation so I can govar val = obj.a.bIf the string was just 'a', I could use obj[a]. But this is more complex. I imagine there is some straightforward method, but it escapes me at present. 解决方案Here's an elegant one-liner that's 10x shorter than the other solutions:function index(obj,i) {return obj[i]}'a.b.etc'.split('.').reduce(index, obj)[edit] Or in ECMAScript 6:'a.b.etc'.split('.').reduce((o,i)=> o[i], obj)(Not that I think eval always bad like others suggest it is (though it usually is), nevertheless those people will be pleased that this method doesn't use eval. The above will find obj.a.b.etc given obj and the string "a.b.etc".)In response to those who still are afraid of using reduce despite it being in the ECMA-262 standard (5th edition), here is a two-line recursive implementation: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')Depending on the optimizations the JS compiler is doing, you may want to make sure any nested functions are not re-defined on every call via the usual methods (placing them in a closure, object, or global namespace).edit:To answer an interesting question in the comments:(sidenote: sadly can't return an object with a Setter, as that would violate the calling convention; commenter seems to instead be referring to a general setter-style function with side-effects like index(obj,"a.b.etc", value) doing obj.a.b.etc = value.)The reduce style is not really suitable to that, but we can modify the recursive implementation: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);}Demo:> obj = {a:{b:{etc:5}}}> index(obj,'a.b.etc')5> index(obj,['a','b','etc']) #works with both strings and lists5> index(obj,'a.b.etc', 123) #setter-mode - third argument (possibly poor form)123> index(obj,'a.b.etc')123...though personally I'd recommend making a separate function setIndex(...). I would like to end on a side-note that the original poser of the question could (should?) be working with arrays of indices (which they can get from .split), rather than strings; though there's usually nothing wrong with a convenience function.A commenter asked:Javascript is a very weird language; in general objects can only have strings as their property keys, so for example if x was a generic object like x={}, then x[1] would become x["1"]... you read that right... yup...Javascript Arrays (which are themselves instances of Object) specifically encourage integer keys, even though you could do something like x=[]; x["puppy"]=5;.But in general (and there are exceptions), x["somestring"]===x.somestring (when it's allowed; you can't do x.123).(Keep in mind that whatever JS compiler you're using might choose, maybe, to compile these down to saner representations if it can prove it would not violate the spec.)So the answer to your question would depend on whether you're assuming those objects only accept integers (due to a restriction in your problem domain), or not. Let's assume not. Then a valid expression is a concatenation of a base identifier plus some .identifiers plus some ["stringindex"]s.Let us ignore for a moment that we can of course do other things legitimately in the grammar like identifier[0xFA7C25DD].asdf[f(4)?.[5]+k][false][null][undefined][NaN]; integers are not (that) 'special'.Commenter's statement would then be equivalent to a["b"][4]["c"]["d"][1][2][3], though we should probably also support a.b["c"validjsstringliteral"][3]. You'd have to check the ecmascript grammar section on string literals to see how to parse a valid string literal. Technically you'd also want to check (unlike in my first answer) that a is a valid javascript identifier.A simple answer to your question though, if your strings don't contain commas or brackets, would be just be to match length 1+ sequences of characters not in the set , or [ or ]:> "abc[4].c.def[1][2]["gh"]".match(/[^][.]+/g)// ^^^ ^ ^ ^^^ ^ ^ ^^^^^["abc", "4", "c", "def", "1", "2", ""gh""]If your strings don't contain escape characters or " characters, and because IdentifierNames are a sublanguage of StringLiterals (I think???) you could first convert your dots to []:> 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"]Of course, always be careful and never trust your data. Some bad ways to do this that might work for some use cases also include:// 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 beforeSpecial 2018 edit:Let's go full-circle and do the most inefficient, horribly-overmetaprogrammed solution we can come up with... in the interest of syntactical purityhamfistery. With ES6 Proxy objects!... Let's also define some properties which (imho are fine and wonderful but) may break improperly-written libraries. You should perhaps be wary of using this if you care about performance, sanity (yours or others'), your job, etc.// [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);}Demo: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']);Output:inefficient idea: You can modify the above to dispatch based on the input argument; either use the .match(/[^][.]+/g) method to support obj['keys'].like[3]['this'], or if instanceof Array, then just accept an Array as input like keys = ['a','b','c']; obj.H[keys].Per suggestion that maybe you want to handle undefined indices in a 'softer' NaN-style manner (e.g. index({a:{b:{c:...}}}, 'a.x.c') return undefined rather than uncaught TypeError)...:This makes sense from the perspective of "we should return undefined rather than throw an error" in the 1-dimensional index situation ({})['e.g.']==undefined, so "we should return undefined rather than throw an error" in the N-dimensional situation.This does not make sense from the perspective that we are doing x['a']['x']['c'], which would fail with a TypeError in the above example.That said, you'd make this work by replacing your reducing function with either:(o,i)=> o===undefined?undefined:o[i], or(o,i)=> (o||{})[i].(You can make this more efficient by using a for loop and breaking/returning whenever the subresult you'd next index into is undefined, or using a try-catch if you expect such failures to be sufficiently rare.) 这篇关于将点表示法的 JavaScript 字符串转换为对象引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云!