如何在标签模板字面量的tag函数内获取使用的变量属性名?
方案总结
JavaScript运行时原生没有提供标签模板函数获取插值表达式源码的能力,以下是三种可落地的实现方式:
方案1:运行时解析调用方函数源码
利用你发现的函数.toString()可看到插值表达式的特性,在tag函数内部主动获取调用栈,找到调用tag的函数,解析源码提取对应位置的插值表达式:
function tag(literals, ...vars) { // 获取调用栈 const stack = new Error().stack.split('\n') // 找到调用tag的函数行,不同浏览器栈格式略有差异,可自行适配 const callerLine = stack[2] // 一般第0行是Error本身,第1行是当前tag函数,第2行是调用方 // 提取调用方函数名,或直接拿到函数引用 const callerFuncName = callerLine.match(/at (\w+)/)?.[1] const callerFunc = window[callerFuncName] // 浏览器环境,node环境可对应调整 if (callerFunc) { const funcStr = callerFunc.toString() // 解析模板字符串的插值部分,匹配tag`xxx${xxx}xxx`里的${}内容 const interpolations = funcStr.match(/tag`[^`]*\$\{([^}]+)\}[^`]*`/)?.slice(1) console.log('插值表达式:', interpolations) // 这里就能拿到 ['data.name'] } // 原有逻辑 console.log(literals.raw) console.log(vars) }
限制
- 仅适用于未压缩、未混淆的源码,代码压缩后变量名、函数名会被改写,解析会失效
- 调用栈解析逻辑需要适配不同运行环境(浏览器/Node/小程序等)
- 性能较低,不适合高频调用的场景
方案2:编译时注入表达式源码
通过Babel插件在编译阶段收集所有标签模板的插值表达式源码,作为额外参数传入tag函数,是生产环境可用的最优方案:
示例编译效果:
// 源码 tag`Test ${data.name}` // 编译后 tag(["Test ", ""], [data.name], ["data.name"])
你可以自己实现简单的Babel插件,遍历TaggedTemplateExpression节点,收集所有插值的expression的源码字符串,作为第三个参数传入即可。
优势
- 运行时无额外性能损耗
- 不受代码压缩混淆影响
限制
- 需要修改项目编译配置
方案3:插值表达式包裹标记函数
不需要修改编译配置也不需要解析源码,仅需要调整插值的写法,用自定义函数包裹需要获取属性名的表达式:
// 标记函数,返回包含值和属性名的结构体 const $ = (obj, prop) => { return { value: obj[prop], propName: prop } } function tag(literals, ...vars) { vars.forEach(item => { if (item.propName) { console.log('属性名:', item.propName) // 拿到 name console.log('值:', item.value) // 拿到 data.name 的值 } }) } // 使用时写法调整为 function myfN() { return tag`Test ${$(data, 'name')}` }
优势
- 逻辑简单可靠,无环境依赖
- 运行时性能稳定
限制
- 需要调整原有插值的写法,不能直接写
${data.name}
内容的提问来源于stack exchange,提问作者inux
相关产品推荐
相关产品推荐

