采坑场景
有时,在处理变量时,我们会忘记考虑变量的类型。比如,我们只是想处理一份数据,但并不想改变原数据。比如下面这样:
let obj = {
id :1
}
如果,此时,你想重新copy
一份obj
,并添加一些属性,用来在别处使用。
let bak = obj;
bak.name = 'obj';
console.log(bak); // {id: 1, name: "obj"}
console.log(obj); // {id: 1, name: "obj"}
正确的做法:如果只是简单copy
对象的属性,可以:let bak = Object.assign({}, obj)
,即把obj
的属性,复制到一个空对象。
原因解析
为什么 bak.name = 'obj'
操作,会导致obj
也跟着修改了呢?
真正的原因就是,
obj
的数据类型是对象
。在JS
中 ,对象
属于引用类型。什么意思?就是说,操作对象事,并不是直接操作内存,而是在操作对象的引用,这个引用指向的才是内存。上面的let bak = obj
,相当于引用赋值。换句话说,这么操作之后,bak
和obj
在内存中,指向的是同一个地址。如果修改了其中任何一方,另一个都会受影响。
案例验证
JS
中的数据类型,简单可分为基本类型
和引用类型
基本类型:Undefined
、Null
、Boolean
、Number
、String
引用类型:Object
- 特殊的是,如果打印
typeof null
,你会惊奇地发现,结果是object
。这就很奇怪了。既然Null
算是对象,为什么还要在数据类型中单独列出呢?其实,null
真正的含义是空指针对象
,即不指向内存中的任何地址,特指那些没有设置值得对象。
基本类型的数据,在进行复制等操作时,会重新分配内存,用来放置新数据(值传递)。而引用类型,则是把简单把引用值复制给新对象(地址传递)。
为了更好理解基本类型和引用类型,进行以下验证:
- 初始化一个变量,调用函数处理变量,对比处理前后的变量值
从上面的图 和下面的代码,可以看出,基本类型直接对参数进行处理,不会影响原来的值,而引用类型则会影响。只要,在操作数据时,稍稍注意下类型,就可以避免类型采坑的问题了。
实验代码
(function () {
for (let i = 1; i < 7; i++) {
printResult(i);
}
})();
// 打印结果
function printResult(type) {
let data = '',
result = '',
initObj = {
id: 1,
name: 'init'
},
initArr = ['first'];
switch (type) {
case 1: // Null
data = null;
result = handleObj(data);
break;
case 2: // Boolean
data = true;
result = handleObj(data);
break;
case 3: // Number
data = 1;
result = handleObj(data);
break;
case 4: // String
data = 'before';
result = handleObj(data);
break;
case 5: // Object
data = initObj;
result = handleObj(initObj);
break;
case 6: // Array
data = initArr;
result = handleObj(initArr);
break;
}
console.log('处理前: ', data);
console.log('%c 处理后: ', 'color:green;font-weight:bold', result);
}
// 处理变量
function handleObj(param) {
if (typeof param === 'boolean') {
return param = false;
}
if (typeof param === 'number') {
return param = 0;
}
if (typeof param === 'string') {
return param = 'after';
}
if (typeof param === 'object') {
if (param === null) {
param = 'null被改变';
}
if (Array.isArray(param)) {
param.push('add');
}
if (param instanceof Object) {
param.name = 'object';
}
return param;
}
}