【JavaScript 面向对象编程——《深浅拷贝》】计数器例子

相关文章导航

👉【JavaScript 面向对象编程——《深浅拷贝》】计数器例子👈
     【JavaScript 面向对象编程——《设计模式》】计数器例子
     【JavaScript 面向对象编程——《原型继承》】计数器例子

关于深浅拷贝:



🔸浅拷贝🔸(❌慎用)

一、【ES5】Object.create()方法

在这里插入图片描述

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8" />
	<title></title>
	<script type="text/javascript">
		// 定义对象字面量
		var makeCounter = {
			num : 0,
			changeBy : function(val) {
				this.num += val;
			},
			add : function() {
				this.changeBy(1);
			},
			red : function() {
				this.changeBy(-1);
			},
			value : function() {
				return this.num;
			}
		};

		// 封装深拷贝函数
		function deepClone(obj) {
			// 通过调用函数创建一个以指定对象的原型为模板的新对象
			var copy = Object.create(Object.getPrototypeOf(obj));

			// 利用数组的 “Array.prototype.forEach” 方法进行复制,实现深拷贝
			var propNames = Object.getOwnPropertyNames(obj);
			propNames.forEach(function(name) {

				// 获取指定对象上一个自有属性对应的属性描述符。
				var desc = Object.getOwnPropertyDescriptor(obj, name);

				// 直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
				Object.defineProperty(copy, name, desc);
			});

			return copy;
		}

		// 执行操作
		function event(ele, btn1, btn2, obj) {
			btn1.onclick = function() {
				obj.add();
				ele.innerHTML = obj.value();
			}
			btn2.onclick = function() {
				obj.red();
				ele.innerHTML = obj.value();
			}
			ele.innerHTML = obj.value();
		}

		window.onload = function() {
			// 计数器 1
			var Counter1 = deepClone(makeCounter);
			var add1 = document.getElementById('add1');
			var red1 = document.getElementById('red1');
			var demo1 = document.getElementById('demo1');
			event(demo1, add1, red1, Counter1);

			// 计数器 2
			var Counter2 = deepClone(makeCounter);
			var add2 = document.getElementById('add2');
			var red2 = document.getElementById('red2');
			var demo2 = document.getElementById('demo2');
			event(demo2, add2, red2, Counter2);
		}
	</script>
</head>
<body>
	<h3>【ES5】Object.create()方法</h3>
	<hr />
	<p>计数器 1</p>
	<input type="button" id="add1" value="自增 (+1)" />
	<input type="button" id="red1" value="自减 (-1)" />
	<p id="demo1"></p>
	<hr />
	<p>计数器 2</p>
	<input type="button" id="add2" value="自增 (+1)" />
	<input type="button" id="red2" value="自减 (-1)" />
	<p id="demo2"></p>
</body>
</html>

优缺点

  • 【优点】:适用于仅有一层嵌套层的对象的拷贝。

  • 【缺点】:无法对多层对象嵌套层实现深拷贝。

二、【ES6】Object.assign()方法

在这里插入图片描述

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8" />
	<title></title>
	<script type="text/javascript">
		// 定义对象字面量
		var makeCounter = {
			num : 0,
			changeBy : function(val) {
				this.num += val;
			},
			add : function() {
				this.changeBy(1);
			},
			red : function() {
				this.changeBy(-1);
			},
			value : function() {
				return this.num;
			}
		};

		// 执行操作
		function event(ele, btn1, btn2, obj) {
			btn1.onclick = function() {
				obj.add();
				ele.innerHTML = obj.value();
			}
			btn2.onclick = function() {
				obj.red();
				ele.innerHTML = obj.value();
			}
			ele.innerHTML = obj.value();
		}

		window.onload = function() {
			// 计数器 1
			var Counter1 = Object.assign({}, makeCounter);
			var add1 = document.getElementById('add1');
			var red1 = document.getElementById('red1');
			var demo1 = document.getElementById('demo1');
			event(demo1, add1, red1, Counter1);

			// 计数器 2
			var Counter2 = Object.assign({}, makeCounter);
			var add2 = document.getElementById('add2');
			var red2 = document.getElementById('red2');
			var demo2 = document.getElementById('demo2');
			event(demo2, add2, red2, Counter2);
		}
	</script>
