JS--Day22(内存的深化递归深拷贝浅拷贝proxy)

一.内存

①普及:

    1)、cpu:负责执行程序(包括运算,读取内存等等)

    2)、内存:只负责数据的存储。

②内存:

    1)、计算机的内存会分为无数小的区域(格子)

    2)、内存有分区:不同的区域放置不同种类的数据。

              在js里重点关注内存的两个:栈区和堆区。

    3)、计算机对内存中的每个格子有个编号。这个编号就是地址。

③栈区存储

             变量,形参;即:当定义变量(形参)时,会在栈区申请空间

④堆区存储:new出来的数据

二.基本类型和引用类型的区别(重点说内存上)

①概念

    1)、基本类型(数据)也叫值类型。在内存存储的是值。

    2)、引用类型也叫地址类型,在内存中存储的是地址。

②内存存储

    1)、基本类型的变量对应的内存区域存储的是值。

    2)、引用类型的变量对应的内存区域存储的是地址。

扫描二维码关注公众号,回复: 15120635 查看本文章

③读取变量:

    1)、基本类型读取数据时,是直接读取。

    2)、引用类型读取数据时,是间接读取。

④变量赋值:

    本质:不管是基本类型还是引用类型,赋值时,都赋的是变量对应内存存储的内容;

    只不过:

    1)、基本类型对应内存存储的是值

    2)、引用类型对应内存存储的是地址。

⑤比较两个变量:

    不管是基本类型还是引用类型,比较时,都是比较变量对应内存存储的内容;

⑥函数传参

    本质:不管是基本类型还是引用类型,传参时,都传的是实参对应内存存储的内容;

    1)、基本类型作为函数的参数传的是值

    2)、引用类型作为函数的参数传的是地址。

var num = 10;
var n = num;//把num变量对应的内存区域存储的内容(值)赋给n;

var p = {
    name:"张三疯",
    age:18
}
var p2 = p;//把p变量对应的内存区域存储的内容(地址)赋给p2;
console.log(p2==p);//true
var p3 = {
    name:"张三疯",
    age:18
}
console.log(p==p3);//false

三.函数的递归调用

①函数的递归调用

    1)、函数调用自身

    2)、在递归函数里必须要有一个分支是不调用自身,否则,就是成了无限调用,造成(内存)               栈溢出。

②循环和递归的选用:

    如果循环和递归都能够解决问题。

    1)、如果考虑性能:循环。

    2)、如果不考虑性能:哪个简单,选哪个。

③递归解决的问题场景:树形结构。


// 1、用递归的方式解决阶乘=====================================================
//    0!=1;
//    1!=1;
//    2!= 2*1 = 2*1!
//    3!= 3*2*1 = 3*2!
//    4!= 4*3*2*1 =4*3!
//    5!= 5*4*3*2*1 = 5*4!;
//    n! = n* (n-1)!

// 定义一个函数:求n 的阶乘;
 function factorial(n){
     if(n==1 || n==0){
         return 1;
     }
     return n*factorial(n-1);
 }

// 2、用递归的方式求第n个斐波那契数列的数========================================
// 1  1  2   3   5   8   13  21。

// 1)、用递归的方式解决
function feiBNQ(n){
    if(n==1 || n==2){
        return 1;
    }
    return feiBNQ(n-1)+feiBNQ(n-2);
}
// 2)、用循环的方式解决
function feiBNQ02(n){
    if(n==1 || n==2){
        return 1;
    }
    let n1 = 1;
    let n2 = 1;
    let n3;
    for(let i=2 ;i<n;i++){
        n3 = n1+n2;
        n1 = n2;
        n2 = n3;
    }
    return n3;
}

四.深拷贝和浅拷贝

①拷贝:复制。

②深拷贝和浅拷贝:深拷贝和浅拷贝是针对引用类型说的。
 

var p1 = {
    name:"张三疯",
    age:12,
    wife:{
        name:"梅超风",
        brother:{
            name:"梅疯",
        }
   }
}

③递归的方式做深拷贝:

   功能:对一个对象做深拷贝(复制)

   参数:被复制的对象

   返回值:复制出来的对象。新对象和源对象不能有任何的内存共享

