我有一个这样的数据结构:

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 part1name = "part1.name";
var part2quantity = "part2.qty";
var part3name1 = "part3[0].name";

part1name应该用someObject.part1.name的值填充,即“Part 1”。part2quantity也是一样,它的容量是60。

有没有办法实现这与纯javascript或JQuery?


当前回答

虽然reduce很好,但我很惊讶没有人使用forEach:

function valueForKeyPath(obj, path){
        const keys = path.split('.');
        keys.forEach((key)=> obj = obj[key]);
        return obj;
    };

Test

其他回答

受到@webjay的回答的启发: https://stackoverflow.com/a/46008856/4110122

我做了这个函数,你可以用它来获取/设置/取消设置对象中的任何值

function Object_Manager(obj, Path, value, Action) 
{
    try
    {
        if(Array.isArray(Path) == false)
        {
            Path = [Path];
        }

        let level = 0;
        var Return_Value;
        Path.reduce((a, b)=>{
            level++;
            if (level === Path.length)
            {
                if(Action === 'Set')
                {
                    a[b] = value;
                    return value;
                }
                else if(Action === 'Get')
                {
                    Return_Value = a[b];
                }
                else if(Action === 'Unset')
                {
                    delete a[b];
                }
            } 
            else 
            {
                return a[b];
            }
        }, obj);
        return Return_Value;
    }

    catch(err)
    {
        console.error(err);
        return obj;
    }
}

使用它:

 // Set
 Object_Manager(Obj,[Level1,Level2,Level3],New_Value, 'Set');

 // Get
 Object_Manager(Obj,[Level1,Level2,Level3],'', 'Get');

 // Unset
 Object_Manager(Obj,[Level1,Level2,Level3],'', 'Unset');

React示例-使用lodash

从性能角度来看,这可能不是最有效的方法,但如果你的应用是一个庞然大物,它肯定会为你节省一些时间。特别是当您将状态数据格式与API后端紧密耦合时。

   import set from "lodash/set";  // More efficient import

    class UserProfile extends Component {

      constructor(props){
        super(props);

        this.state = {
          user: {
            account: {
              id: "",
              email: "",
              first_name: ""
            }
          }
        }
      }

       /**
       * Updates the state based on the form input
       * 
       * @param {FormUpdate} event 
       */
      userAccountFormHook(event) {
        // https://lodash.com/docs#get
        // https://lodash.com/docs#set
        const { name, value } = event.target;
        let current_state = this.state
        set(current_state, name, value)  // Magic happens here
        this.setState(current_state);
      }

    render() {
        return (
          <CustomFormInput
            label: "First Name"
            type: "text"
            placeholder: "First Name"
            name: "user.account.first_name"
            onChange: {this.userAccountFormHook}
            value: {this.state.user.account.first_name}

          />
      )
  }
}

以防万一,有人在2017年或以后访问这个问题,并寻找一种容易记住的方法,这里有一篇关于在JavaScript中访问嵌套对象而不被迷惑的详细博客文章

无法读取未定义错误的属性“foo”

使用数组缩减访问嵌套对象

让我们以这个例子结构为例

const user = {
    id: 101,
    email: 'jack@dev.com',
    personalInfo: {
        name: 'Jack',
        address: [{
            line1: 'westwish st',
            line2: 'washmasher',
            city: 'wallas',
            state: 'WX'
        }]
    }
}

为了能够访问嵌套数组,您可以编写自己的数组reduce util。

const getNestedObject = (nestedObj, pathArr) => {
    return pathArr.reduce((obj, key) =>
        (obj && obj[key] !== 'undefined') ? obj[key] : undefined, nestedObj);
}

// pass in your object structure as array elements
const name = getNestedObject(user, ['personalInfo', 'name']);

// to access nested array, just pass in array index as an element the path array.
const city = getNestedObject(user, ['personalInfo', 'address', 0, 'city']);
// this will return the city from the first address item.

还有一种出色的类型处理最小库类型可以为您完成所有这些。

使用typy,代码看起来像这样

const city = t(user, 'personalInfo.address[0].city').safeObject;

免责声明:我是这个软件包的作者。

// (IE9+) Two steps var pathString = "[0]['property'].others[3].next['final']"; var obj = [{ property: { others: [1, 2, 3, { next: { final: "SUCCESS" } }] } }]; // Turn string to path array var pathArray = pathString .replace(/\[["']?([\w]+)["']?\]/g,".$1") .split(".") .splice(1); // Add object prototype method Object.prototype.path = function (path) { try { return [this].concat(path).reduce(function (f, l) { return f[l]; }); } catch (e) { console.error(e); } }; // usage console.log(obj.path(pathArray)); console.log(obj.path([0,"doesNotExist"]));

我只是基于一些类似的代码,我已经有了,它似乎工作:

Object.byString = function(o, s) {
    s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
    s = s.replace(/^\./, '');           // strip a leading dot
    var a = s.split('.');
    for (var i = 0, n = a.length; i < n; ++i) {
        var k = a[i];
        if (k in o) {
            o = o[k];
        } else {
            return;
        }
    }
    return o;
}

用法::

Object.byString(someObj, 'part3[0].name');

在http://jsfiddle.net/alnitak/hEsys/上可以看到一个工作演示

有些人注意到,如果传递的字符串中最左边的索引与对象中正确嵌套的条目不对应,这段代码将抛出错误。这是一个有效的问题,但我认为最好在调用时使用try / catch块来解决,而不是让这个函数对于无效的索引无声地返回undefined。