</head>
<body>
	<h3>【ES6】Object.assign()方法(该方法实际属于浅拷贝)</h3>
	<hr />
	<p>计数器 1</p>
	<input type="button" id="add1" value="自增 (+1)" />
	<input type="button" id="red1" value="自减 (-1)" />
	<p id="demo1"></p>
	<hr />
	<p>计数器 2</p>
	<input type="button" id="add2" value="自增 (+1)" />
	<input type="button" id="red2" value="自减 (-1)" />
	<p id="demo2"></p>
</body>
</html>

优缺点

  • 【优点】
    a)适用于仅有一层嵌套层的对象的拷贝;
    b)方便简洁快捷,一行代码解决…

  • 【缺点】:无法对多层对象嵌套层实现深拷贝。

三、【ES6】对象展开运算符 “…”

在这里插入图片描述

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8" />
	<title></title>
	<script type="text/javascript">
		// 定义对象字面量
		var makeCounter = {
			num : 0,
			changeBy : function(val) {
				this.num += val;
			},
			add : function() {
				this.changeBy(1);
			},
			red : function() {
				this.changeBy(-1);
			},
			value : function() {
				return this.num;
			}
		};

		// 执行操作
		function event(ele, btn1, btn2, obj) {
			btn1.onclick = function() {
				obj.add();
				ele.innerHTML = obj.value();
			}
			btn2.onclick = function() {
				obj.red();
				ele.innerHTML = obj.value();
			}
			ele.innerHTML = obj.value();
		}

		window.onload = function() {
			// 计数器 1
			var Counter1 = {...makeCounter};
			var add1 = document.getElementById('add1');
			var red1 = document.getElementById('red1');
			var demo1 = document.getElementById('demo1');
			event(demo1, add1, red1, Counter1);

			// 计数器 2
			var Counter2 = {...makeCounter};
			var add2 = document.getElementById('add2');
			var red2 = document.getElementById('red2');
			var demo2 = document.getElementById('demo2');
			event(demo2, add2, red2, Counter2);
		}
	</script>
</head>
<body>
	<h3>【ES6】扩展运算符</h3>
	<hr />
	<p>计数器 1</p>
	<input type="button" id="add1" value="自增 (+1)" />
	<input type="button" id="red1" value="自减 (-1)" />
	<p id="demo1"></p>
	<hr />
	<p>计数器 2</p>
	<input type="button" id="add2" value="自增 (+1)" />
	<input type="button" id="red2" value="自减 (-1)" />
	<p id="demo2"></p>
</body>
</html>

优缺点

  • 【优点】
    a)适用于仅有一层嵌套层的对象的拷贝;
    b)方便简洁快捷,一行代码解决…

  • 【缺点】:无法对多层对象嵌套层实现深拷贝。

🔸深拷贝🔸

一、【jQuery】extend()方法

在这里插入图片描述

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8" />
	<title></title>
	<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
	<script type="text/javascript">
		// 定义对象字面量
		var makeCounter = {
			num : 0,
			changeBy : function(val) {
				this.num += val;
			},
			add : function() {
				this.changeBy(1);
			},
			red : function() {
				this.changeBy(-1);
			},
			value : function() {
				return this.num;
			}
		};

		// 执行操作
		function event(ele, btn1, btn2, obj) {
			btn1.onclick = function() {
				obj.add();
				ele.innerHTML = obj.value();
			}
			btn2.onclick = function() {
				obj.red();
				ele.innerHTML = obj.value();
			}
			ele.innerHTML = obj.value();
		}

		window.onload = function() {
			// 计数器 1
			var Counter1 = $.extend(true, {}, makeCounter);
			var add1 = document.getElementById('add1');
			var red1 = document.getElementById('red1');
			var demo1 = document.getElementById('demo1');
			event(demo1, add1, red1, Counter1);

			// 计数器 2
			var Counter2 = $.extend(true, {}, makeCounter);
			var add2 = document.getElementById('add2');
			var red2 = document.getElementById('red2');
			var demo2 = document.getElementById('demo2');
			event(demo2, add2, red2, Counter2);
		}
	</script>
</head>
<body>
	<h3>【jQuery】extend()方法</h3>
	<hr />
	<p>计数器 1</p>
	<input type="button" id="add1" value="自增 (+1)" />
	<input type="button" id="red1" value="自减 (-1)" />
	<p id="demo1"></p>
	<hr />
	<p>计数器 2</p>
	<input type="button" id="add2" value="自增 (+1)" />
	<input type="button" id="red2" value="自减 (-1)" />
	<p id="demo2"></p>
