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

2020/10/13 13:01 · javascript ·  · 0评论

给定一个JS对象

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

和一个字符串

"a.b"

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

var val = obj.a.b

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

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

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

如果您的用例很小,并且不会遇到性能问题,并且通常不需要在抽象上进行构建,以后再使它变得更复杂,那么这样做通常是可以的。实际上,如果这将减少代码的复杂性并使事情简单,那么您可能应该继续执行OP所要求的。但是,如果不是这种情况,请考虑以下任何一项是否适用:

情况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+的字符序列(不在集合,or[或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编辑:

让我们全力以赴,并为语法纯洁的礼貌做一个我们可以想出的最低效,可怕的,过度编程的解决方案使用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接受,那么就接受数组作为输入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来提高效率。)

如果可以使用lodash,则有一个函数可以完全做到这一点:

_.get(对象,路径,[defaultValue])

var val = _.get(obj, "a.b");

您也可以使用lodash.get

您只需安装此软件包(npm i --save lodash.get),然后像这样使用它:

const get = require('lodash.get');

const myObj = { user: { firstName: 'Stacky', lastName: 'Overflowy' }, id: 123 };

console.log(get(myObj, 'user.firstName')); // prints Stacky
console.log(get(myObj, 'id')); //prints  123

//You can also update values
get(myObj, 'user').firstName = John;

递归涉及更多示例。

function recompose(obj,string){
    var parts = string.split('.');
    var newObj = obj[parts[0]];
    if(parts[1]){
        parts.splice(0,1);
        var newString = parts.join('.');
        return recompose(newObj,newString);
    }
    return newObj;
}


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

alert(recompose(obj,'a.d.a.b')); //blah

如果您希望多次取消对同一路径的引用,那么到目前为止,为每个点表示法路径构建函数实际上都具有最佳的性能(扩大了James Wilkins在上面的评论中链接的性能测试)。

var path = 'a.b.x';
var getter = new Function("obj", "return obj." + path + ";");
getter(obj);

就安全性和最坏情况的性能而言,使用Function构造函数具有与eval()相同的缺点,但是IMO对于需要极端动态性和高性能相结合的情况而言,是一个使用不足的工具。我使用这种方法来构建数组过滤器函数,并在AngularJS摘要循环中调用它们。我的配置文件一致地显示了array.filter()步骤,使用3-4级深度的动态定义路径,用不到1ms的时间对大约2000个复杂对象进行了解引用和过滤。

当然,可以使用类似的方法来创建setter函数:

var setter = new Function("obj", "newval", "obj." + path + " = newval;");
setter(obj, "some new val");

自原始职位以来已有多年。现在有一个很棒的库叫做“对象路径”。
https://github.com/mariocasciaro/object-path

在NPM和BOWER上可用
https://www.npmjs.com/package/object-path

就像这样简单:

objectPath.get(obj, "a.c.1");  //returns "f"
objectPath.set(obj, "a.j.0.f", "m");

并且适用于深层嵌套的属性和数组。

我建议拆分路径并对其进行迭代,并减少所拥有的对象。该建议使用缺少属性的默认值

const getValue = (object, keys) => keys.split('.').reduce((o, k) => (o || {})[k], object);

console.log(getValue({ a: { b: '1', c: '2' } }, 'a.b'));
console.log(getValue({ a: { b: '1', c: '2' } }, 'foo.bar.baz'));

您可以使用npm上可用的库,从而简化了此过程。https://www.npmjs.com/package/dot-object

 var dot = require('dot-object');

var obj = {
 some: {
   nested: {
     value: 'Hi there!'
   }
 }
};

var val = dot.pick('some.nested.value', obj);
console.log(val);

// Result: Hi there!

请注意,如果您已经在使用Lodash,则可以使用propertyget函数:

var obj = { a: { b: '1', c: '2' } };
_.property('a.b')(obj); // => 1
_.get(obj, 'a.b'); // => 1

下划线也具有property功能,但不支持点表示法。

其他建议有些含糊,所以我想我会做出贡献:

Object.prop = function(obj, prop, val){
    var props = prop.split('.')
      , final = props.pop(), p 
    while(p = props.shift()){
        if (typeof obj[p] === 'undefined')
            return undefined;
        obj = obj[p]
    }
    return val ? (obj[final] = val) : obj[final]
}

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

// get
console.log(Object.prop(obj, 'a.c')) // -> 2
// set
Object.prop(obj, 'a.c', function(){})
console.log(obj) // -> { a: { b: '1', c: [Function] } }
var a = { b: { c: 9 } };

function value(layer, path, value) {
    var i = 0,
        path = path.split('.');

    for (; i < path.length; i++)
        if (value != null && i + 1 === path.length)
            layer[path[i]] = value;
        layer = layer[path[i]];

    return layer;
};

value(a, 'b.c'); // 9

value(a, 'b.c', 4);

value(a, 'b.c'); // 4

与更简单的eval方法相比,这是很多代码,但是就像Simon Willison所说的那样,您永远不要使用eval

另外,JSFiddle

我已经扩展了ninjagecko的优雅答案,以便该函数可以处理点和/或数组样式引用,并可以使空字符串导致返回父对象。

干得好:

string_to_ref = function (object, reference) {
    function arr_deref(o, ref, i) { return !ref ? o : (o[ref.slice(0, i ? -1 : ref.length)]) }
    function dot_deref(o, ref) { return ref.split('[').reduce(arr_deref, o); }
    return !reference ? object : reference.split('.').reduce(dot_deref, object);
};

在这里查看我的工作jsFiddle示例:http : //jsfiddle.net/sc0ttyd/q7zyd/

您可以使用单行代码通过点表示法获取对象成员的值:

new Function('_', 'return _.' + path)(obj);

在您的情况下:

var obj = { a: { b: '1', c: '2' } }
var val = new Function('_', 'return _.a.b')(obj);

为简单起见,您可以编写如下函数:

function objGet(obj, path){
    return new Function('_', 'return _.' + path)(obj);
}

说明:

Function构造函数创建一个新的Function对象。在JavaScript中,每个函数实际上都是一个Function对象。使用Function构造函数显式创建函数的语法是:

new Function ([arg1[, arg2[, ...argN]],] functionBody)

其中arguments(arg1 to argN)必须是与有效javaScript标识符相对应的字符串,并且functionBody是包含包含函数定义的javaScript语句的字符串。

在我们的例子中,我们利用字符串函数体的优势来检索带点表示法的对象成员。

希望能帮助到你。

var find = function(root, path) {
  var segments = path.split('.'),
      cursor = root,
      target;

  for (var i = 0; i < segments.length; ++i) {
   target = cursor[segments[i]];
   if (typeof target == "undefined") return void 0;
   cursor = target;
  }

  return cursor;
};

var obj = { a: { b: '1', c: '2' } }
find(obj, "a.b"); // 1

var set = function (root, path, value) {
   var segments = path.split('.'),
       cursor = root,
       target;

   for (var i = 0; i < segments.length - 1; ++i) {
      cursor = cursor[segments[i]] || { };
   }

   cursor[segments[segments.length - 1]] = value;
};

set(obj, "a.k", function () { console.log("hello world"); });

find(obj, "a.k")(); // hello world

GET / SET答案也可以在本机反应中使用(您Object.prototype目前无法分配给它):

Object.defineProperty(Object.prototype, 'getNestedProp', {
    value: function(desc) {
        var obj = this;
        var arr = desc.split(".");
        while(arr.length && (obj = obj[arr.shift()]));
        return obj;
    },
    enumerable: false
});

Object.defineProperty(Object.prototype, 'setNestedProp', {
    value: function(desc, value) {
        var obj = this;
        var arr = desc.split(".");
        var last = arr.pop();
        while(arr.length && (obj = obj[arr.shift()]));
        obj[last] = value;
    },
    enumerable: false
});

用法:

var a = { values: [{ value: null }] };
var b = { one: { two: 'foo' } };

a.setNestedProp('values.0.value', b.getNestedProp('one.two'));
console.log(a.values[0].value); // foo

我从里卡多·托马西(Ricardo Tomasi)的答案中复制了以下内容,并进行了修改,以创建还没有必要的子对象。它的效率较低(更多ifs并创建空对象),但效果应该不错。

而且,它使我们能够做Object.prop(obj, 'a.b', false)以前无法做的事情不幸的是,它仍然不能让我们分配undefined...不确定该怎么做。

/**
 * Object.prop()
 *
 * Allows dot-notation access to object properties for both getting and setting.
 *
 * @param {Object} obj    The object we're getting from or setting
 * @param {string} prop   The dot-notated string defining the property location
 * @param {mixed}  val    For setting only; the value to set
 */
 Object.prop = function(obj, prop, val){
   var props = prop.split('.'),
       final = props.pop(),
       p;

   for (var i = 0; i < props.length; i++) {
     p = props[i];
     if (typeof obj[p] === 'undefined') {
       // If we're setting
       if (typeof val !== 'undefined') {
         // If we're not at the end of the props, keep adding new empty objects
         if (i != props.length)
           obj[p] = {};
       }
       else
         return undefined;
     }
     obj = obj[p]
   }
   return typeof val !== "undefined" ? (obj[final] = val) : obj[final]
 }

如果要将任何包含点符号键的对象转换为这些键的数组版本,可以使用此方法。


这将转换类似

{
  name: 'Andy',
  brothers.0: 'Bob'
  brothers.1: 'Steve'
  brothers.2: 'Jack'
  sisters.0: 'Sally'
}

{
  name: 'Andy',
  brothers: ['Bob', 'Steve', 'Jack']
  sisters: ['Sally']
}

convertDotNotationToArray(objectWithDotNotation) {

    Object.entries(objectWithDotNotation).forEach(([key, val]) => {

      // Is the key of dot notation 
      if (key.includes('.')) {
        const [name, index] = key.split('.');

        // If you have not created an array version, create one 
        if (!objectWithDotNotation[name]) {
          objectWithDotNotation[name] = new Array();
        }

        // Save the value in the newly created array at the specific index 
        objectWithDotNotation[name][index] = val;
        // Delete the current dot notation key val
        delete objectWithDotNotation[key];
      }
    });

}

这是我的代码,不使用eval它也很容易理解。

function value(obj, props) {
  if (!props) return obj;
  var propsArr = props.split('.');
  var prop = propsArr.splice(0, 1);
  return value(obj[prop], propsArr.join('.'));
}

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

console.log(value(obj, 'a.d.a.b')); //returns blah

是的,是在4年前问过的,是的,扩展基本原型通常不是一个好主意,但是,如果将所有扩展都放在一个位置,它们可能会很有用。

因此,这是我的方法。

   Object.defineProperty(Object.prototype, "getNestedProperty", {
    value     : function (propertyName) {
        var result = this;
        var arr = propertyName.split(".");

        while (arr.length && result) {
            result = result[arr.shift()];
        }

        return result;
    },
    enumerable: false
});

现在,您可以在任何地方获取嵌套属性,而无需导入具有功能的模块或复制/粘贴功能。

UPD示例:

{a:{b:11}}.getNestedProperty('a.b'); //returns 11

UPD 2.我的项目中的下一个扩展打破了猫鼬。我也读过它可能会破坏jQuery。所以,千万不要以其他方式

 Object.prototype.getNestedProperty = function (propertyName) {
    var result = this;
    var arr = propertyName.split(".");

    while (arr.length && result) {
        result = result[arr.shift()];
    }

    return result;
};

这是我的实现

实施1

Object.prototype.access = function() {
    var ele = this[arguments[0]];
    if(arguments.length === 1) return ele;
    return ele.access.apply(ele, [].slice.call(arguments, 1));
}

实现2(使用数组缩减而不是切片)

Object.prototype.access = function() {
    var self = this;
    return [].reduce.call(arguments,function(prev,cur) {
        return prev[cur];
    }, self);
}

例子:

var myobj = {'a':{'b':{'c':{'d':'abcd','e':[11,22,33]}}}};

myobj.access('a','b','c'); // returns: {'d':'abcd', e:[0,1,2,3]}
myobj.a.b.access('c','d'); // returns: 'abcd'
myobj.access('a','b','c','e',0); // returns: 11

它也可以处理数组内的对象

var myobj2 = {'a': {'b':[{'c':'ab0c'},{'d':'ab1d'}]}}
myobj2.access('a','b','1','d'); // returns: 'ab1d'

这是我提出的扩展解决方案:ninjagecko

对我来说,简单的字符串表示法是不够的,因此以下版本支持以下内容:

index(obj,'data.accounts [0] .address [0] .postcode');

/**
 * Get object by index
 * @supported
 * - arrays supported
 * - array indexes supported
 * @not-supported
 * - multiple arrays
 * @issues:
 *  index(myAccount, 'accounts[0].address[0].id') - works fine
 *  index(myAccount, 'accounts[].address[0].id') - doesnt work
 * @Example:
 * index(obj, 'data.accounts[].id') => returns array of id's
 * index(obj, 'data.accounts[0].id') => returns id of 0 element from array
 * index(obj, 'data.accounts[0].addresses.list[0].id') => error
 * @param obj
 * @param path
 * @returns {any}
 */
var index = function(obj, path, isArray?, arrIndex?){

    // is an array
    if(typeof isArray === 'undefined') isArray = false;
    // array index,
    // if null, will take all indexes
    if(typeof arrIndex === 'undefined') arrIndex = null;

    var _arrIndex = null;

    var reduceArrayTag = function(i, subArrIndex){
        return i.replace(/(\[)([\d]{0,})(\])/, (i) => {
            var tmp = i.match(/(\[)([\d]{0,})(\])/);
            isArray = true;
            if(subArrIndex){
                _arrIndex =  (tmp[2] !== '') ? tmp[2] : null;
            }else{
                arrIndex =  (tmp[2] !== '') ? tmp[2] : null;
            }
            return '';
        });
    }

    function byIndex(obj, i) {
        // if is an array
        if(isArray){
            isArray = false;
            i = reduceArrayTag(i, true);
            // if array index is null,
            // return an array of with values from every index
            if(!arrIndex){
                var arrValues = [];
                _.forEach(obj, (el) => {
                    arrValues.push(index(el, i, isArray, arrIndex));
                })
                return arrValues;
            }
            // if array index is specified
            var value = obj[arrIndex][i];
            if(isArray){
                arrIndex = _arrIndex;
            }else{
                arrIndex = null;
            }
            return value;
        }else{
            // remove [] from notation,
            // if [] has been removed, check the index of array
            i = reduceArrayTag(i, false);
            return obj[i]
        }
    }

    // reduce with byIndex method
    return path.split('.').reduce(byIndex, obj)
}

冒着击败一头死马的风险...我发现这对于遍历嵌套对象以参考您相对于基础对象或具有相同结构的相似对象所处的位置最为有用。为此,这对于嵌套对象遍历函数很有用。请注意,我已经使用数组保存路径。修改它以使用字符串路径或数组将是微不足道的。另请注意,您可以为该值分配“未定义”,这与其他一些实现不同。

/*
 * Traverse each key in a nested object and call fn(curObject, key, value, baseObject, path)
 * on each. The path is an array of the keys required to get to curObject from
 * baseObject using objectPath(). If the call to fn() returns falsey, objects below
 * curObject are not traversed. Should be called as objectTaverse(baseObject, fn).
 * The third and fourth arguments are only used by recursion.
 */
function objectTraverse (o, fn, base, path) {
    path = path || [];
    base = base || o;
    Object.keys(o).forEach(function (key) {
        if (fn(o, key, o[key], base, path) && jQuery.isPlainObject(o[key])) {
            path.push(key);
            objectTraverse(o[key], fn, base, path);
            path.pop();
        }
    });
}

/*
 * Get/set a nested key in an object. Path is an array of the keys to reference each level
 * of nesting. If value is provided, the nested key is set.
 * The value of the nested key is returned.
 */
function objectPath (o, path, value) {
    var last = path.pop();

    while (path.length && o) {
        o = o[path.shift()];
    }
    if (arguments.length < 3) {
        return (o? o[last] : o);
    }
    return (o[last] = value);
}

我在项目中使用了这段代码

const getValue = (obj, arrPath) => (
  arrPath.reduce((x, y) => {
    if (y in x) return x[y]
    return {}
  }, obj)
)

用法:

const obj = { id: { user: { local: 104 } } }
const path = [ 'id', 'user', 'local' ]
getValue(obj, path) // return 104

几年后,我发现它可以处理范围和数组。例如a['b']["c"].d.etc

function getScopedObj(scope, str) {
  let obj=scope, arr;

  try {
    arr = str.split(/[\[\]\.]/) // split by [,],.
      .filter(el => el)             // filter out empty one
      .map(el => el.replace(/^['"]+|['"]+$/g, '')); // remove string quotation
    arr.forEach(el => obj = obj[el])
  } catch(e) {
    obj = undefined;
  }

  return obj;
}

window.a = {b: {c: {d: {etc: 'success'}}}}

getScopedObj(window, `a.b.c.d.etc`)             // success
getScopedObj(window, `a['b']["c"].d.etc`)       // success
getScopedObj(window, `a['INVALID']["c"].d.etc`) // undefined

目前尚不清楚您的问题是什么。给定您的对象,obj.a.b将按原样给您“ 2”。如果要操纵字符串以使用方括号,则可以执行以下操作:

var s = 'a.b';
s = 'obj["' + s.replace(/\./g, '"]["') + '"]';
alert(s); // displays obj["a"]["b"]

这是我要付的10美分,波纹管功能将根据提供的路径获得/设置,..确定您可以改善它,删除|| Object.hasOwnProperty如果您错误地关心错误的值,则将其替换为

我用a.b.cab2.c{a:{b:[0,1,{c:7}]}}和它的设置和获取:)测试了它

啦啦队

function helper(obj, path, setValue){
  const l = String(path).split('.');
  return l.reduce((o,i, idx)=>{
   if( l.length-idx===1)  { o[i] = setValue || o[i];return setValue ? obj : o[i];}
  o[i] = o[i] || {};
   return o[i];
  }, x)
}
本文地址:http://javascript.askforanswer.com/jiangdianbiaoshifadejavascriptzifuchuanzhuanhuanweiduixiangyinyong.html
文章标签:
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!