Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
Inchill opened this issue Sep 19, 2022 · 2 comments
Open

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

Inchill opened this issue Sep 19, 2022 · 2 comments

Comments

@Inchill
Copy link
Owner

Inchill commented Sep 19, 2022

// 请实现一个 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
Copy link
Owner Author

Inchill commented Oct 14, 2022

上述方案存在一个问题,那就是如果我想在插值表达式里执行一些运算逻辑,那么就会报错。如果要执行一个字符串,第一时间想到的是 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
Copy link
Owner Author

Inchill commented Oct 14, 2022

第二种方案其实仍然不够好,原因就是 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 岁。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant