如何通过点属性访问器优雅获取对象键名的字符串形式
优雅获取对象属性名的几种实用方案
嘿,你提到的那个Object.keys加indexOf的方法确实不够优雅,而且还暗藏坑点——比如如果对象里有两个属性值相同,这个方法就会返回第一个匹配的属性名,完全不符合预期。下面给你几个更靠谱、更符合你需求的方案:
1. 箭头函数辅助法(最推荐)
写一个简单的通用函数,利用箭头函数的字符串表示来解析属性名,这也是社区里最常用的优雅方案:
const getPropName = (fn) => { // 匹配箭头函数里的属性访问表达式 const match = fn.toString().match(/^\s*\(\s*\)\s*=>\s*[^\.]+\.([^\s;]+)/); return match ? match[1] : ''; }; // 用起来超简单 const x = { y: 123, z: 321 }; console.log(getPropName(() => x.y)); // 输出 'y'
这个方法的优势很明显:
- 完全不会因为属性值重复而出错
- 写法直观,一眼就能看懂是在取哪个属性的名字
- 如果是用TypeScript开发,写错属性名会直接报错,类型安全拉满
2. Proxy实现原生name()方法(完全匹配你的需求)
如果你真的想要x.y.name()这种调用方式,用ES6的Proxy就能实现,给每个属性包装一个带name方法的对象:
const wrapWithName = (obj) => { return new Proxy(obj, { get(target, prop) { const value = target[prop]; // 基础类型值包装成带name方法的对象 if (typeof value !== 'object' || value === null) { return { get value() { return value; }, name: () => prop }; } // 如果属性是对象,递归包装 return wrapWithName(value); } }); }; // 测试一下 const x = wrapWithName({ y: 123, z: 321 }); console.log(x.y.name()); // 输出 'y' console.log(x.y.value); // 输出 123,要通过value拿原始值
这个方案完美实现了你想要的API,但需要注意访问原始值要通过.value,适合你有大量这类调用需求的场景。
3. TypeScript专属类型安全方案
如果你在用TypeScript,结合类型系统可以写出既简洁又安全的代码:
const getPropName = <T, K extends keyof T>(obj: T, prop: K): K => prop; // 使用示例 const x = { y: 123, z: 321 }; console.log(getPropName(x, 'y')); // 输出 'y'
TypeScript会自动检查你传入的'y'是不是x的合法属性,完全不用担心拼写错误,重构的时候也非常方便。
为什么不推荐你原来的方法?
你之前的Object.keys(x)[Object.values(x).indexOf(x.y)]有两个硬伤:
- 值重复时失效:比如
x = { y: 123, z: 123 },这个方法会返回'y',不管你要的是哪个属性 - 效率低下:每次都要遍历所有键和值,对象属性多的时候性能很差
总的来说,如果你只是偶尔需要获取属性名,箭头函数的方法最省心;如果一定要x.y.name()的调用方式,Proxy方案就能满足你。
内容的提问来源于stack exchange,提问作者Nick Russler
相关产品推荐
相关产品推荐