</body>
</html>

优缺点

  • 【优点】
    a)方便简单且快捷;
    b)支持很多层级的数据;

  • 【缺点】
    a)无法拷贝对象中值为 ‘undefined’ 的属性;
    b)鸡肋:为了一个方法引入 jQuery 库…除非项目用 jQuery 开发;


二、递归算法

(1)第一种递归

在这里插入图片描述

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8" />
	<title></title>
	<script type="text/javascript">
		// 定义对象字面量
		var makeCounter = {
			num : 0,
			changeBy : function(val) {
				this.num += val;
			},
			add : function() {
				this.changeBy(1);
			},
			red : function() {
				this.changeBy(-1);
			},
			value : function() {
				return this.num;
			}
		};

		// 递归封装深拷贝
		function deepClone(obj) {
			if (obj === null) {
				return null;
			} else if (typeof obj !== 'object') {
				// 不再具有下一层次
				return obj;
			} else if (obj.constructor === Date) {
				return new Date(obj);
			}

			// 保持继承链
			var objClone = new obj.constructor ();
			for (var k in obj) {

				// 不遍历其原型链上的属性
				if (obj.hasOwnProperty(k)) {

					// 判断obj子元素是否为对象,如果是,递归复制
					if (obj[k] && typeof obj[k] === 'object') {
						objClone[k] = deepClone(obj[k]);
					} else {
						objClone[k] = obj[k];
					}

					// 简写
					// objClone[k] = typeof obj[k] === 'object' ? deepClone(obj[k]) : obj[k];
				}
			}

			// 返回深度克隆后的对象
			return objClone;
		};

		// 执行操作
		function event(ele, btn1, btn2, obj) {
			btn1.onclick = function() {
				obj.add();
				ele.innerHTML = obj.value();
			}
			btn2.onclick = function() {
				obj.red();
				ele.innerHTML = obj.value();
			}
			ele.innerHTML = obj.value();
		}

		window.onload = function() {
			// 计数器 1
			var Counter1 = deepClone(makeCounter);
			var add1 = document.getElementById('add1');
			var red1 = document.getElementById('red1');
			var demo1 = document.getElementById('demo1');
			event(demo1, add1, red1, Counter1);

			// 计数器 2
			var Counter2 = deepClone(makeCounter);
			var add2 = document.getElementById('add2');
			var red2 = document.getElementById('red2');
			var demo2 = document.getElementById('demo2');
			event(demo2, add2, red2, Counter2);
		}
	</script>
</head>
<body>
	<h3>(一)递归算法</h3>
	<hr />
	<p>计数器 1</p>
	<input type="button" id="add1" value="自增 (+1)" />
	<input type="button" id="red1" value="自减 (-1)" />
	<p id="demo1"></p>
	<hr />
	<p>计数器 2</p>
	<input type="button" id="add2" value="自增 (+1)" />
	<input type="button" id="red2" value="自减 (-1)" />
	<p id="demo2"></p>
</body>
</html>

优缺点

  • 【解释】
    a)用 new obj.constructor () 构造函数新建一个空的对象,而不是使用 {} 或者 [],这样可以保持原形链的继承;
    b)用 obj.hasOwnProperty(key) 来判断属性是否来自原型链上,因为 for…in… 也会遍历其原型链上的可枚举属性;
    c)对于 function 类型,这里是直接赋值的,还是共享一个内存值。这是因为函数更多的是完成某些功能,有个输入值和返回值,而且对于上层业务而言更多的是完成业务功能,并不需要真正将函数深拷贝。

  • 【优点】
    a)常用的递归封装方法,可满足普通对象深拷贝;
    b)支持很多层级的数据;

  • 【缺点】
    a)无法保持引用;
    b)当数据的层次很深,会栈溢出;

(2)第二种递归