function deepCopy(obj){
    let newObj = {};
    for(let key in obj){
        if(typeof obj[key] == "object"){            
            newObj[key] = deepCopy(obj[key]);
        }else{
            newObj[key] = obj[key];
        }
    }
    return newObj;
}
let newObj = deepCopy(p1);
p1.wife.brother.name="没有风";
console.log("p1.wife.brother.name",p1.wife.brother.name);
console.log("newObj.wife.brother.name",newObj.wife.brother.name);

五.Proxy

①Proxy可以代理一个对象

②当访问Proxy对象时,会自动调用一些方法:

    1)、当使用Proxy对象,修改属性时,会调用set方法

    2)、当使用Proxy对象,读取属性时,会调用get方法

    3)、当使用in运算符判断Proxy对象上是否有某个属性是,会调用has方法

③一般情况下,代理对象会具有源对象的一切属性和方法。

    通过 console.dir();打印代理对象,

    1)、在代理对象有类型的信息,那么,就可以。

    2)、如果没有类型的信息,那么不可以。

// 一、proxy的基本使用==============================================================
 let doctor = {
     name:"丁丽娟",
     sex:"女",
     age:12
 }
 let p = new Proxy(doctor,{
 });
console.log("p.name",p.name);//但访问代理对象的name属性时,代理对象会找源对象的name属性
// 二、proxy的第二个参数:setter和getter=============================================
let doctor = {
    name:"丁丽娟",
    sex:"女",
    age:12
}
let p = new Proxy(doctor,{
    set:function(target,key,value){
        console.log("set函数",key,value);
        if(key=="age"){
            if(value<0 || value>150){
                return;
            }
        }
        target[key] = value;//这是默认的代码,如果我们要重写set函数,就得写上这句话
    },
    get:function(target,key){
        // console.log("get函数",key);
        return target[key];
    }
})
 console.log("p.name",p.name);//调用get方法
 console.log("p.sex",p.sex);//调用get方法
 p.name="梅超风";//调用set方法
 p.age = 25;
 console.log("p.age",p.age);
 p.age = 250;
 console.log("p.age",p.age);

// 三、代理对象会具有源对象的一切属性和方法(?要看情况)==========================
// 通过 console.dir();打印代理对象,在代理对象的[[target]]里如果有类型的信息,
//   就可以。如果没有类型的信息,不可以。
// 1、代理数组,那么代理对象就会拥有数组的一切属性和方法
let arr = [12,23,34];
let p = new Proxy(arr,{});
console.dir(p);
 p.push(300);
 console.log("arr",arr);
 console.log("p.length",p.length);
// 2、代理日期:代理对象没法访问日期对象的属性和方法
let d1 = new Date();
let p1 = new Proxy(d1,{});
 console.log(p1.getFullYear());
 console.dir(p1);
// 3、代理一个自定义类型:代理对象可以访问自定义类的所有属性和方法。
class Person{
    constructor(name){
        this.myname=name
    }
    eat(){
        console.log("eat");
    }
}
let person1 = new Person("梅超风");
let p3 = new Proxy(person1,{});
console.dir(p3);
console.log("p3.myname",p3.myname);
p3.eat();
// 四、has:=========================================================================
//  1、每个对象都有一个in运算符。in运算符是判断一个对象里是否有某个属性
//  2、在代理对象上使用 in 运算符,会调用has方法。
 console.dir(Proxy);
let doctor = {
    name:"丁丽娟",
    sex:"女",
    age:12
}
 console.log("sex" in doctor);
 if("sex" in doctor){
 }
let p4 = new Proxy(doctor,{
     has:function(target,key){
        console.log("has");
        return key in target;//判断target对象里是否有 属性 key。
    }
});
console.dir(p4);
console.log(p4.sex);
console.log("sex" in p4);//在代理对象上使用in运算符,会调用代理对象的has方法。

01原型内存示意图

 02引用类型变量的内存

03基本类型和引用类型作为函数的参数的内存示意图

 04函数递归调用的示意图(阶乘为例)

猜你喜欢

转载自blog.csdn.net/weixin_72756818/article/details/129823370