代码要写成别人看不懂的样子(十九)

本篇文章参考书籍《JavaScript设计模式》–张容铭

前言

  大家有没有遇到过操作大量节点的情况,比如我们在页面上想要创建一个展示区域,这块区域,需要跟不同的选项来展示不同结构的内容,那我们就需要根据选项来操作不同的节点。首先创建节点,然后创建节点内容,将节点内容插入到创建的节点中,最后将节点插入到父节点中。

  这一套操作下俩,一个节点就需要 4 步,如果节点比较多,那这性能消耗就有点遭不住了。

  为了解决这一个问题,我们可以使用简单模板方法。如果各位接触过模板字符串,那学起来易如反掌。

简单模板模式

  通过格式化字符串拼凑出视图避免创建视图时大量节点操作,优化内存开销

  举个例子,我们规定一个展示区容器,用来展示不同的信息。

//命名空间 单体对象
var A = A || [];
//主体展示容器区
A.root = document.getElementById('container');
//创建视图方法集合
A.strategy = {
	'listPart': function() {},
	'codePart': function() {},
	'onlyTitle': function() {},
	'guide': function() {}
	//.......
}
//创建视图入口
A.init = function(data) {
	//根据传输的试图类型创建视图
	this.strategy[data.type](data);
}

  接下来是实现 strategy 中的每一种创建算法。(下面代码粗略扫一眼就行)

