一个老生常谈的问题,关于JavaScript的浅复制、深复制,这次还带来了面向切面。本文中将会举例来说明这两种区别,最后将会演示面向切面的作用。
源代码
直接上代码,给出的是一个工具类,就不一一解释代码了,足够演示以及生产使用。
/**
* 对象操作工具类
* */
function Utils() {
let __this__ = this;
/**
* 是否是Object
* @param {object} obj
* @returns {Boolean}
*/
this.isObject = function(obj) {
return obj && (typeof obj === 'object');
}
/**
* 是否不是Object
* @param {object} obj
* @returns {Boolean}
*/
this.isNotObject = function(obj) {
return !__this__.isObject(obj);
}
/**
* 是否是数组
* @param {object} obj || Array
* @returns {Boolean}
*/
this.isArray = function(obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
}
/** 浅复制
* @param {object} source 源对象
* @param {object} target 目标对象
*/
this.reflection = function(source, target) {
//如果不是对象将抛出异常
if (__this__.isNotObject(source) || __this__.isNotObject(target)) {
throw new Error('There are parameters that are not objects in the given parameters.');
}
let arrSource = Object.keys(source);
for (let i = 0; i < arrSource.length; i++) {
let key = arrSource[i];
target[key] = source[key]
}
}
/** 深复制
* @param {object} source 源对象
* @param {object} target 目标对象
*/
this.copy = function(source, target) {
//如果不是对象将抛出异常
if (__this__.isNotObject(source) || __this__.isNotObject(target)) {
throw new Error('There are parameters that are not objects in the given parameters.');
}
let arrSource = Object.keys(source);
for (let i = 0; i < arrSource.length; i++) {
let key = arrSource[i];
//如果这个属性是对象则迭代
if(__this__.isObject(source[key])){
target[key] = __this__.isArray(source[key]) ? [] : {};
__this__.copy(source[key],target[key]);
}else{
target[key] = source[key]
}
}
}
/**
* 切面
* 重构目标函数,提供函数执行之前执行之后的钩子函数
* @param {object} source 目标对象
* @param {string} attribute 目标函数,属于目标对象的需要代理的属性,必须是函数,否则将抛出异常
* @param {function} before 目标函数执行之前执行,接收一个参数args(目标函数中arguments转变的数组),可改变目标函数的参数值
* @param {function} after 目标函数执行之后执行,接收一个参数args(目标函数中arguments转变的数组),可改变目标函数的参数值
*/
this.aspect = function(source, attribute, before, after) {
if (typeof source[attribute] === "function") {
let temporary = source[attribute];
source[attribute] = function(){
let parameter = Array.prototype.slice.call(arguments);
if(typeof before !== 'undefined' && before instanceof Function){
before.call(source,parameter);
}
temporary.apply(source,parameter);
if(typeof after !== 'undefined' && after instanceof Function){
after.call(source,parameter);
}
}
} else {
throw new Error('The property is not an executable function type.');
}
}
}
export default ObjectUtils;
验证
const ObjectUtils = new Utils();
let source = {
"name":"杰克",
"age":21,
"relatives":[
{
"name":"父亲",
"age":46,
},
{
"name":"母亲",
"age":46,
}
],
"data":{
"gender":"男",
"hobby":"女"
}
}
let target1 = {}
let target2 = {}
ObjectUtils.reflection(source,target1);
ObjectUtils.copy(source,target2);
console.log(target1)
console.log(target2)
target1.relatives[0].age = 50;
console.log(target1.relatives[0].age) // 50
console.log(target2.relatives[0].age) // 48
console.log(source.relatives[0].age) // 50
从代码中看到,深复制与浅复制的区别在于深复制在循环中进行了判断,如果属性是Object则创建一个空Object或者Array进行递归复制。
从演示中看到,深复制与浅复制的区别在于深复制与源对象没有任何关联,改变源对象不会影响复制后的对象,而浅复制相反。
总结:实现上深复制进行了类型判断并递归复制,浅复制复制基本类型以及对象引用。使用上深复制是一个全新的对象与源对象没有关联,浅复制(可能)存在相同引用的内容。
面向切面演示
const ObjectUtils = new Utils();
let source = {
"run":function(){
console.log(2)
},
}
ObjectUtils.aspect(source,"run",function(){
console.log(1)
},function(){
console.log(3)
});
source.run();
// 1
// 2
// 3
由代码中可知切面函数有四个参数,前两个必须,后两个可选,分别是源对象、目标函数在源对象中的key,前置函数,后置函数。
从演示中看到,在经过切面函数处理之后,执行源函数会按照顺序执行前置函数、目标函数、后置函数。在其他语言中面向切面编程,可以为代码省去一些重复的步骤,或是进行集中处理。当然这里只能算是一个另类实现,重构函数,远算不上面向切面,具体的使用场景就自行摸索吧~