Sunny-117 / js-challenges

✨✨✨ Challenge your JavaScript programming limits step by step

Home Page:https://juejin.cn/column/7244788137410560055

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

正则表达式模版字符串

Sunny-117 opened this issue · comments

String.prototype.render = function (data) {
    return this.replace(/{{[.\s\S]*?}}/g, match => {
        if ((match = match.substring(2, match.length - 2).trim()) == "") {
            return "";
        } else if (match.startsWith("#")) {
            return eval(match.substr(1, match.length - 1));
        } else {
            return data[match] ? data[match] : "";
        }
    })
}


const data = {
    name: "小明",
    age: 16,
    school: "第三中学",
    classroom: "教室2"
}


console.log(
    "{{ name }} 今年 {{ age }} 岁,就读于 {{ school }} 今天在 {{ classroom }} 上课,{{ name }} {{ #data.age >= 18 ? '成年了' : '未成年' }}".render(data)
);
// 小明 今年 16 岁,就读于 第三中学 今天在 教室2 上课,小明 未成年

console.log(
    `{{name}}说了句{{#
        if (data.age >= 18) {
            "我已经成年了!"
        } else {
            "我还没有成年!"
        }
    }}`.render(data)
);
// 小明说了句我还没有成年!
function createTemplate(template = '', option = {}) {
  const { dynamic = ['{{', '}}'], statement = '#' } = option
  const RE_CONTENT = '[.\\s\\S]*'
  const RE_DYNAMIC = new RegExp(
    `(${RE_CONTENT}?)${dynamic[0]}(${RE_CONTENT}?)${dynamic[1]}(${RE_CONTENT})`
  )
  const tokens = []
  const createTokens = (tpl) => {
    const match = tpl.match(RE_DYNAMIC)
    if (match) {
      // ['', text, dynamic, nexTemplate] = match
      match[1] && tokens.push({
        type: 'text',
        content: match[1],
      })
      const dynamicMatch = match[2].trim()
      dynamicMatch && tokens.push({
        type: dynamicMatch.startsWith(statement) ? 'statement' : 'dynamic',
        content: dynamicMatch,
      });
      match[3] && createTokens(match[3])
    } else {
      tokens.push({
        type: 'text',
        content: tpl,
      })
    }
  }

  createTokens(template)

  return function format(data) {
    return tokens.map(({ type, content }) => {
      switch (type) {
        case 'text':
          return content
        case 'dynamic':
          return data[content]
        case 'statement':
          const fn = new Function(...Object.keys(data), content.slice(1))
          return fn(...Object.values(data))
      }
    }).join('')
  }
}

const template = createTemplate(`
  名字: {{ name }}
  {{}}
  年龄: {{ age }}
  {{#
    if (age > 18) {
      return '成年人' 
    } else { 
      return '未成年人' 
    }
  }}
`)

console.log(template({ name: "张三", age: 14 }))
console.log(template({ name: "李四", age: 19 }))
commented

补一个双指针的写法,效率可能高一点。
好奇题目的模板字符串的含义,不应该是 es6 标准的${}这种字符串吗?

const data = {
  name: "小明",
  age: 16,
  school: "第三中学",
  classroom: "教室2",
};
// 解构取值
let { name, age, school, classroom } = data;

/**
 * @description: 双指针匹配 模板字符串,发现${}
 * @param {*} s
 * @return {*}
 * @author: jlx
 */
function matchStrV2(s) {
  let res = "";
  for (let i = 0; i < s.length - 1; i++) {
    let j = i + 2;
    if (s[i] == "$" && s[i + 1] == "{") {
      // 直到 右侧括号匹配成功
      while (j < s.length && s[j] != "}") j++;
      // 说明匹配成功
      if (s[j] == "}") {
        res += eval(s.substring(i + 2, j));
      }
      i = j + 1;
    } else {
      res += s[i];
    }
  }
  return res;
}

let cases =
  "${ name } 今年 ${ age } 岁,就读于 ${ school } 今天在 ${ classroom } 上课,${ name } ${ data.age >= 18 ? '成年了' : '未成年' }";

console.log(matchStrV2(cases));
// 小明今年 16岁,就读于 第三中学今天在 教室2上课,小明未成年

// 如果是es6标准的模板字符串``,第二个case好像是非法的
commented
String.prototype.render = function (data) {
    return this.replace(/{{[.\s\S]*?}}/g, match => {
        if ((match = match.substring(2, match.length - 2).trim()) == "") {
            return "";
        } else if (match.startsWith("#")) {
            return eval(match.substr(1, match.length - 1));
        } else {
            return data[match] ? data[match] : "";
        }
    })
}


const data = {
    name: "小明",
    age: 16,
    school: "第三中学",
    classroom: "教室2"
}


console.log(
    "{{ name }} 今年 {{ age }} 岁,就读于 {{ school }} 今天在 {{ classroom }} 上课,{{ name }} {{ #data.age >= 18 ? '成年了' : '未成年' }}".render(data)
);
// 小明 今年 16 岁,就读于 第三中学 今天在 教室2 上课,小明 未成年

console.log(
    `{{name}}说了句{{#
        if (data.age >= 18) {
            "我已经成年了!"
        } else {
            "我还没有成年!"
        }
    }}`.render(data)
);
// 小明说了句我还没有成年!

面试会考正则吗,完全不会

function renderTemplate(template, data) {
  const regex = /{{(.*?)}}/g;
  return template.replace(regex, (match, key) => {
    if (key.startsWith("#")) {
      return eval(key.substr(1));
    } else {
      return data[key.trim()] || "";
    }
  });
}
commented
/**
     *遇到#表示是表达式
     *遇到字符表示变量 
     */
    String.prototype.render = function (data) {
        return this.replace(/{{[\s\S]*?}}/g, match => {
            match = match.slice(2, match.length - 2).trim();
            // console.log(match)
            if (match == "") {
                return;
            } else if (match[0] == '#') {
                return eval(match.slice(1));
            } else {
                return data[match] || '';
            }
        })
    }
    const data = {
        name: "小明",
        age: 16,
        school: "第三中学",
        classroom: "教室2"
    }


    console.log(
        "{{ name }} 今年 {{ age }} 岁,就读于 {{ school }} 今天在 {{ classroom }} 上课,{{ name }} {{ #data.age >= 18 ? '成年了' : '未成年' }}".render(data)
    );
    // 小明 今年 16 岁,就读于 第三中学 今天在 教室2 上课,小明 未成年

    console.log(
        `{{name}}说了句{{#
        if (data.age >= 18) {
            "我已经成年了!"
        } else {
            "我还没有成年!"
        }
    }}`.render(data)
    );
        // 小明说了句我还没有成年!

索性把正则写完整一些,少一些字符串操作吧

function resolveStr(tpl, model) {
  const reg = /{{\s*(.+?)\s*}}/g
  return tpl.replace(reg, (str, key) => {
    if (key.startsWith('#')) {
      key = key.slice(1)
      return eval(key)
    }
    console.log(str, key)
    return eval(`model.${key}`)
  })
}
String.prototype.render = function (data) {
    return this.replace(/{{\s*([\S\s]*?)\s*}}/g, (match, p1) => {
        if (p1 && p1[0] === '#') return eval(p1.slice(1))
        else return data[p1] || ''
    })
}