//文字列表展示
'listPart': function(data) {
	var s = document.createElement('div'),          //模块容器
		h = document.createElement('h2'),           //标题容器
		p = document.createElement('p'),            //描述容器
		ht = document.createTextNode(data.data.h2), //标题内容
		pt = document.createTextNode(data.data.p),  //描述内容
		ul = document.createElement('ul'),          //列表容器
		ldata = data.data.li,                       //列表数据
		//li 列表项容器, strong 列表项标题,span 列表项解释,t 列表项标题内容, c 列表项解释内容
		li, strong, span, t, c;
	//有 id 设置模块 id
	data.id && (s.id = data.id);
	s.className = 'part'; //设置模块类名
	h.appendChild(ht);    //将标题内容放到标题容器
	p.appendChild(pt);    //将描述内容放到描述容器
	s.appendChild(h);     //将标题容器插入模块容器
	s.appendChild(p);     //将描述容器插入模板容器
	//遍历列表数据
	for(var i = 0, len = ldata.length; i < len; i++) {
		li = document.createElement('li');            //创建列表项容器
		strong = document.createElement('strong');    //创建列表项标题容器
		span = document.createElement('span');        //创建列表项解释容器
		t = document.createTextNode(ldata[i].strong); //创建列表项标题内容
		c = document.createTextNode(ldata[i].span);   //创建列表项解释内容
		strong.appendChild(t);       //向列表项标题容器中插入标题内容
		span.appendChild(c);         //向列表项解释容器中插入解释类容
		li.appendChild(strong);      //向列表项中插入列表项标题
		li.appendChild(span);        //向列表项中插入列表项解释
		ul.appendChild(li);          //向列表项中插入列表项
	}
	s.appendChild(ul);     //向模块中插入列表
	A.root.appendChild(s); //展现模块
}

  大家有没有觉得上面的代码,阅读起来总是看串行,虽然整整齐齐,但是看着就头大。这就是我们一开始说的操作各种节点。写起来费劲,读起来费劲,浏览器运行起来也费劲。

  为了解决上面的问题,我们可以试着创建一个模板,用数据去格式化字符串来渲染视图并插入到容器里,这样实现的方案性能上会提高很多,首先需要一个渲染器,比如想用数据对象 ‘{demo: ‘this is a demo’}’ 去格式化 ‘<a>{#demo#}</a>+’ 字符串模板,得到 ‘<a>this is a demo</a>’ 渲染后的模板,便可插入到页面中,不过先要一个渲染模板引擎的方法 formateString 方法,将字符串模板中的 {#demo#} 替换成数据对象中的 demo 属性值。

//渲染模板方法
A.formateString = function(str, data) {
	return str.replace(/\{#(\w+)#\}/g, function(match, key) {return typeof data[key] === undefined ? '' : data[key]});
}

  有了这个方法,我们就可以通过简单模板来渲染出我们需求的视图了。

//文字列表展示
'listPart': function(data) {
	var s = document.creatElement('div'),   //容器模块
		ul = '',                            //列表字符串
		ldata = data.data.li,               //列表数据
		//模块模板
		tpl = [
			'<h2>{#h2#}</h2>',
			'<p>{#p#}</p>',
			'<ul>{#ul#}</ul>'
		].join(''),
		//列表项模板
		liTpl = [
			'<li>',
				'<strong>{#strong#}</strong>',
				'<span>{#span#}</span>',
			'</li>'
		].join('');
	//有 id 设置模块 id
	data.id && (s.id = data.id);
	//遍历列表数据
	for(var i = 0, len = ldata.length; i < len; i++) {
		//如果有列表项数据
		if(ldata[i].em || ldata[i].span) {
			//列表字符串追加一项列表项
			ul += A.formateString(liTpl, ldata[i]);
		}
	}
	//装饰列表数据
	data.data.ul = ul;
	//渲染模块并插入模块中
	s.innerHTML = A.formateString(tpl, data.data);
	//渲染模块
	A.root.appendChild(s);
}

  这样我们的模板就完成了,一行的渲染工作,便完成了上面那么多节点操作。不过这个版本还能优化, tpl 这个模板内部相似的地方还可以提取,这样我们创建一个模板生成器,我们只要传入不同的标签名就可以了。

//模板生成器 name: 标识
A.view = function(name) {
	//模板库
	var v = {
		//代码模板
		code: '<pre><code>{#code#}</code></pre>',
		//图片模板
		img: '<img src="{#src#}" alt="{#alt#}" title="{#title#}" />',
		//带有id和类的模块模板
		part: '<div id="{#id#}" class="{#class#}">{#part#}</div>',
		//组合模板
		theme: [
			'<div>',
				'<h1>{#title#}</h1>',
				'{#content#}',
			'</div>'
		].join('')
	}
	//如果参数是一个数组,则返回多行模板
	if(Object.prototype.toString.call(name) === '[object Array]') {
		//模板缓存器
		var tpl = '';
		for(var i = 0, len = name.length; i < len; i++) {
			//模板缓存器追加模板
			tpl += arguments.callee(name[i]);
		}
		//返回最终模板
		return tpl;
	} else {
		//如果模板库中有该模板则返回该模板,否则返回建议模板
		return v[name] ? v[name] : ('<' + name + '>{#' + name +'#}</' + name + '>');
	}
}

  有了这个小型模板生成器,我们先获取一个模板就简单多了,比如我们想获取一个 ‘<span>{#span#}</span>’ ,就可以通过 A.view(‘span’) 方式来获取。有了它,完成我们的需求就容易多了。

//文字列表展示
'listPart': function(data) {
	//......
		//模块模板
		tpl = A.view(['h2', 'p', 'ul']),
		//列表项模板
		liTpl = A.formateString(A.view('li'), {li: A.view(['strong', 'span'])}),
	//......
}

  模板字符串的应用其实非常广泛,大家一定要好好熟悉这部分内容。




<think>好的,我现在需要帮助用户让一个C++新手理解之前的代码。首先,我得回顾一下之前的对话,用户的问题是关于读取和输出双精度浮点数,保留12位小数。用户现在希望解释这段代码,让新手能明白。 首先,我要确定用户的需求是什么。他们可能是一个刚学C++的学生,或者刚开始接触编程的人,对语法和库函数太熟悉。所以需要将代码分解,逐步解释每个部分的作用,使用简单易懂的语言,避免专业术语过多,或者至少解释清楚术语。 接下来,我需要回顾之前的代码代码包括头文件iostream和iomanip,使用命名空间std,主函数中读取一个double变量,然后使用cout配合fixed和setprecision输出。对于新手来说,每个部分都可能有问题,比如头文件的作用、using namespace std是什么、double是什么,输入输出流的使用,以及格式控制的含义。 然后,我应该分步骤解释代码,从整体结构开始,比如每个头文件的作用,为什么需要包含iomanip。然后解释主函数的结构,变量的声明,输入语句cin,再到输出的格式控制。重点在于解释fixed和setprecision的作用,因为这是保留12位小数的关键。同时,要指出为什么需要这些控制符,比如默认情况下cout可能会用科学计数法,而fixed确保小数点后的位数固定。 还要注意可能新手会犯的错误,比如忘记包含iomanip导致setprecision可用,或者没有使用fixed导致输出格式符合要求。需要强调这些细节的重要性。另外,代码中的endl的作用是什么,和"\n"的区别,是否需要解释?可能对新手来说,endl除了换行还有刷新缓冲区的功能,但这里可能暂时需要深入,以免增加复杂度。 此外,代码示例中的注释是否足够清晰?可能需要更详细的注释,或者在解释时逐步拆解。例如,解释using namespace std的作用,避免新手理解为什么可以直接用cin和cout而用std::前缀。 可能还需要举例子,比如输入一个数,输出会是什么样子,特别是当小数位数足12位时,是否会补零。例如,输入3.14,输出会是3.140000000000,这样新手可以直观地看到结果。 还要考虑新手可能对函数和库的理解足,比如main函数的结构,return 0的意义,变量声明的位置等等。是否需要简要解释这些基础内容?可能用户已经知道这些,但为了确保全面性,还是应该提到。 最后,总结整个代码的执行流程,从输入到处理到输出,每一步的作用,帮助新手形成整体的认识。确保语言通俗易懂,避免过于技术性的描述,用比喻或实际例子辅助理解。</think>让我们用更简单的方式分解这段代码,帮助新手逐步理解: ### 代码详解(适合新手版) ```cpp // ① 引入工具包 #include <iostream> // 输入输出必备工具(cin/cout) #include <iomanip> // 格式控制工具(fixed/setprecision) // ② 使用标准工具库(避免 std:: 前缀) using namespace std; // ③ 所有C++程序都必须有main函数,像程序的入口大门 int main() { // ④ 声明一个双精度浮点变量(能存带小数点的数) double num; // ⑤ 从键盘读取输入 >> 像水流进变量 cin >> num; // ⑥ 格式化输出(重点!) cout << fixed // 固定小数显示(用科学计数法) << setprecision(12) // 显示12位小数(够补零) << num // 输出数值 << endl; // 换行(相当于按下回车键) // ⑦ 告诉系统程序正常结束 return 0; } ``` ### 关键概念解释表 | 代码部分 | 作用说明 | 类比理解 | |-------------------|--------------------------------------------------------------------------|------------------------------| | `#include <...>` | 导入功能模块,就像玩游戏前要下载安装包 | 做菜前要准备好食材和厨具 | | `using namespace` | 使用标准工具库的缩,避免每次都`std::` | 说"手机"用每次都加品牌前缀 | | `double num;` | 创建一个可以存储小数的容器(双精度浮点型),比`float`精度更高 | 准备一个高精度电子秤 | | `cin >> num` | 从键盘输入数值,存入`num`容器 | 把食材放进电子秤称重 | | `fixed` | 固定小数点显示模式(如用这个,大数会显示成`1.23e+5`这样的科学计数法) | 让电子秤始终显示千克单位 | | `setprecision(12)`| 精确控制显示12位小数(第13位会四舍五入) | 设置电子秤显示到0.000000克 | | `cout << ...` | 把结果输出到屏幕 | 把称重结果打印到标签上 | ### 举个实际例子 🌰 **输入:** ```bash 3.141592653589793 ``` **输出:** ```bash 3.141592653590 ``` (注意:第13位是`8`,四舍五入后第12位`9`变成`10`,进位后显示为`90`) ### 新手常见疑问解答 ❓ 1. **为什么要用`double`用`float`?** - `double`精度更高(15-17位有效数字),`float`只有6-9位,存12位小数必须用`double` 2. `fixed`和`setprecision`的顺序重要吗? - 非常重要!必须先`fixed`,否则`setprecision`控制的是总有效位数(比如`cout << setprecision(3) << 12.345`会输出`12.3`) 3. **输入123会怎样显示?** - 输出`123.000000000000`,自动补足12位小数 4. **能处理超大数吗?** - 可以!`fixed`模式会完整显示,比如输入`123456789.123456789`会显示为`123456789.123456789000` 建议新手自己尝试修改代码: 1. 去掉`fixed`观察输出变化 2. 把12改成其他数字(如3或15) 3. 输入超过12位小数的数观察四舍五入效果
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值