在这里插入图片描述

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8" />
	<title></title>
	<script type="text/javascript">
		// 定义对象字面量
		var makeCounter = {
			num : 0,
			changeBy : function(val) {
				this.num += val;
			},
			add : function() {
				this.changeBy(1);
			},
			red : function() {
				this.changeBy(-1);
			},
			value : function() {
				return this.num;
			}
		};

		// 创建对元素进行类型判断的函数
		function getType(obj) {
			// tostring会返回对应不同的标签的构造函数
			var _toString = Object.prototype.toString;
			var map = {
				'[object Boolean]' : 'boolean',
				'[object Number]' : 'number',
				'[object String]' : 'string',
				'[object Function]' : 'function',
				'[object Array]' : 'array',
				'[object Date]' : 'date',
				'[object RegExp]' : 'regExp',
				'[object Undefined]' : 'undefined',
				'[object Null]' : 'null',
				'[object Object]' : 'object'
			};
			if (obj instanceof Element) {
				return 'element';
			}
			return map[_toString.call(obj)];
		}

		// 递归封装深拷贝
		function deepClone(obj) {
			var type = getType(obj);
			var objClone;
			if (type === 'array') {
				objClone = [];
			} else if (type === 'object') {
				objClone = {};
			} else {
				// 不再具有下一层次
				return obj;
			}

			// 类型为数组或对象时的递归复制
			if (type === 'array') {
				for (var i = 0, len = obj.length; i < len; i++) {
					objClone.push(deepClone(obj[i]));
				}
			} else if (type === 'object') {
				for (var k in obj) {
					objClone[k] = deepClone(obj[k]);
				}
			}

			// 返回深度克隆后的对象
			return objClone;
		}

		// 执行操作
		function event(ele, btn1, btn2, obj) {
			btn1.onclick = function() {
				obj.add();
				ele.innerHTML = obj.value();
			}
			btn2.onclick = function() {
				obj.red();
				ele.innerHTML = obj.value();
			}
			ele.innerHTML = obj.value();
		}

		window.onload = function() {
			// 计数器 1
			var Counter1 = deepClone(makeCounter);
			var add1 = document.getElementById('add1');
			var red1 = document.getElementById('red1');
			var demo1 = document.getElementById('demo1');
			event(demo1, add1, red1, Counter1);

			// 计数器 2
			var Counter2 = deepClone(makeCounter);
			var add2 = document.getElementById('add2');
			var red2 = document.getElementById('red2');
			var demo2 = document.getElementById('demo2');
			event(demo2, add2, red2, Counter2);
		}
	</script>
</head>
<body>
	<h3>(二)递归算法</h3>
	<hr />
	<p>计数器 1</p>
	<input type="button" id="add1" value="自增 (+1)" />
	<input type="button" id="red1" value="自减 (-1)" />
	<p id="demo1"></p>
	<hr />
	<p>计数器 2</p>
	<input type="button" id="add2" value="自增 (+1)" />
	<input type="button" id="red2" value="自减 (-1)" />
	<p id="demo2"></p>
</body>
</html>

优缺点

  • 【解释】:对于 function 类型,这里是直接赋值的,还是共享一个内存值。这是因为函数更多的是完成某些功能,有个输入值和返回值,而且对于上层业务而言更多的是完成业务功能,并不需要真正将函数深拷贝。

  • 【优点】
    a)可满足普通对象深拷贝;
    b)支持很多层级的数据;

  • 【缺点】
    a)无法保持引用;
    b)当数据的层次很深,会栈溢出;


三、二叉树遍历算法

【DFS】树深度优先遍历

(1)第一种 DFS 写法

