Inchill / fe-notes

🍎个人前端知识汇总,并且处于持续更新中。 This is a summary of personal front-end knowledges, and it will be updated from time to time.
69 stars 1 forks source link

实现一个插值表达式处理函数 #40

Open Inchill opened 1 year ago

Inchill commented 1 year ago
// 请实现一个 render 函数:该函数接受两个参数,一个是模版,一个是数据对象。
// 模版通过插值表达式引用数据对象上的数据,通过 render 函数能够把模版转换为真实的字符串。

let template = '大家好,我的名字叫做{{ name }},我是一名{{ job }}。'

let data = {
  name: 'Henry',
  gender: 'male',
  job: 'software engineer'
}

function render (template = '', data) {
  if (data === null) return

  const MUSTACHE_REG = /\{\{.*?\}\}/ig
  const KEY_REG = /(?<=\{\{(\s*?)).*?(?=(\s*?)\}\})/ig

  return template.replace(MUSTACHE_REG, function (match = '') {
    let key = match.match(KEY_REG)[0].trim()
    return data[key]
  })
}

console.log(render(template, data)) // 大家好,我的名字叫做Henry,我是一名software engineer。
Inchill commented 1 year ago

上述方案存在一个问题,那就是如果我想在插值表达式里执行一些运算逻辑,那么就会报错。如果要执行一个字符串,第一时间想到的是 eval 函数,但是这仍然存在一个问题,就是 eval 执行字符串的时候,里面的变量上下文该如何获取?这时候就可以使用 with 函数,把 data 传递进去,这样就避免了访问数据时还得加上 data. 前缀。

let template = "大家好,我的名字叫做${ name.includes('H') ? name : '' },我是一名${ job },今年${ age[1] }岁。"

let data = {
  name: 'Henry',
  gender: 'male',
  job: 'software engineer',
  age: [24, 25]
}

function render (template = '', data) {
  if (data === null) return

  const MUSTACHE_REG = /\$\{.*?\}/ig
  const KEY_REG = /(?<=\$\{(\s*?)).*?(?=(\s*?)\})/ig

  return template.replace(MUSTACHE_REG, function (match = '') {
    let content = match.match(KEY_REG)[0].trim()
    let result
    with (data) {
      result = eval(content)
    }
    return result
  })
}

console.log(render(template, data)) // 大家好,我的名字叫做Henry,我是一名software engineer,今年25岁。
Inchill commented 1 year ago

第二种方案其实仍然不够好,原因就是 eval 存在安全隐患,不推荐继续使用它。能够想到的替代方案就是 new Function,把需要执行的代码拼接成字符串:

let template = "大家好,我的名字叫做 ${ name.includes('H') ? name : '' },我是一名 ${ job },今年 ${ age[1] } 岁。"

let data = {
  name: 'Henry',
  gender: 'male',
  job: 'software engineer',
  age: [24, 25]
}

function render (template = '', data) {
  if (data === null) return

  const MUSTACHE_REG = /\$\{.*?\}/ig
  const KEY_REG = /(?<=\$\{(\s*?)).*?(?=(\s*?)\})/ig

  return template.replace(MUSTACHE_REG, function (match = '') {
    let content = match.match(KEY_REG)[0].trim()
    let result = new Function('obj', 'with (obj) { return ' + content + '}')(data)
    return result
  })
}

console.log(render(template, data)) // 大家好,我的名字叫做 Henry,我是一名 software engineer,今年 25 岁。