相关文章导航
-
👉【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));
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() {
var Counter1 = deepClone(makeCounter);
var add1 = document.getElementById('add1');
var red1 = document.getElementById('red1');
var demo1 = document.getElementById('demo1');
event(demo1, add1, red1, Counter1);
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() {
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);
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() {
var Counter1 = {...makeCounter};
var add1 = document.getElementById('add1');
var red1 = document.getElementById('red1');
var demo1 = document.getElementById('demo1');
event(demo1, add1, red1, Counter1);
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() {
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);
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)) {
if (obj[k] && typeof obj[k] === 'object') {
objClone[k] = deepClone(obj[k]);
} else {
objClone[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() {
var Counter1 = deepClone(makeCounter);
var add1 = document.getElementById('add1');
var red1 = document.getElementById('red1');
var demo1 = document.getElementById('demo1');
event(demo1, add1, red1, Counter1);
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) {
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() {
var Counter1 = deepClone(makeCounter);
var add1 = document.getElementById('add1');
var red1 = document.getElementById('red1');
var demo1 = document.getElementById('demo1');
event(demo1, add1, red1, Counter1);
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;
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() {
var Counter1 = deepClone(makeCounter);
var add1 = document.getElementById('add1');
var red1 = document.getElementById('red1');
var demo1 = document.getElementById('demo1');
event(demo1, add1, red1, Counter1);
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) {
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 = {};
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() {
var Counter1 = deepClone(makeCounter);
var add1 = document.getElementById('add1');
var red1 = document.getElementById('red1');
var demo1 = document.getElementById('demo1');
event(demo1, add1, red1, Counter1);
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;
}
};
function deepClone(obj) {
var objClone = {};
var originQueue = [obj];
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 {
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() {
var Counter1 = deepClone(makeCounter);
var add1 = document.getElementById('add1');
var red1 = document.getElementById('red1');
var demo1 = document.getElementById('demo1');
event(demo1, add1, red1, Counter1);
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) {
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 objClone = {};
var originQueue = [obj];
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 {
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() {
var Counter1 = deepClone(makeCounter);
var add1 = document.getElementById('add1');
var red1 = document.getElementById('red1');
var demo1 = document.getElementById('demo1');
event(demo1, add1, red1, Counter1);
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)支持很多层级的数据;
- 【缺点】:…