在这里插入图片描述

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8" />
	<title></title>
	<script type="text/javascript">
		// 定义对象字面量
		var makeCounter = {
			num : 0,
			changeBy : function(val) {
				this.num += val;
			},
			add : function() {
				this.changeBy(1);
			},
			red : function() {
				this.changeBy(-1);
			},
			value : function() {
				return this.num;
			}
		};

		// 深度优先深度克隆, 利用栈的方式实现
		// 防栈溢出
		function deepClone(obj) {
			var objClone = {};

			// 栈
			var loopList = [{
				parent: objClone,
				key: undefined,
				data: obj,
			}];

			while (loopList.length) {
				// 深度优先
				var node = loopList.pop();
				var parent = node.parent;
				var key = node.key;
				var data = node.data;

				// 初始化赋值目标,key为undefined则拷贝到父元素,否则拷贝到子元素
				var res = parent;
				if (typeof key !== 'undefined') {
					res = parent[key] = {};
				}
				for (var k in data) {
					if (data.hasOwnProperty(k)) {
						if (typeof data[k] === 'object') {
							// 下一次循环
							loopList.push({
								parent : res,
								key : k,
								data : data[k],
							});
						} else {
							res[k] = data[k];
						}
					}
				}
			}

			// 返回深度克隆后的对象
			return objClone;
		}

		// 执行操作
		function event(ele, btn1, btn2, obj) {
			btn1.onclick = function() {
				obj.add();
				ele.innerHTML = obj.value();
			}
			btn2.onclick = function() {
				obj.red();
				ele.innerHTML = obj.value();
			}
			ele.innerHTML = obj.value();
		}

		window.onload = function() {
			// 计数器 1
			var Counter1 = deepClone(makeCounter);
			var add1 = document.getElementById('add1');
			var red1 = document.getElementById('red1');
			var demo1 = document.getElementById('demo1');
			event(demo1, add1, red1, Counter1);

			// 计数器 2
			var Counter2 = deepClone(makeCounter);
			var add2 = document.getElementById('add2');
			var red2 = document.getElementById('red2');
			var demo2 = document.getElementById('demo2');
			event(demo2, add2, red2, Counter2);
		}
	</script>
</head>
<body>
	<h3>【DFS】树深度优先遍历(一)</h3>
	<hr />
	<p>计数器 1</p>
	<input type="button" id="add1" value="自增 (+1)" />
	<input type="button" id="red1" value="自减 (-1)" />
	<p id="demo1"></p>
	<hr />
	<p>计数器 2</p>
	<input type="button" id="add2" value="自增 (+1)" />
	<input type="button" id="red2" value="自减 (-1)" />
	<p id="demo2"></p>
</body>
</html>
优缺点
  • 【优点】
    a)不会栈溢出;
    b)支持很多层级的数据;

  • 【缺点】:…

(2)第二种 DFS 写法

在这里插入图片描述

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8" />
	<title></title>
	<script type="text/javascript">
		// 定义对象字面量
		var makeCounter = {
			num : 0,
			changeBy : function(val) {
				this.num += val;
			},
			add : function() {
				this.changeBy(1);
			},
			red : function() {
				this.changeBy(-1);
			},
			value : function() {
				return this.num;
			}
		};

		// 创建对元素进行类型判断的函数
		function getType(obj) {
			// tostring会返回对应不同的标签的构造函数
			var _toString = Object.prototype.toString;
			var map = {
				'[object Boolean]' : 'boolean',
				'[object Number]' : 'number',
				'[object String]' : 'string',
				'[object Function]' : 'function',
				'[object Array]' : 'array',
				'[object Date]' : 'date',
				'[object RegExp]' : 'regExp',
				'[object Undefined]' : 'undefined',
				'[object Null]' : 'null',
				'[object Object]' : 'object'
			};
			if (obj instanceof Element) {
				return 'element';
			}
			return map[_toString.call(obj)];
		}

		// 深度优先深度克隆, 利用栈的方式实现
		// 解决【递归、对象环、边界处理(比如函数,Set等)、防栈溢出】问题
		function deepClone(obj) {
			var type = getType(obj);
			var objClone = {};
			// 用于存储复制过程中访问过的对象的队列,避免对象环的问题
			var visitQueue = [];
			if (type === 'array' || type === 'object') {
				var index = visitQueue.indexOf(obj);
				if (index > -1){
					objClone = visitQueue[index]
				} else {
					visitQueue.push(obj);
					for (var key in obj) {
						objClone[key] = deepClone(obj[key], visitQueue);
					}
				}

			} else if (type === 'function') {
				// 处理函数
				objClone = eval( '(' + obj.toString() + ')');
			} else {
				// 处理原始值
				objClone = obj;
			}

			// 返回深度克隆后的对象
			return objClone;
		}

		// 执行操作
		function event(ele, btn1, btn2, obj) {
			btn1.onclick = function() {
				obj.add();
				ele.innerHTML = obj.value();
			}
			btn2.onclick = function() {
				obj.red();
				ele.innerHTML = obj.value();
			}
			ele.innerHTML = obj.value();
		}

		window.onload = function() {
			// 计数器 1
			var Counter1 = deepClone(makeCounter);
			var add1 = document.getElementById('add1');
			var red1 = document.getElementById('red1');
			var demo1 = document.getElementById('demo1');
			event(demo1, add1, red1, Counter1);

			// 计数器 2
			var Counter2 = deepClone(makeCounter);
			var add2 = document.getElementById('add2');
			var red2 = document.getElementById('red2');
			var demo2 = document.getElementById('demo2');
			event(demo2, add2, red2, Counter2);
		}
	</script>
