一.内存
①普及:
1)、cpu:负责执行程序(包括运算,读取内存等等)
2)、内存:只负责数据的存储。
②内存:
1)、计算机的内存会分为无数小的区域(格子)
2)、内存有分区:不同的区域放置不同种类的数据。
在js里重点关注内存的两个:栈区和堆区。
3)、计算机对内存中的每个格子有个编号。这个编号就是地址。
③栈区存储
变量,形参;即:当定义变量(形参)时,会在栈区申请空间
④堆区存储:new出来的数据
二.基本类型和引用类型的区别(重点说内存上)
①概念
1)、基本类型(数据)也叫值类型。在内存存储的是值。
2)、引用类型也叫地址类型,在内存中存储的是地址。
②内存存储
1)、基本类型的变量对应的内存区域存储的是值。
2)、引用类型的变量对应的内存区域存储的是地址。
③读取变量:
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函数递归调用的示意图(阶乘为例)