一道测试题

阿里的一道测试题(群里的小伙伴提供的),题目大概这样:

https://static.surmon.me/17-4-11/45532373-file_1491845773280_4863.png

大概就是让写个简单的模板解析器,尝试了好多种解构的方法都没成功...

先思考,下面就是答案了!

问题代码:

              
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
var greeting = 'My name is ${name}, age ${age}, I am a ${job.jobName}'; var employee = { name: 'XiaoMing', age: 11, job: { jobName: 'designer', jobLevel: 'senior' } }; var result = greeting.render(employee); console.log(result);

解决方案一(绝大多数的解决方案-正则):

              
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
// 你可以趁机想想还有没有其他解法,时间充裕 // 代码参考自 @剪影boy + @Zhongsheng String.prototype.render = function(obj) { return this.replace(/\$\{(\w+|\w+\.\w+)\}/g, match => { var keys = match.replace('${', '').replace('}', '').split('.') return keys.reduce((acc, cv) => acc[cv], obj) }) }

解决方案二(字符串模板):

              
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
// 这办法是我自己想到的,有点笨 String.prototype.render = function(obj) { // 利用了ES6的解构、对象keys新方法,在函数内部解构并自动展开变量 eval(`var {${Object.keys(obj).join(',')}} = obj`) // 利用eval使字符串直接作为ES6解析 return eval('`' + this + '`') }

方案二是后来不断尝试得到的

拿键声明变量 => 拒绝循环所以用keys => 数组转成字符串再组合对象字符 => eval去构造解构 => OK!

对JS又爱出了新高度😂 six six six!

解决方案三(With函数):

              
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
// 代码由 掘金@一口怪兽一口烟 提供 String.prototype.render = function (obj) { with(obj) { return eval('`' + this + '`') } }

莫非是史上最优解?😂double click six six six!

期待更多的解法

字符串模板支持函数变量表达式,纯天然的模板解析器,如果是Mustache范儿,解析前replace下就行了

不再补充明细


去控制台跑下试试吧

随时欢迎补充!

相关学习资料:

本文于 2017/4/11 上午 发布在 Code 分类下,当前已被围观 3183 次

相关标签:Web开发Javascript

永久地址:https://surmon.me/article/32

