数据存储
1.JavaScript数据类型
js中共有8种数据类型。7种原始数据类型,1种引用数据类型。
标题 | 属性 |
---|---|
Boolean | 只有true或false |
null | 空对象指针 |
undefined | 声明但未被赋值的变量默认为undefined,变量提升时的默认值也为undefined |
number | 数字类型 |
bigint | 大数类型,在数字后加n即代表当前是大数类型,只能相同类型进行操作 |
string | 字符串类型 |
symbol | 唯一且不可修改的 |
object | 引用数据类型,对象,包含Date、function、array等 |
2.判断数据类型的方法
typeof——主要用于判断原始数据类型
- typeof 数据 === ? |名称|返回值是否正确| |---|---| |基本数据类型(除null)|√| |null|×,返回object| |引用数据类型(除function)|×,均返回object| |function|√,function|
instanceof:检测某一个实例的原型链上是否有这个类的原型属性
- [数据] instanceof [类名]//[] instanceof array 能够区分数组和常见的对象等,但是对于原始数据类型来说,过程更为复杂,不建议使用。
原理:左边这个数据的原型链上是否有右边这个类的原型属性。
function instanceMy(left,right){
let proto = left.__proto__;
let prototype = right.prototype;
while(true){
if(proto == null) return false;
if(proto == prototype) return true;
proto = proto.__proto__;
}
}
constructor——用于引用数据
- 数据.constructor === ? ※原型链不会对其进行干扰
[].constructor === Object//false
在我们的构造函数原型上,会有一个constructor是指向构造函数自身的,因此使用我们的实例.constructor会直接使用这个属性,并返回这个构造函数自身。
Object.prototype.toString.call()————适用于所有的数据类型
- Object.prototype.toString.call(数据) 该方法可以检测出所有的数据类型。object.prototype.toString会返回一个对象类型的字符串,通过.call可以改变this的指向,使得其返回对应的数据的数据类型。
3.内存空间
内存空间分配
js中有3类空间,代码空间、栈空间、堆空间。
- 代码空间中保存代码。
- 栈空间:栈空间中存执行上下文,执行完后弹出,先进后出。
- 堆空间:存储复杂数据类型。
栈和堆空间
对于我们的foo函数,此时有
function foo(){
var a = "闷倒驴";
var b = a;
var c = {name:"王美丽"};
var d = c;
}
foo();
可以发现在栈中,基本数据类型存储的是数据的值,而对于复杂数据类型,栈中存的是数据的地址。
不把栈中的数据放在堆中的原因:
js使用栈来维护程序执行期间上下文的的状态,若将所有的数据放在栈中,此时栈的数据变大,会影响上下文切换的效率,进而影响程序的执行效率。
赋值
在我们的赋值操作中,对于基本类型来说,赋值操作是直接将数据a在栈中的数据给了数据b。但是对于引用类型来说,赋值操作是将保存在栈中的数据a的地址赋值给了b,那么b中得到的其实是数据a的地址,因此一旦我修改了对应的地址中的数据,我们的数据a|b都会发生改变。
深浅拷贝
浅拷贝
对于拷贝的引用数据类型来说,如果我们要拷贝的对象,它既包含基本类型,又包含有对象。我们的浅拷贝则会开辟的新的堆栈,在我们的栈中,基本数据类型其数据被复制了过来。但是对于对象,我们浅拷贝拷贝过来的这个对象的地址。他们指向的仍然是同一块堆。
var user = {
userName:"闷倒驴",
sex:"女",
body:{
weight:"50kg",
height:"160"
}
};
userCopy = Object.assign({},user);
浅拷贝方法
- object.assign()
- lodash的.clone方法
- 展开运算符...
- concat()
- slice
深拷贝
深拷贝就是在我的拷贝之后,我新建了所有的空间地址等,我只是将你的数据拷过来了,但是之后再也没有了联系。
深拷贝方法
- Json.parse(Json.stringfy()),可以用来实现数组或对象的深拷贝,但是不能用来处理函数和正则。
- lodash的._deepClone
- jQuery的extend
- 手写深克隆 递归一直找,直到为基本数据类型,使用WeakMap来处理循环引用。
function deepClone(obj, hash = new WeakMap()) {
if(obj === null) return obj;
if(obj instanceof Date) return new Date(obj);
if(obj instanceof RegExp) return new RegExp(obj);
if(typeof obj !== "object") return obj;
if(hash.get(obj)) return hash.get(obj);
let cloneObj = new obj.constructor();
hash.set(obj,cloneObj);
for(let key in obj){
if(obj.hasOwnProperty(key)){
cloneObj[key] = deepClone(obj[key],hash);
}
}
return cloneObj;
}
let obj = { name: 1, address: { x: 100 } };
obj.o = obj; // 对象存在循环引用的情况
let d = deepClone(obj);
obj.address.x = 200;
console.log(d);
垃圾回收机制
垃圾回收的方式
垃圾回收分为手动垃圾回收(C语言)和自动垃圾回收(JS)两种方式。
js中垃圾回收的方式
- 调用栈中的垃圾回收
- 堆中的垃圾数据回收
调用栈中垃圾数据回收方式
function foo(){
var a = 1;
var b = {name:"王美丽"};
function showName(){
var c = 2;
var d = {name:"闷倒驴"};
};
showName()
};
foo()
当一个函数执行结束后,js引擎通过移动我们的esp指针来销毁函数在栈中的执行上下文。