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

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

文章目录

前言

  本章节会比较简短,介绍一下解释器模式,这是行为型设计模式里面的最后一个了,敲黑板,好好听课的同学还记我们总过学习过那些种类的设计模式不?

  给大家复习一下哈。

  首先是 创建型设计模式 ,代表有工厂相关的方法,建造者,原型,单例等;

  然后是 结构型设计模式 ,代表的有,装饰者,适配器,外观等;

  接着是 行为型设计模式 ,代表有代表有观察者,命令,迭代器等;

解释器模式

   对于一种语言,给出其文法表示形式,并定义一种解释器,通过使用这种解释器来解释语言中定义的句子

  举个例子,当我们点击页面种一个 button 的时候,想知道这个元素的 Xpath (元素在页面中所处的路径),这个与事件冒泡类似,只不过在这个路径中还要关心一下同一层级中,当前元素前面的兄弟元素。比如下面的结构:

<div class="wrap">
	<div class="link-inner">
		<a href='#'>link</a>
	</div>
	<div class="button-inner">
		<button>text</button>
	</div>
</div >

  要获取 button 下那个对于 calss wrap div 元素的 Xpath 路径,那么可以表示成 DIV>DIV2>BUTTON 然后记录以作统计,再比如:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
</head>
<body>
	<button>text</button>
</body>
</html>

  获取 button 相对于整个页面文档的 Xpath 路径为 HTML>BODY|HEAD>BUTTON

  大家看到这里,可能会有点蒙,我们上面定义了一种文法,什么是文法呢? 类似普通话里的“什么什么吗?”,“什么什么啊!”,文法是用来定义一组语言规则的,上面我们想获取“元素在页面中所处的路径”,就是一组规则,我们需要做的就是书写一个这种规则的解释器。

  那么针对这一条规则,我们应该怎么来写解释器呢?

  首先观察规则,找到相似点,1.右边第一个元素都是目标元素,2.左边第一个元素是容器元素,3.很像事件流里的冒泡阶段,4.注意兄弟元素之间如果有相同的,要增加数字区分,没有相同的,就增加新类型。

//同级兄弟元素遍历
function getSublingName(node) {
    
    
	//如果存在兄弟元素
	if(node.previousSibling) {
    
    
		var name = '',    //返回兄弟元素名称字符串
			count = 1,    //紧邻兄弟元素中相同名称元素个数
			nodeName = node.nodeName,    //原始节点名称
			sibling = node.previousSibling; //前一个兄弟元素
			//如果存在前一个兄弟元素
		while(sibling) {
    
    
			//如果节点为元素,并且节点与前一个兄弟元素类型相同,并且前一个兄弟元素名称存在
			if(sibling.nodeType == 1 && sibling.nodeType == node.nodeType && sibling.nodeName) {
    
    
				//如果节点名称和前一个兄弟元素名称相同
				if(nodeName == sibling.nodeName) {
    
    
					//节点名称后面添加计数
					name += ++count;
				} else {
    
    
					//重置相同紧邻节点名称节点个数
					count = 1;
					//追加新的节点名称
					name += '|' + sibling.nodeName.toUpperCase();
				}
			}
			//向前获取前一个兄弟元素
			sibling = sibling.previousSibling;
		}
		return name;
	} else {
    
    
		//否则不存在兄弟元素,返回
		return '';
	}
}

  有了这个方法,我们在实现冒泡遍历整个文档树的时候,处理每一层的元素节点就方便多了,下面我们实现一下:

//Xpath 解释器
var Interpreter = (function() {
    
    
	//获取兄弟元素名称
	function getSublingName(node) {
    
    
		//...
	}
	//参数1 node: 目标节点 参数2 wrap: 容器节点
	return function(node, wrap) {
    
    
		//路径数组
		var path = [].
			//如果不存在容器节点,默认为 document
			wrap = wrap || document;
		//如果当前(目标)节点等于容器节点
		if(node === wrap) {
    
    
			//容器节点为元素
			if(wrap.nodeType ==1) {
    
    
				//路径数组中输入容器节点名称
				path.push(wrap.nodeName.toUpperCase());
			}
			//返回最终路径数组结果
			return path;
		}
		//如果当前节点的父节点不等于容器节点
		if(node.parentNode !== wrap) {
    
    
			//对当前节点的父节点执行遍历操作
			path = arguments.callee(node.parentNode, wrap);
		}
		//如果当前节点的父元素节点与容器节点相等
		else {
    
    
			//容器节点为元素
			if(wrap,nodeType == 1) {
    
    
				//路径数组中输入容器节点名称
				path.push(wrap.nodeName.toUpperCase());
			}
		}
		//获取元素的兄弟元素名称统计
		var sublingsNames = getSublingName(node);
		//如果节点为元素
		if(node.nodeType == 1) {
    
    
			//输入当前元素节点名称及其前面兄弟元素统计
			path.push(node.nodeName.toUpperCase() + sublingsNames);
		}
		//返回最终路径数组结果
		return path;
	}
}) ()

  有了这个解释器之后,不管什么样的页面,都可以直接获取到他的 Xpath 路径了。

  本节就到此结束,下一节我们学习一个新的大类,技巧型设计模式。

  (最近打算开一个新的系列,《跟我一起读–微信小程序》,筹备的差不多了,本系列可能会更新慢一点,各位有问题可以直接评论回复,或者私信给我哈)




猜你喜欢

转载自blog.csdn.net/EcbJS/article/details/110877417