</head>
<body>
	<h3>【DFS】树深度优先遍历(二)</h3>
	<hr />
	<p>计数器 1</p>
	<input type="button" id="add1" value="自增 (+1)" />
	<input type="button" id="red1" value="自减 (-1)" />
	<p id="demo1"></p>
	<hr />
	<p>计数器 2</p>
	<input type="button" id="add2" value="自增 (+1)" />
	<input type="button" id="red2" value="自减 (-1)" />
	<p id="demo2"></p>
</body>
</html>
优缺点
  • 【优点】
    a)不会栈溢出;
    b)支持很多层级的数据;

  • 【缺点】:…

【BFS】树广度优先遍历

(1)第一种 BFS 写法

在这里插入图片描述

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8" />
	<title></title>
	<script type="text/javascript">
		// 定义对象字面量
		var makeCounter = {
			num : 0,
			changeBy : function(val) {
				this.num += val;
			},
			add : function() {
				this.changeBy(1);
			},
			red : function() {
				this.changeBy(-1);
			},
			value : function() {
				return this.num;
			}
		};

		// 广度优先深度克隆, 利用队列的方式实现
		// 利用objClone建立一个与原对象相同的数据结构, 遇到可处理的值(比如原始值,函数,就处理后赋值到相应的节点下)
		function deepClone(obj) {
			var objClone = {};

			// 进队列
			var originQueue = [obj];

			// 同时objClone也跟着一起进队列
			var copyQueue = [objClone];

			// 以下两个队列用来保存复制过程中访问过的对象,以此来避免对象环的问题(对象的某个属性值是对象本身)
			var visitQueue = [];
			var copyVisitQueue = [];

			while (originQueue.length > 0) {
				var _obj = copyQueue.shift();
				var _data = originQueue.shift();
				copyVisitQueue.push(_obj);
				visitQueue.push(_data);
				for (var k in _data) {
					var _value = _data[k];
					if (typeof _value !== 'object') {
						_obj[k] = _value;
					} else {
						// 使用indexOf可以发现数组中是否存在相同的对象(实现indexOf的难点就在于对象比较)
						var index = visitQueue.indexOf(_value);
						if (index >= 0) {
							// 出现环的情况不需要再取出遍历
							_obj[k] = copyVisitQueue[index];
						} else {
							originQueue.push(_value);
							_obj[k] = {};
							copyQueue.push(_obj[k]);
						}
					}
				}
			}

			// 返回深度克隆后的对象
			return objClone;
		}

		// 执行操作
		function event(ele, btn1, btn2, obj) {
			btn1.onclick = function() {
				obj.add();
				ele.innerHTML = obj.value();
			}
			btn2.onclick = function() {
				obj.red();
				ele.innerHTML = obj.value();
			}
			ele.innerHTML = obj.value();
		}

		window.onload = function() {
			// 计数器 1
			var Counter1 = deepClone(makeCounter);
			var add1 = document.getElementById('add1');
			var red1 = document.getElementById('red1');
			var demo1 = document.getElementById('demo1');
			event(demo1, add1, red1, Counter1);

			// 计数器 2
			var Counter2 = deepClone(makeCounter);
			var add2 = document.getElementById('add2');
			var red2 = document.getElementById('red2');
			var demo2 = document.getElementById('demo2');
			event(demo2, add2, red2, Counter2);
		}
	</script>
</head>
<body>
	<h3>【BFS】树广度优先遍历(一)</h3>
	<hr />
	<p>计数器 1</p>
	<input type="button" id="add1" value="自增 (+1)" />
	<input type="button" id="red1" value="自减 (-1)" />
	<p id="demo1"></p>
	<hr />
	<p>计数器 2</p>
	<input type="button" id="add2" value="自增 (+1)" />
	<input type="button" id="red2" value="自减 (-1)" />
	<p id="demo2"></p>
</body>
</html>
优缺点
  • 【优点】
    a)不会栈溢出;
    b)支持很多层级的数据;

  • 【缺点】:…

