背景作者:陈大鱼头
github: KRISACHAN
在最近的项目中,有这么一个功能点,就是要获取在WEB IDE里用户所写的注释中的一段特殊规则,然后解析成一段JS config 对象
例如:
//% width="100px" height="200px"
//% pos.top="50px" pos.left="50px"
//% writable=true
//% q.b.d.w.r.f=30 q.b.d.w.r.a=40
要转成
{
width: "100px",
height: "200px",
pos: {
top: "50px",
left: "50px"
},
writable: true,
q: {
b: {
d: {
w: {
r: {
f: 30,
a: 40
}
}
}
}
}
}
类似的规则
什么是递归
来自百度的解释:
递归:程序调用自身的编程技巧称为递归 (recursion)
尾递归:如果一个函数中所有递归形式的调用都出现在函数的末尾,我们称这个递归函数是尾递归的。
就是 复读机
递归怎么写?
一般
const fibonacci = num => (num === 1 ");
尾递归
const fibonacci = (num, total = 1) => (num === 0 ");
Array.reduce
const getArray = count => Array.from({ length: count }, (value, key) => key) const fibonacci = num => getArray(num).reduceRight((accumulator, currentValue) => accumulator * currentValue)功能实现 分步实现
过滤常规内容,获取特殊的备注信息,去除空格,并且转成数组
此时的数组内容为
[
"//% blockId="sloth_servo_write" block="set servo %channel|degree %degree"",
"//% advanced=true",
"//% weight=50",
"//% degree.min=0 degree.max=180",
"//% channel.fieldEditor="gridpicker" channel.fieldOptions.columns=4",
"//% a.b.c.d=20 a.b.c.e=222",
"//% q.b.d.w.r.f=30 q.b.d.w.r.a=40"
]
const code = `
//% width="100px" height="200px"
//% pos.top="50px" pos.left="50px"
//% writable=true
//% q.b.d.w.r.f=30 q.b.d.w.r.a=40`
// 获取特殊注释数组
const annotation_array_filter = annotation_item => annotation_item.indexOf("//%") >= 0;
// 去除特殊注释前后的空格
const annotation_array_remove_space = annotation_item => annotation_item.trim();
const annotation_array = code.split("
")
.filter(annotation_array_filter)
.map(annotation_array_remove_space)
遍历特殊规则数组,把每一项配置都压入一个新对象
此时的新对象内内容为
{
a.b.c.d: 20,
a.b.c.e: 222,
advanced: true,
block: "set servo %channel|degree %degree",
blockId: "sloth_servo_write",
channel.fieldEditor: "gridpicker",
channel.fieldOptions.columns: 4,
degree.max: 180,
degree.min: 0,
q.b.d.w.r.a: 40,
q.b.d.w.r.f: 30,
weight: 50,
}
const annotation_array_loop = annotation_item => {
// 把注释中的每一项配置转成对象
const result_forEach = result_item => {
let annotation_sub_object = {};
// 如果特殊注释数组中的每一项包含多个配置,则扁平化
const array_flattened = data => {
const is_array = (this.type(data) === "[object Array]");
const object_recursion = () => {
const [key, value] = data.split("=");
const annotation_sub_object = {};
try {
annotation_sub_object[key] = JSON.parse(value);
} catch (error) {
annotation_sub_object[key] = JSON.parse(value + """)
};
annotation_object = {
...annotation_object,
...annotation_sub_object
};
};
// 判断注释数组项中每一个元素是否有多个配置,如果有则递归,否则则注入对象
is_array ");" ") : result_item);
const result = annotation_item.replace("//% ", "")
.split("/" /g")
.map(result_map);
result_forEach(result);
};
let annotation_object = {}; // 承载每一个配置的对象
annotation_array.forEach(annotation_array_loop);
把数组里的元素转成对象
此时数组内容为
[
{
blockId: "sloth_servo_write"
},
{
advanced: true
},
...
]
let main_array = []; // 承载每一个配置的数组
const annotation_object_keys = Object.keys(annotation_object); // 获取扁平化后的注释对象的key
const annotation_object_keys_loop = annotation_object_key => { // 循环变量每一项注释
const annotation_object_key_array = annotation_object_key.split("."); // 把多级对象转成数组
const annotation_object_value = annotation_object[annotation_object_key]; // 获取每一项元素的值
let sub_object = {}; // 暂时承载配置对象的对象
const key_reduce = (accumulator, current_value, current_index, array) => { // key值递归,对每一项配置进行合并
if (current_index === 0) { // 如果当前遍历的元素为第一项,也就是说为配置的顶级对象,所以直接压入对象,并且输出
sub_object[current_value] = (current_index === array.length - 1 ");return sub_object[current_value];
}
accumulator[current_value] = {}; // 如果当前遍历的元素不为第一项,则当前对象元素变为对象
if (current_index === array.length - 1) { // 如果当前遍历的元素为数组最后一项,说明是配置对象最底的元素,可以直接赋值
accumulator[current_value] = annotation_object_value;
}
return accumulator[current_value];
};
let level_object = annotation_object_key_array.reduce(key_reduce, annotation_object_key_array[0]);
level_object = undefined; // 清空level_object
main_array.push(sub_object);
sub_object = undefined; // 清空sub_object
}
annotation_object_keys.forEach(annotation_object_keys_loop);
递归合并对象
此时的对象为
{
a: {b: {…}},
advanced: true,
block: "set servo %channel|degree %degree",
blockId: "sloth_servo_write",
channel: {fieldEditor: "gridpicker", fieldOptions: {…}},
degree: {min: 0, max: 180},
q: {b: {…}},
weight: 50
}
const annotation_tree = {};
const tree_data = (key, value, object) => { // 递归合并对象
if (this.type(value) !== "[object Object]") { // 如果当前传入元素为对象,则直接压入对象中
object[key] = value;
} else { // 否则继续递归
if (!object[key]) {
object[key] = {};
};
for (let item in value) {
tree_data(item, value[item], object[key]);
}
};
};
const main_array_forEach = item => { // 循环遍历配置数组
const key = Object.keys(item)[0];
const value = Object.values(item)[0];
tree_data(key, value, annotation_tree);
};
main_array.forEach(main_array_forEach);
main_array = undefined; // 清空main_array
完整代码
// 代码转换器
((wid, dcm) => {
"use strict";
const win = wid;
const doc = dcm;
// 基础信息
const base_info = {
"version": "0.0.1",
"author": "kris",
};
// 输出的函数
const funcs = {
annotation_parser (annotation) {
// 配置树初始化
this.annotation_tree = {};
// 获取特殊注释数组
const annotation_array_filter = annotation_item => annotation_item.indexOf("//%") >= 0;
// 去除特殊注释前后的空格
const annotation_array_remove_space = annotation_item => annotation_item.trim();
// 循环遍历特殊注释数组
const annotation_array_loop = annotation_item => {
// 把注释中的每一项配置转成对象
const result_forEach = result_item => {
let annotation_sub_object = {};
// 如果特殊注释数组中的每一项包含多个配置,则扁平化
const array_flattened = data => {
const is_array = (this.type(data) === "[object Array]");
const object_recursion = () => {
const [key, value] = data.split("=");
const annotation_sub_object = {};
try {
annotation_sub_object[key] = JSON.parse(value);
} catch (error) {
annotation_sub_object[key] = JSON.parse(value + """)
};
annotation_object = {
...annotation_object,
...annotation_sub_object
};
};
// 判断注释数组项中每一个元素是否有多个配置,如果有则递归,否则则注入对象
is_array ");" ") : result_item);
const result = annotation_item.replace("//% ", "")
.split("/" /g")
.map(result_map);
result_forEach(result);
};
let annotation_object = {}; // 承载每一个配置的对象
annotation.filter(annotation_array_filter)
.map(annotation_array_remove_space)
.forEach(annotation_array_loop);
let main_array = []; // 承载每一个配置的数组
const annotation_object_keys = Object.keys(annotation_object); // 获取扁平化后的注释对象的key
const annotation_object_keys_loop = annotation_object_key => { // 循环变量每一项注释
const annotation_object_key_array = annotation_object_key.split("."); // 把多级对象转成数组
const annotation_object_value = annotation_object[annotation_object_key]; // 获取每一项元素的值
let sub_object = {}; // 暂时承载配置对象的对象
const key_reduce = (accumulator, current_value, current_index, array) => { // key值递归,对每一项配置进行合并
if (current_index === 0) { // 如果当前遍历的元素为第一项,也就是说为配置的顶级对象,所以直接压入对象,并且输出
sub_object[current_value] = (current_index === array.length - 1 ");return sub_object[current_value];
}
accumulator[current_value] = {}; // 如果当前遍历的元素不为第一项,则当前对象元素变为对象
if (current_index === array.length - 1) { // 如果当前遍历的元素为数组最后一项,说明是配置对象最底的元素,可以直接赋值
accumulator[current_value] = annotation_object_value;
}
return accumulator[current_value];
};
let level_object = annotation_object_key_array.reduce(key_reduce, annotation_object_key_array[0]);
level_object = undefined; // 清空level_object
main_array.push(sub_object);
sub_object = undefined; // 清空sub_object
}
annotation_object_keys.forEach(annotation_object_keys_loop);
const tree_data = (key, value, object) => { // 递归合并对象
if (this.type(value) !== "[object Object]") { // 如果当前传入元素为对象,则直接压入对象中
object[key] = value;
} else { // 否则继续递归
if (!object[key]) {
object[key] = {};
};
for (let item in value) {
tree_data(item, value[item], object[key]);
}
};
};
const main_array_forEach = item => { // 循环遍历配置数组
const key = Object.keys(item)[0];
const value = Object.values(item)[0];
tree_data(key, value, this.annotation_tree);
};
main_array.forEach(main_array_forEach);
main_array = undefined; // 清空main_array
},
};
// 引用的资源
const libs = {};
// 工具函数
const tools = {
// 获取元素类型
type (object) {
return Object.prototype.toString.call(object);
},
// 分离传入的代码跟配置
separate_code_and_config (data) {
data.split("
")
.forEach(item => {
item.indexOf("//%") >= 0 ");return this || (0, eval)("this");
})();
if (typeof module !== "undefined" && module.exports) {
module.exports = code_transformer;
} else if (typeof define === "function" && define.amd) {
define([], function () {
return code_transformer;
});
} else {
!("code_transformer" in _global) && (_global.code_transformer = code_transformer);
};
})(window, document);
备注:函数体积好大呀,但这只是业务里的一个小小小功能,流下了不会优化代码的泪水~
如果你、喜欢探讨技术,或者对本文有任何的意见或建议,你可以扫描下方二维码,关注微信公众号“鱼头的Web海洋”,随时与鱼头互动。欢迎!衷心希望可以遇见你。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/6931.html
摘要:手头做的项目开发得差不多了,而打包配置是一开始粗略配置的,不大的项目打包出来得,所以现在必须进行优化。用于生产环境的打包,设置其为后,这些库会提供最小体积的文件。这种情况打包后的体积要更小一些。最后打包结果的体积开销主要就是以上几项。 手头做的项目开发得差不多了,而打包配置是一开始粗略配置的,不大的项目打包出来得6MB+,所以现在必须进行优化。 打包结果分析 执行命令 webpack ...
摘要:之所以在本地构建,而没有使用仓库的,是因为,我们的镜像采用了国内阿里云的源,再加上某些很奇妙的网络因素,在中自动构建时,升级总会失败。然而,在本地再次构建成功。 见字如晤。 前段时间,Node.js 官方发布了Node 8.9.3 LTS版本,并且官网首页提示新版本有重要安全更新,Important security releases, please update now! ,然后我立...
阅读 3264·2021-11-23 09:51
阅读 2383·2021-11-09 09:46
阅读 1445·2019-08-30 15:54
阅读 3076·2019-08-30 14:22
阅读 2889·2019-08-29 12:40
阅读 1590·2019-08-26 10:33
阅读 1746·2019-08-23 17:09
阅读 1521·2019-08-23 16:11