通过自定义动态方法访问嵌套的 JavaScript 属性

Accessing nested JavaScript properties via custom dynamic method

提问人:tweb 提问时间:9/30/2023 最后编辑:tweb 更新时间:10/1/2023 访问量:79

问:

给定这个 JavaScript 对象,我想通过以“get”开头的动态方法访问它们的嵌套属性 + CamelCased 属性名称(当然,该属性必须在初始对象中定义)

据我所知,这可以通过使用代理对象 + 递归来实现,但无论如何我都需要您的输入。

此致敬意!

function Order() {
    this.no = 'n1234';
    this.info = {
        customer: {
            ID: 'c1234'
            address: {
                //...
                shiping: {
                    // ...
                }
            }
        }
    }
}

var order = new Order();

// The expected result should be:
// order.no = order.getNo()
// order.info = order.getInfo()
// order.info.customer or order.info.getCustomer() or order.getInfo().customer or order.getInfo().getCustomer()
// and so on for all nested attributes

这是我准备的片段,但它似乎不适用于嵌套属性。

function createProxy(obj) {
  return new Proxy(obj, {
    get(target, prop) {
      if (typeof target[prop] === 'object' && target[prop] !== null) {
        return createProxy(target[prop]);
      }
      if (prop.startsWith('get')) {
        const propName = prop.charAt(3).toLowerCase() + prop.slice(4);
        if (target[propName] !== undefined) {
          return () => target[propName];
        }
      }
      return target[prop];
    },
  });
}

function Order() {
  const properties = {
    no: 'n1234',
    info: {
      customer: {
        ID: 'c1234'
      }
    }
  };

  return createProxy(properties);
}

var order = new Order();

// order.getInfo().customer - { ID: 'c1234' }
// order.getInfo().getCustomer() - TypeError: order.getInfo().getCustomer is not a function"
JavaScript 方法 嵌套的 动态 属性

评论

1赞 Barmar 9/30/2023
所以你想要一些东西来自动定义飞蛾,比如?当你可以写的时候,你为什么需要它?getNo()order.no
0赞 Barmar 9/30/2023
对于代理,您需要添加一个代理来标记嵌套对象。我不认为当您访问嵌套属性时会使用代理。order
0赞 tweb 9/30/2023
@Barmar,是的,我需要一个代码才能实现。两者都应该有效
0赞 Barmar 9/30/2023
你似乎知道你必须创建代理。展示你尝试过的内容。
1赞 Barmar 9/30/2023
正如我所说,顶级对象的代理不适用于嵌套对象。您需要为每个级别创建代理。

答:

1赞 Alexander Nenashev 9/30/2023 #1

一些调整会有所帮助,主要是从调用中返回代理:getXXX()

const createProxy = (obj) => new Proxy(obj, {
  get(target, prop) {
    const out = () => target[prop]?.__proto__ === Object.prototype ? createProxy(target[prop]) : target[prop];
    return prop.startsWith?.('get') && (prop = prop[3].toLowerCase() + prop.slice(4)) ? out : out();
  },
});

function Order() {
  const properties = {
    no: 'n1234',
    info: {
      customer: {
        ID: 'c1234',
        title: 'Bad customer'
      }
    }
  };

  return createProxy(properties);
}

var order = new Order();

console.log(order.getInfo());
console.log(order.getInfo().getCustomer().getTitle());
console.log(order.info.getCustomer().ID);

评论

0赞 tweb 10/1/2023
谢谢!这很有用。我只看到getID()无法解析并返回undefined。无论如何,我修改了您的代码,以便获得预期的结果。
1赞 tweb 10/1/2023 #2

这是有效的解决方案。感谢 @Barmar Alexander Nenashev

/**
 * @param {Object} obj
 * @returns {Proxy} obj
 */
function createProxy(obj) {
    return new Proxy(obj, {
        get(target, prop) {
            function out() {
                if (Object.getPrototypeOf(target[prop]) === Object.prototype) {
                    return createProxy(target[prop]);
                } else {
                    return target[prop];
                }
            }

            if (!prop.startsWith('get')) {
                return out();
            }

            var property = prop[3] + prop.slice(4);

            if (property.valueOf().toUpperCase() === property.valueOf()) {
                prop = property;
                return out;
            } else {
                prop = prop[3].toLowerCase() + prop.slice(4);
                return out
            }
        },
    });
}

function Order() {
    this.no = 'n1234';
    this.info = {
        customer: {
            ID: 'c1234',
            address: {
                shipping: {
                    ID: 's4354654'
                }
            }
        }
    }
    
    return createProxy(this);
}

var order = new Order();

console.log(order.no) // "n1234"
console.log(order.getNo()) // "n1234"
console.log(order.info.getCustomer().getID()) // "c1234"
console.log(order.getInfo().customer.ID) // "c1234"
console.log(order.info.customer.getAddress().getShipping().ID) // "s4354654"
console.log(order.getInfo().getCustomer().address.getShipping().getID()) // "s4354654"