递归
什么是递归
递归:如果一个函数在内部可以调用其本身,那么这个函数就是递归函数。简单理解:函数内部自己调用自己, 这个函数就是递归函数
注意:递归函数的作用和循环效果一样,由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件return。
<script>
var num = 1;
function fn() {
console.log('6');
if(num == 6) {
return; //必须加return
}
num++;
fn();
}
fn();
</script>
递归例子
1.阶乘
<script>
//利用递归函数求1~n的阶乘
function fn(n) {
if(n == 1) {
return 1;
}
return n * fn(n-1);
}
console.log(fn(4));
//详细思路
//假如用户输入3,
//return 3 * fn(3-1)
//fn(2) = 2 * fn(2-1);
//return 3 * (2*fn(1))
//fn(1) 满足n == 1 return 1;
//return 3 * (2 * 1) = 6
</script>
2.递归求和
//递归实现n个数字的和
function getSum(x){
if(x==1){
return 1;
}
return x+getSum(x-1);
}
console.log(getSum(5));
常规求5个数的和
//求5个数字的和
var sum=0
for(var i=0;i<6;i++){
sum=sum+i;
}
console.log(sum);
求斐波那契数列:1,1,2,3,5,8…
function Fib(x) {
if(x == 1 || x == 2){
return 1
}
return Fib(x-1) + Fib(x-2);
}
console.log(Fib(12));
利用递归遍历数组
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
var data = [{
id: 1,
name: '家电',
goods: [{
id: 11,
gname: '冰箱',
goods: [{
id: 111,
gname: '海尔'
}, {
id: 112,
gname: '美的'
},]
}, {
id: 12,
gname: '洗衣机'
}]
}, {
id: 2,
name: '服饰'
}];
//1.利用forEach遍历每一个对象
function getID(json, id) {
var o = {}
json.forEach(function (item) {
if (item.id == id) {
o = item;
return item;
//2.想要得到里层11 12 可以利用递归
//里面应该有goods这个数组 应且长度不为0
} else if (item.goods && item.goods.length > 0) {
o = getID(item.goods, id) //拿到 return item 的值 并赋值给o
}
});
return o;
}
console.log(getID(data, 1));
console.log(getID(data, 2));
console.log(getID(data, 11));
console.log(getID(data, 12));
</script>
</body>
</html>
结果:
拷贝
两种数据类型
1.基本数据类型和引用数据类型
2.基本数据类型指的是,保存在栈中的简单数据类型
3.引用数据类型则是保存在堆内存中的对象,也就是说变量中保存的是指向该对象的指针
4.基本数据类型包括:
number
string
boolean
undefined
null
5.引用数据类型:
array
object
function
浅拷贝
1.深浅拷贝的问题根源是js中不同的数据类型在内存中的存储方式不一样。
基本数据类型是以名值得形式存储在栈内存中的。
let a = 1;
Name Value
a 1
进行赋值操作时会新开辟一片栈内存存储新的名值。
let a = 1;
let b = a;
Name Value
a 1
b 1
2.引用数据类型:数组、对象、函数等
以上的类型会在栈内存和堆内存中分别开辟一片空间进行存储。
let a = [1,3,5,7];
当直接赋值时其实是将a的堆地址赋值给了b,两者最终指向了同一个堆内存
let a = [1,3,5,7];
let b = a;
这就是浅拷贝,带来的后果就是对b的元素进行操作时,同时改变了a对应的值。
<script>
// 浅拷贝只是拷贝一层, 更深层次对象级别的只拷贝引用(地址).
// 深拷贝拷贝多层, 每一级别的数据都会拷贝.
var obj = {
id: 1,
name: 'andy',
msg: {
age: 18
}
};
var o = {};
// for (var k in obj) {
// // k 是属性名 obj[k] 属性值
// o[k] = obj[k];
// }
// console.log(o);
// o.msg.age = 20;
// console.log(obj);
console.log('--------------');
Object.assign(o, obj); //浅拷贝建议用法 ES6中新增方法
console.log(o);
o.msg.age = 20;
console.log(obj);
//修改 o的msg值 obj的msg也会修改 因为浅拷贝 拷贝的是同一个地址
</script>
深拷贝
1.简单粗暴,使用JSON.stringify和JSON.parse
进行两次转。
let a = [1,3,5,7];
let b = JSON.parse(JSON.stringify(a));
b[0] = 99;
console.log("a", a); // [1,3,5,7]
console.log("b", b); // [99,3,5,7]
缺点:转换时会自动忽略undefined,Symbol、function
2.用递归的方法去遍历复制所有的层级
function deepClone(obj){
// 判断数据形式
let clone = Array.isArray(obj)?[]:{};
if(obj && typeof obj === "object"){
for(key in obj){
if(obj.hasOwnProperty(key)){
// 属性是对象则进行递归
if(obj[key] && typeof obj[key] === "object"){
clone[key] = deepClone(obj[key]);
}else{
clone[key] = obj[key];
}
}
}
}
return clone;
}
let a = [1,3,5,7],
b = deepClone(a);
a[0] = 999;
console.log(a); // [1,3,5,7]
console.log(b); // [999,3,5,7]
3.for 循环:
<script>
// 深拷贝拷贝多层, 每一级别的数据都会拷贝.
var obj = {
id: 1,
name: 'andy',
msg: {
age: 18
},
color: ['pink', 'red']
};
var o = {};
// 封装函数
function deepCopy(newobj, oldobj) {
for (var k in oldobj) {
// 判断我们的属性值属于那种数据类型
// 1. 获取属性值 oldobj[k]
var item = oldobj[k];
// 2. 判断这个值是否是数组 先判断数组 在判断对象,因为数组也是对象
if (item instanceof Array) {
newobj[k] = [];
deepCopy(newobj[k], item)
} else if (item instanceof Object) {
// 3. 判断这个值是否是对象
newobj[k] = {};
deepCopy(newobj[k], item)
} else {
// 4. 属于简单数据类型
newobj[k] = item;
}
}
}
deepCopy(o, obj);
console.log(o);
var arr = [];
console.log(arr instanceof Object);
o.msg.age = 20;
console.log(obj); //此时不会改变
</script>
总结
1.在对象中赋值运算符 = 实现的是浅拷贝,只拷贝对象的引用值。
2.递归是做复杂深拷贝比较合理的方法。
3.JSON深拷贝只能是对象中没有function时可以使用。
4.数组的深拷贝方法较多,但是大多是只能进行第一层的深拷贝。