(2)第二种 BFS 写法

在这里插入图片描述

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8" />
	<title></title>
	<script type="text/javascript">
		// 定义对象字面量
		var makeCounter = {
			num : 0,
			changeBy : function(val) {
				this.num += val;
			},
			add : function() {
				this.changeBy(1);
			},
			red : function() {
				this.changeBy(-1);
			},
			value : function() {
				return this.num;
			}
		};

		// 创建对元素进行类型判断的函数
		function getType(obj) {
			// tostring会返回对应不同的标签的构造函数
			var _toString = Object.prototype.toString;
			var map = {
				'[object Boolean]' : 'boolean',
				'[object Number]' : 'number',
				'[object String]' : 'string',
				'[object Function]' : 'function',
				'[object Array]' : 'array',
				'[object Date]' : 'date',
				'[object RegExp]' : 'regExp',
				'[object Undefined]' : 'undefined',
				'[object Null]' : 'null',
				'[object Object]' : 'object'
			};
			if (obj instanceof Element) {
				return 'element';
			}
			return map[_toString.call(obj)];
		}

		// 广度优先深度克隆, 利用队列的方式实现
		// 利用objClone建立一个与原对象相同的数据结构, 遇到可处理的值(比如原始值,函数,就处理后赋值到相应的节点下)
		function deepClone(obj) {
			var objClone = {};

			// 进队列
			var originQueue = [obj];

			// 同时objClone也跟着一起进队列
			var copyQueue = [objClone];

			// 用于存储复制过程中访问过的对象的队列,避免对象环的问题
			var visitQueue = [];
			while(originQueue.length){
				var _obj = originQueue.shift();
				var _data = copyQueue.shift();
				if (getType(_obj) === 'array' || getType(_obj) === 'object') {
					for(item in _obj){
						var val = _obj[item];
						if (getType(val) === 'object') {
							var index = visitQueue.indexOf(val);
							if (~index) {
								// 对象环
								_data[item] = visitQueue[index];
							} else {
								// 新的对象,给objClone一个对应属性的空对象
								originQueue.push(val);
								_data[item] = {};
								copyQueue.push(_data[item]);
								visitQueue.push(val);
							}
						}
						else if (getType(val) === 'array') {
							originQueue.push(val);
							_data[item] = [];
							copyQueue.push(_data[item])
						} else if (getType(val) === 'function') {
							// 处理函数
							_data[item] = eval( '(' + val.toString() + ')');
						} else {
							// 处理原始值
							_data[item] = val;
						}
					}
				} else if (getType(obj) === 'function') {
					// 处理函数
					_data = eval( '(' + _obj.toString() + ')');
				} else {
					// 处理原始值
					_data = _obj;
				}
			}
			// 返回深度克隆后的对象
			return objClone;
		}

		// 执行操作
		function event(ele, btn1, btn2, obj) {
			btn1.onclick = function() {
				obj.add();
				ele.innerHTML = obj.value();
			}
			btn2.onclick = function() {
				obj.red();
				ele.innerHTML = obj.value();
			}
			ele.innerHTML = obj.value();
		}

		window.onload = function() {
			// 计数器 1
			var Counter1 = deepClone(makeCounter);
			var add1 = document.getElementById('add1');
			var red1 = document.getElementById('red1');
			var demo1 = document.getElementById('demo1');
			event(demo1, add1, red1, Counter1);

			// 计数器 2
			var Counter2 = deepClone(makeCounter);
			var add2 = document.getElementById('add2');
			var red2 = document.getElementById('red2');
			var demo2 = document.getElementById('demo2');
			event(demo2, add2, red2, Counter2);
		}
	</script>
</head>
<body>
	<h3>【BFS】树广度优先遍历(二)</h3>
	<hr />
	<p>计数器 1</p>
	<input type="button" id="add1" value="自增 (+1)" />
	<input type="button" id="red1" value="自减 (-1)" />
	<p id="demo1"></p>
	<hr />
	<p>计数器 2</p>
	<input type="button" id="add2" value="自增 (+1)" />
	<input type="button" id="red2" value="自减 (-1)" />
	<p id="demo2"></p>
</body>
</html>
优缺点
  • 【优点】
    a)不会栈溢出;
    b)支持很多层级的数据;

  • 【缺点】:…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值