前言
第一次写,简单写一些自己关于自己对前端的理解及实际工作中遇到的问题,如有错误,还请各位指正。
我们都知道js里对象属于引用类型,仅仅是简单的赋值,只是将一个对象在堆中的地址赋给另一个变量,实际指向的还是同一内存空间:
var obj1 = {a: 1,b: 2};
obj2 = obj1;
obj2 // {a: 1,b: 2}
obj2.a = 2;
obj1 // {a: 2,b: 2}
实际上obj1和obj2指向的是相同的对象,所以对obj2进行操作会改变原对象,而在实际应用中,我们常常需要的是克隆一个相同的对象去进行操作。这时我们就会想到使用for(in)去遍历对象进行克隆:
var obj1 = {a: 1,b: 2,c: 3};
var obj2 = {};
for(var key in obj1){
obj2[key] = obj1[key]
};
obj2 // {a: 1,b: 2,c: 3}
这个时候我们想到去写一个方法实现对象的克隆:
function clone(obj){
var result={};
for(key in obj){
result[key]=obj[key];
}
return result;
}
var obj1 = {
x:1,
y:2,
z:{
a:1,
b:2,
c:{
e:3,
f:4
}
}
};
var obj2 = clone(obj1)
console.log(obj2.z.c.f); //4
obj2.z.c.f = 10;
console.log(obj1.z.c.f); //10
可以看到我们的克隆并不彻底,对于对象中的对象并没有克隆出来,这时我们需要健壮程序,当我们传入的是一个对象,而如果对象的属性值不单单仅是对象呢?可能是数组,可能是null,或undefined,所以,我们需要对对象及他的属性进行类型检测:
function clone(obj) {
var result; //
if(obj === null){
return 'null' //如果是null,直接返回
}
else if(obj === undefined){
return 'undefined' //如果是undefined,直接返回
}
else if(Object.prototype.toString.call(obj).slice(8,-1) === 'Object'){
result = {} //这种方式俗称为检查胎记,能直接返回对象的类属性
}else if(Object.prototype.toString.call(obj).slice(8,-1) === 'Array'){
result = []
}else{
return obj
}
for(var key in obj){
if(Object.prototype.toString.call(obj[key]).slice(8,-1) ==='Object' || 'Array'){
result[key] = clone(obj[key]) //递归调用
}else{
result[key] = obj[key];
}
}
return result
}
var obj2 = clone(obj1)
console.log(obj2.z.c.f); //4
obj2.z.c.f = 10;
console.log(obj1.z.c.f); //4
这个时候,我们的方法基本完成,但是,我们有没有想过,如果for(in)循环拿不到对象的属性,那我们怎么办?等等,我们似乎忘记对象的属性还有四大特性了:
var obj = {
x: 1,
y: 2
};
console.log(Object.getOwnPropertyDescriptor(obj,'x'));
// Object {value: 1, writable: true, enumerable: true, configurable: true}
Object.defineProperty(obj,'x',{
writable:false,
enumerable:false,
}); // 我们重新设置x的特性为不可读写,不可遍历
obj.x = 2;
console.log(obj.x);// 1
var Oobj={};
for(var key in obj){
Oobj[key] = obj[key]
}
console.log(Oobj); //{y:2},我们压根就拿不到x,更不要说去克隆了
这时我们就需要继续改进我们之前定义的克隆方法了
function clone(obj) {
var result;
if(obj === null){
return 'null' //如果是null,直接返回
}
else if(obj === undefined){
return 'undefined' //如果是undefined,直接返回
}
else if(Object.prototype.toString.call(obj).slice(8,-1) === 'Object'){
result = {} //这种方式俗称为检查胎记,能直接返回对象的类属性
var names = Object.getOwnPropertyNames(obj);// 拿到元素所有属性的键值
for( var i=0 ;i< names.length; i ++){
var resc = Object.getOwnPropertyDescriptor(obj,names[i]); //拿到属性的特性
var copy = obj[names[i]]; //元素属性值
Object.defineProperty(result,names[i],resc);//重新添加属性并设置其特性
if(Object.prototype.toString.call(copy).slice(8,-1) ==='Object' || 'Array'){
result[names[i]] = clone(copy)
}
}
}else if(Object.prototype.toString.call(obj).slice(8,-1) === 'Array'){
result = [];
for(var key in obj){
if(Object.prototype.toString.call(copy).slice(8,-1) ==='Object' || 'Array'){
result[key] = clone(obj[key]) //递归调用
}else{
result[key] = obj[key];
}
}
}else{
return obj
}
return result
}
var obj1 = {
x:1,
y:2,
z:{
a:1,
b:[1,2,[3,4]],
c:{
e:3,
f:4
}
}
};
Object.defineProperty(obj1.z,'c',{
writable:false,
enumerable:false,
});
obj2 = clone(obj1);
console.log(obj2); // 与obj1一致
如有错误或遗漏,欢迎指正,谢谢。