三目运算
「三目运算符」也称为「三元运算符」。
三目运算符可以使我们简单判断语句,比如用if
判断语句:
var a = 5;
if (a > 0) {
console.log("大于0");
} else {
console.log("小于等于0");
}
利用三目运算可以简化成:
var a = 5;
a > 0 ? console.log("大于0") : console.log("小于等于0");
三目运算还具有return
的功能:
var a = 5;
var str = a > 0 ? "大于0" : "小于等于0";
console.log(str); // "大于0"
三目运算还能嵌套使用:
var a = 5;
var str1 = a > 0
? (a > 3 ? "大于3" : "小于3")
: "小于等于0";
console.log(str1); // "大于3"
面试题:
// 请问 str 打印什么?
var str = 89 > 9
? ("89" > "9" ? "通过了" : "内层未通过")
: "外层未通过";
console.log(str)
// 解析:
// 先判断 89 > 9 肯定是 true
// 然后来到 ("89" > "9" ? "通过了" : "内层未通过")
// 这里需要注意的是 “89” 和 “9” 不会进行隐式类型,而是使用 ASCII 表的顺序进行比较
// 所以答案是 "内层未通过"
对象克隆
我们都知道「原始类型」和「引用类型」的区别是:原型类型的数据保存在栈内存当中,而引用类型的数据是在栈内存当中保存了堆内存的地址。
所以当我们复制一个对象的时候,其实复制了一个堆内存的地址。
var person1 = {
name: "zhangsan",
age: 18,
height: 180,
wetght: 140,
};
var person2 = person1;
person2.name = "lisi";
console.log(person2);
// {name: 'lisi', age: 18, height: 180, wetght: 140}
console.log(person1);
// {name: 'lisi', age: 18, height: 180, wetght: 140}
以上代码person2
在复制person1
对象后更改了name
属性为lisi
,打印person1
和person2
发现它们两的name
属性都被更改了,这就是因为指向的是同一个堆内存地址,所以相互影响。
浅拷贝
既然我们知道了原因,那我们可以利用循环的方式,将**person1**
的每一个属性循环的复制给**person2**
来实现浅拷贝。
var person1 = {
name: "zhangsan",
age: 18,
height: 180,
wetght: 140,
};
var person2 = {};
for (var key in person1) {
person2[key] = person1[key];
}
// 赋值后互不影响,完成克隆
person2.name = "lisi";
console.log(person1); // {name: 'zhangsan', age: 18, height: 180, wetght: 140}
console.log(person2); // {name: 'lisi', age: 18, height: 180, wetght: 140}
这样的浅拷贝还不是最完美的,因为for...in...
会把person1
上原型的属性也复制过来:
// 浅拷贝的问题,没有处理对象内部的引用数据
Object.prototype.num = 1;
// 对象字面量其实也是 new Object() 构造出来的!!!
var person1 = {
name: "zhangsan",
age: 18,
height: 180,
wetght: 140,
children: {
first: "Jenney",
second: "Lucy",
},
};
var person2 = {};
// for in 的问题会把 person1 的原型也拷贝进去
for (var key in person1) {
person2[key] = person1[key];
}
person2.name = "lisi";
person2.children.third = "Jone";
console.log(person1);
console.log(person2);
接着我们来优化一下浅拷贝:
function clone(origin, target) {
for (var key in origin) {
// 解决克隆 person1 原型属性的问题
if (origin.hasOwnProperty(key)) {
target[key] = origin[key];
}
}
}
Object.prototype.num = 1;
var person1 = {
name: "zhangsan",
age: 18,
height: 180,
wetght: 140,
children: {
first: "Jenney",
second: "Lucy",
},
};
var person2 = {};
clone(person1, person2);
person2.name = "lisi";
person2.children.third = "Jone";
console.log(person1);
console.log(person2);
这样就完成了浅拷贝,但是我们能发现虽然更改name
属性后,person1
和person2
相互不影响了,但是在给person2.children
新增属性的时候,person1
也发生了变化,这是因为我们虽然解决了第一层的引用关系,但是第二层的引用关系我们没有进行处理。
深拷贝
那么如何处理第二层甚至第三层、第四层的引用关系呢?我们可以利用递归的方式去判断处理,如果是引用值我们就进行递归,如果是原始值我们就直接赋值。
// 循环的时候判断引用值,利用递归
function deepclone(origin, target) {
var target = target || {};
var toStr = Object.prototype.toString;
var arrType = "[object Array]";
for (var key in origin) {
// 判断是否是 origin 的属性
if (origin.hasOwnProperty(key)) {
// 判断是否是引用类型,因为 typeof null 会返回 object,所以需要判断 !== null
if (typeof origin[key] === "object" && origin[key] !== null) {
// 利用 Object.prototype.toString() 方法能更准确的判断 object 或者 array
toStr.call(origin[key]) === arrType
? target[key] = []
: target[key] = {}
// 进行递归
deepclone(origin[key], target[key]);
} else {
// 原始值直接赋值
target[key] = origin[key];
}
}
}
// 返回 target 对象
return target;
}
Object.prototype.num = 1;
var person1 = {
name: "张三",
age: 40,
height: 180,
wetght: 140,
car: ["Benz", "Mazda"],
children: {
first: {
name: "张小一",
age: 13,
},
second: {
name: "张小二",
age: 10,
},
},
};
var person2 = deepclone(person1);
person2.children.third = {
name: "张小三",
age: 8,
};
console.log(person1);
console.log(person2);
利用递归的方式完美的实现了深拷贝!
另外还有一种方式可以利用JSON
的方法进行深克隆。
var person1 = {
name: "张三",
age: 40,
height: 180,
wetght: 140,
car: ["Benz", "Mazda"],
children: {
first: {
name: "张小一",
age: 13,
},
second: {
name: "张小二",
age: 10,
},
},
};
var str = JSON.stringify(person1);
var person2 = JSON.parse(str);
console.log(person1);
console.log(person2);