基础存储单元
位(bit):二进制数中的一位,可以是0或者1,是计算机中数据的最小单位,简称b。
字节(Byte):计算机中数据的基本单位,每8位组成一个字节,简称B。各种信息在计算机中存储、处理至少需要一个字节。例如,一个ASCII码用一个字节表示,一个汉字用两个字节表示。
内存占用
在这里,我们只讨论四种基本类型,String,Number,Boolean,Object,Array
String:2个字节
我们都知道,计算机内部,所有的信息最终都是一个二进制值。每个二进制位有0和1的状态,所以八个二进制就可以组合出256种状态,这就称为一个字节。
上世纪60年代,美国制定了一套字符编码,将英文字符和二进制位之间一一对应,被称为ASCII码,一直沿用至今。
但这用来表示中文字符,一个字节远远不够,所以中国就制定了GB2312编码用来表示中文字符。其他国家也因为同样的原因制定了自己的编码规则。
世界上存在着多种编码方式,同一个二进制数字可以被解释成不同的符号。因此非常容易因编码冲突导致乱码的出现。
为了解决乱码的问题,Unicode编码应运而生。它将世界上所有的符号都纳入其中,每一个符号都给予一个独一无二的编码,采用2个字节表示一个字哦福,最多可以表示65535个字符。
JS采用的也是Unicode编码,所以JS中String类型占用2个字节。
Number:8个字节
JS中的Number类型采用IEEE754标准中的双精度浮点数来表示一个数字,不区分整数和浮点数。
在IEEE754中,双精度浮点数采用64位存储,即8个字节表示一个浮点数。其存储结构如下图所示:
Boolean:4个字节
Boolean实际上只有一个字节,但在不同平台由于对齐的缘故,会导致分配的内存至少占用4到8个字节,并且以最低有效位和3位为零的方式对齐。
A boolean is actually 1 byte. But alignment may cause 4 bytes to be used on a 32-bit platform or 8 bytes on a 64-bit platform. This old trick comes from the observation that allocated memory takes up at least 4 or 8 bytes, and are aligned in the way that the least significant bit or three will be zero.
详情查看(需要梯子)
实现
思路
考察计算机基础,js内存考察 递归 边界处理
1.了解各个类型所占用字节数 2.根据各个类型分开判断,
- String需要字符串长度*2;
- Number直接return 8;
- Boolean直接return 4;
- Array需要考虑对象数组,数字数组,字符串数组等,使用reduce累加即可;
- Object需要考虑null,null的话直接return 0;非null,因为key和value都是占用内存空间的,所以对key和value都进行累加。(注意:两个key有可能引用一个value,所以需要Set来存储这唯一value)。
代码
// 用来存储已经被引用的变量
const seen = new Set();
function sizeOfObject(obj) {
if (obj === null) return 0;
let bytes = 0;
//对象里的key也占用内存空间
const keys = Object.keys(obj);
//遍历获取,累加key和value所占用的字节
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
bytes += calculator(key);
if (Object.prototype.toString.call(obj[key]) === 'Object') {
//如果已经存在,就直接跳出这次循环
if (seen.has(obj[key])) continue;
seen.add(obj[key]);
}
bytes += calculator(obj[key]);
}
return bytes;
}
function calculator(params) {
const paramsType = Object.prototype.toString.call(params).slice(8, -1);
switch (paramsType) {
case 'String':
return params.length * 2;
case 'Number':
return 8;
case 'Boolean':
return 4;
case 'Object':
return sizeOfObject(params);
case 'Array':
// 需要考虑对象数组,数字数组,字符串数组等情况
//[1,2,3,4,5]
//[{x;1}, {x;2}]
return params.map(calculator).reduce((prev, curr) => prev + curr, 0);
default:
//默认返回0
return 0;
}
}
// 测试
const testObj = {
a: 111,
b: 'cccx',
2222: false,
xxxx: true,
};
const str = 'str';
const num = 123;
const boo = true;
console.log(`calclator(testData)`, calculator(testObj)); //44
console.log(`calclator(str)`, calculator(str)); //6
console.log(`calclator(num)`, calculator(num)); //8
console.log(`calclator(boo)`, calculator(boo)); //4
为什么Object.prototype.toString.call(params)可以判断类型?
这是调用了Object原型链的toString方法,所以对于Object是可以判断出来的。而能够判断基础类型,是因为它对值进行了包装。
「那么,什么是包装对象:」
所谓“包装对象”,指的是与数值、字符串、布尔值分别相对应的Number、String、Boolean三个原生对象。这三个原生对象可以把原始类型的值变成(包装成)对象。 详情参考