目录
- 一、介绍
- 二、准备
- 三、目标
- 四、代码
- 五、完成
一、介绍
JSON 已经是大家必须掌握的知识点,JSON 数据格式为前后端通信带来了很大的便利。在开发中,前端开发工程师可以借助于 JSON 生成器快速构建一个 JSON 用来模拟数据。
本题请你开发一个简易的 JSON 生成器工具,使它能根据模板生成对应格式的 JSON 。
二、准备
开始答题前,需要先打开本题的项目代码文件夹,目录结构如下:
├── css
├── index.html
└── js
    ├── highlight.min.js
    └── index.js
其中:
- css 是样式文件夹。
- index.html 是主页面。
- js/highlight.min.js 是 json 格式化文件。
- js/index.js 是需要补充代码的 js 文件。
在浏览器中预览 index.html 页面效果如下:
 
三、目标
请在 index.js 文件中补全 generateData 函数代码,并最终返回一个 js 对象(说明 : generateData 生成的数据会由插件自动转化成 JSON)。
在左侧的输入框中输入指定格式的数据模板,点击生成 JSON 按钮,右侧会自动生成对应格式的 JSON 数据。
- 数据模板中对象的 key 对应的 value 如果是 {{}} 并且符合下述规则,则根据下述规则解析,否则一律返回原始 value 值。具体规则如下: 
  - {{bool()}} 表示随机生成布尔值。
- {{integer(n, m)}} 表示生成 n-m 之间(包含 n、m )的随机整数(注意:n<m)。
 
 附目标 1 测试用例:
 
{
  isPass: '{{bool()}}',
  age: '{{integer(3, 5)}}',
  goodsNumber:2,
  isShow:false,
  tag:'phone',
  fn:'{{integer}}'
}
- 数据模板中 {{repeat()}}(此项只会出现在数组首位)表示重复生成数组中的数据,如:“repeat(5, 7)” 则表示随机生成 5-7 条数组数据,repeat 中值只包含一个数字,如"repeat(5)" 表示生成 5 条数组数据。
  
 附目标 2 测试用例(3 组):
// (1)随机生成 `2-5` 条数组数据
[
  "{{repeat(2, 5)}}",
  {
    isActive: "{{bool()}}",
    age: "{{integer(20, 40)}}",
    num: 2,
    boolean: true,
    str: "str",
    isTel: "{{bool}}",
    fn: "{{fn()}}",
  },
]
// (2)固定生成 `7` 条数组数据
[
  ("{{repeat(7)}}",
  {
    isTrue: "{{bool()}}",
    score: "{{integer(3, 7)}}",
    tag: "android",
    isSamll: true,
    fn: "{{fn()}}",
  })
]
// (3)无 repeat 的情况
[
  { maxNum: 10 }
];
注意:本题不考虑用户输入和传参不合法的情况,只处理合法的数据格式即可,实际测试中 key 和 value 为非固定值。提供的测试用例仅为方便测试代码使用,实际使用中需要对所有符合要求的数组/对象结构的模板生效。
四、代码
index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>JSON 生成器</title>
    <link rel="stylesheet" href="./css/default.min.css" />
    <script src="./js/highlight.min.js"></script>
    <script>
      hljs.highlightAll();
    </script>
  </head>
  <body>
    <div class="btnbox" id="btnbox">
      <button class="btn" onclick="generate()">生成json</button>
    </div>
    <div class="con">
      <div class="left">
        <textarea id="myTextarea" name="myTextarea"> </textarea>
      </div>
      <div>
        <pre>
            <code id="code " class="language-js "></code>
            </pre>
      </div>
    </div>
    <script>
      let myTextarea = document.getElementById("myTextarea");
      myTextarea.innerHTML = `[
  '{{repeat(2, 3)}}',
  {
    isActive: '{{bool()}}',
    age: '{{integer(20, 40)}}',
    num:2,
    boolean:true,
    str:'str',
    isTel:'{{bool}}',
    fn:'{{fn()}}'
   }
]`;
      let code = document.getElementById("code ");
      let lastData;
      // 点击生成按钮生成对应的 JSON
      function generate() {
        let str = myTextarea.value.replace(/\s*/g, "");
        var jsonData = new Function("return" + str)();
        lastData = generateData(jsonData);
        let jsonCode = JSON.stringify(lastData, null, 2); // 设置 tab 为两个空格
        code.innerHTML = jsonCode;
        hljs.highlightAll();
      }
    </script>
    <script src="./js/index.js "></script>
  </body>
</html>
js/index.js
/*
 * @param {*}  左侧输入框输入的值转化成的 js 数据
 * @return {*} 根据传入的数据生成对应的 js 格式数据
 */