版权声明:自由转载-署名-非商业性使用  |  Creative Commons BY-NC 3.0 CN
  • lihs
    LihsAndroidQQ浏览器 | 6.2CN - Tianjin#142

    套总威武👏👏👏

  • Surmon
    SurmonMac OS XChrome | 57CN - Xian#143

    回复 #142 @lihs

    v587💊💊💊

  • kevin
    KevinMac OS XChrome | 57CN - Beijing#144

    我还以为要手工实现字符串模板呢....,原来直接evel调用就可以了

  • wan
    WanMac OS XChrome | 57CN - Shanghai#145

    with 语句是运行缓慢的代码块,尤其是在已设置了属性值时。大多数情况下,如果可能,最好避免使用它

  • Surmon
    SurmonMac OS XChrome | 57CN - Xian#146

    回复 #145 @wan

  • 剪影Boy
    剪影boyLinuxChrome | 57CN - Suzhou#147

    正则的一种实现:

                  
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    String.prototype.render = function(obj) { return this.replace(/\${(\w+?\.?\w+?)}/g, function(match, $1) { if ($1.includes('.')) { var arr = $1.split('.'); return obj[arr[0]][arr[1]]; } else { return obj[$1]; } }); }
  • Surmon
    SurmonMac OS XChrome | 57CN - Xian#148

    回复 #147 @剪影Boy

    应该用递归取数据哈,不然再多层对象就没法解释啦🌹

  • zhongsheng
    ZhongshengWindows 7Chrome | 56CN - Wuhan#149
                  
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    String.prototype.render = function(employee) { var str = this + ''; return str.replace(/\$\{(\w+|\w+\.\w+)\}/g, function(match) { var key = match.replace('${', '').replace('}', ''); if (/\./g.test(key)) { var key = key.split('.'); var value = employee; for( var i =0; i<key.length; i++ ){ value = value[key[i]] } return value; } else { return employee[key] } }) }
  • Surmon
    SurmonMac OS XChrome | 57CN - Xian#150

    回复 #149 @zhongsheng

    送花🌻

  • Surmon
    SurmonMac OS XChrome | 57CN - Xian#151

    回复 #149 @zhongsheng

    最终优化为

                  
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    String.prototype.render = function(obj) { return this.replace(/\$\{(\w+|\w+\.\w+)\}/g, match => { var keys = match.replace('${', '').replace('}', '').split('.') return keys.reduce((accumulator, currentValue) => accumulator[currentValue], obj) }) }
  • hufei
    HufeiWindows 7Chrome | 57CN#152

    还可以在加上多级的情况,现在最多只能匹配到xx.xx

  • hufei
    HufeiWindows 7Chrome | 57CN#153

    回复 #152 @hufei

    /\${((\w+.)*\w+)}/g.test('${a.b.c.d}')

  • Vace
    VaceMac OS XChrome | 57CN - Hefei#154

    😂 随便写了一下,可以继续扩展parseExpression 函数,支持管道或者算术运算之类的。 评论上限2000字符... 源码在:gist

                  
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    function renderParser(a, b) { function c(p) { return 0 > p || p >= i ? !1 : a[p].charCodeAt() } function d(p) { return p.split('.').reduce(function(q, r) { return q = q[r] || '', 'function' == typeof q && (q = q.call(b)), q }, b) } for (var k, f = 123, i = a.length, j = 0, l = '', m = '', n = !1, o = ''; j < i;) k = c(j), k === 36 && c(j + 1) === f ? n = !0 : n && k === 125 ? (m = d(l), l = '', n = !1, o += m) : n ? k != 32 && k != f && (l += a[j]) : o += a[j], j++; return o } String.prototype.render = function(a) { return renderParser(this, a) };
  • Surmon
    SurmonMac OS XChrome | 57CN - Xian#155

    回复 #154 @Vace

    Six! Six! Six! 🌹

  • Andy
    AndyLinuxChrome | 58CN - Beijing#180

    不错啊

  • Surmon
    SurmonMac OS XChrome | 57CN - Xian#181

    回复 #180 @Andy

    🌻

  • marsoln
    MarsolnWindows 10Chrome | 57CN - Beijing#219

    那个with eval不要太吊 跪了

                  
    • 1
    • 2
    • 3
    String.prototype.render = function (ctx) { return this.replace(/\x24\x7b([\w\.]+)\x7d/ig, (_, key) => key.split('\.').reduce((obj, attr) => obj[attr], employee)) }
  • Surmon
    SurmonMac OS XChrome | 58CN - Xian#220

    回复 #219 @marsoln

    666🌚🌹

  • marsoln
    MarsolnWindows 10Chrome | 57CN - Beijing#221

    回复 #219 @marsoln

    更正一下

                  
    • 1
    • 2
    • 3
    String.prototype.render = function (ctx) { return this.replace(/\x24\x7b([\w\.]+)\x7d/ig, (_, key) => key.split('\.').reduce((obj, attr) => obj[attr], ctx)) }
  • marsoln
    MarsolnWindows 10Chrome | 57CN - Beijing#222

    回复 #220 @Surmon

    你的博客太酷了呀:sunglasses:

  • Surmon
    SurmonMac OS XChrome | 58CN - Xian#226

    回复 #222 @marsoln

    一起Cool!😂

  • 子浪
    子浪Windows 7Chrome | 57CN - Guangzhou#247

    酷炫的博客。。

  • Surmon
    SurmonMac OS XChrome | 58CN - Xian#248

    回复 #247 @子浪

    开不开心?满不满意?惊不惊喜?