let generateData = (data) => {
  // TODO:待补充代码
};
module.exports = { generateData };
css/default.min.css
/*!
  Theme: Default
  Description: Original highlight.js style
  Author: (c) Ivan Sagalaev <maniac@softwaremaniacs.org>
  Maintainer: @highlightjs/core-team
  Website: https://highlightjs.org/
  License: see project LICENSE
  Touched: 2021
*/
* {
  margin: 0;
  padding: 0;
}
.con {
  display: flex;
  width: 90%;
  margin: 0 auto;
  justify-content: space-between;
}
.con div {
  width: 48%;
}
.btnbox {
  width: 100%;
  text-align: center;
}
.btn {
  display: inline-block;
}
.left {
  margin-top: 20px;
}
pre code.hljs {
  display: block;
  overflow-x: auto;
  padding: 1em;
}
code.hljs {
  padding: 3px 5px;
}
.hljs {
  background: #f3f3f3;
  color: #444;
}
.hljs-comment {
  color: #697070;
}
.hljs-punctuation,
.hljs-tag {
  color: #444a;
}
.hljs-tag .hljs-attr,
.hljs-tag .hljs-name {
  color: #444;
}
.hljs-attribute,
.hljs-doctag,
.hljs-keyword,
.hljs-meta .hljs-keyword,
.hljs-name,
.hljs-selector-tag {
  font-weight: 700;
}
.hljs-deletion,
.hljs-number,
.hljs-quote,
.hljs-selector-class,
.hljs-selector-id,
.hljs-string,
.hljs-template-tag,
.hljs-type {
  color: #800;
}
.hljs-section,
.hljs-title {
  color: #800;
  font-weight: 700;
}
.hljs-link,
.hljs-operator,
.hljs-regexp,
.hljs-selector-attr,
.hljs-selector-pseudo,
.hljs-symbol,
.hljs-template-variable,
.hljs-variable {
  color: #ab5656;
}
.hljs-literal {
  color: #695;
}
.hljs-addition,
.hljs-built_in,
.hljs-bullet,
.hljs-code {
  color: #397300;
}
.hljs-meta {
  color: #1f7199;
}
.hljs-meta .hljs-string {
  color: #38a;
}
.hljs-emphasis {
  font-style: italic;
}
.hljs-strong {
  font-weight: 700;
}
.btn,
.btn-all {
  margin-top: 10px;
  display: inline-block;
  color: #0099cc;
  background: #2e7eee;
  border-radius: 10px;
  text-decoration: none;
  text-transform: uppercase;
  border: none;
  color: white;
  padding: 8px 16px;
  text-align: center;
  text-decoration: none;
  display: inline-block;
  font-size: 16px;
  transition-duration: 0.4s;
  cursor: pointer;
}
#myTextarea {
  width: 100%;
  height: 500px;
}
五、完成
index.js
// 定义正则
let reg = /^\{\{(.+)\}\}$/; // 匹配{{..}}
let repeatReg = /^\{\{repeat\(\d+(,\d+)?\)\}\}$/; // 匹配{{repeat(n)}}和{{repeat(n,m)}}
let boolReg = /^\{\{bool\(\)\}\}$/; // 匹配{{bool()}}
let integerReg = /^\{\{integer\(\d+,\d+\)\}\}$/; // 匹配{{integer(n,m)}}
let objReg = [boolReg, integerReg]; // 对象里会用到的所有正则
// 判断value是否符合objReg中的规则
function isConformObjReg(value) {
  return objReg.find((r) => r.test(value));
}
// 随机生成布尔值的函数
function bool() {
  return Math.random() < 0.5 ? true : false;
}
// 生成n到m之间的随机整数(包含n和m)
function integer(n, m) {
  return Math.floor(Math.random() * (m - n + 1) + n);
}
// 获得需要重复的次数,次数在n到m之间,没传递m时次数为n
function repeat(n, m) {
  if (!m) return n;
  return integer(n, m);
}
/*
 * @param {*}  左侧输入框输入的值转化成的 js 数据
 * @return {*} 根据传入的数据生成对应的 js 格式数据
 */
let generateData = (data) => {
  // TODO:待补充代码
  if (typeof data !== "object") {
    // 如果data是基础数据类型,不做处理
    return data;
  }
  // 到这里说明data一定是object类型的
  let result; // 存放结果
  if (Array.isArray(data)) {
    // 如果data是数组
    result = [];
    if (repeatReg.test(data[0])) {
      // 数组第一项是{{repeat(...)}}
      // 获得需要重复的次数
      const nums = eval(reg.exec(data[0])[1]);
      for (let i = 0; i < nums; i++) {
        // 以数组第二项为基准生成数据,递归调用generateData
        result[i] = generateData(data[1]);
      }
    } else {
      // 数组第一项不是{{repeat(...)}},则直接遍历数据对数组每一项进行generateData处理
      for (let i = 0; i < data.length; i++) {
        result[i] = generateData(data[i]);
      }
    }
  } else {
    // 如果data不是数组那就是对象
    result = {};
    for (const key in data) {
      const value = data[key];
      if (isConformObjReg(value)) {
        // 属性值是{{bool()}}或{{integer(...)}}
        // 获得{{和}}之间的匹配的结果,如:"bool()"或"integer(...)""
        const fnStr = reg.exec(value)[1];
        // 用eval将fnStr字符串当作js代码来运行,如果fnStr为"bool()"则 eval(fnStr)代表运行bool函数
        result[key] = eval(fnStr);
      } else if (typeof value === "object") {
        // 属性值不符合对象允许的正则规则并且属性值是object类型,则递归调用generateData
        result[key] = generateData(value);
      } else {
        // 属性值不符合对象允许的正则规则并且属性值不是object类型,则属性值是基础数据类型,不做处理
        result[key] = value;
      }
    }
  }
  return result;
};
module.exports = { generateData };


















