theme: fancy
highlight: agate
内存管理
基本数据类型在内存中的存放方式
复杂数据类型在内存中的存放方式
V8引擎有多大 64位有1.4G,32位700M,不同浏览器还有些许扩容
其中又分为新生代:短时间存活的新变量会存在新生代中,新生代的内存量极小,64位下大概是32MB
老生代:生存时间比较长的变量,会转存到老生代,老生代占据了几乎所有内存。64位下大概是1400MB
新生代的回收算法:分为两个空间to,from,一开始都存放在from空间,死亡的变量依然放在from空间,把存活着的变量复制到to空间,from
空间清空,然后对调from和to。这样可以提升回收速度,空间换时间。
老生代的回收算法:标记已死变量,清除已死变量,整理磁盘(否则内存碎片太多,浪费空间)
新生代转换为老生代:这个对象经历过一次新生代的回收(即在 from 和 to空间内进行互换一次),并且新生代发现本次复制,会占用超过百分之25的to空间。
什么时候会触发回收:执行完一次代码或者内存不够时(这个情况比较极端)
变量什么时候会被回收:全局变量会直到程序执行完毕才会回收。普通变量,就是当他们失去引用
优化:
- 尽量不要定义全局,定义了及时手动释放。
- 注意闭包
Node端的一些特殊点:
- Node可以手动进行垃圾回收
- Node可以设置新老生代内存大小
为什么V8设置成1.4G:
- 对于浏览器脚本够用
- 回收的时候是阻塞式的,也就是进行垃圾回收的时候会中断代码的执行。如果不限制内存大小,垃圾回收时间变长。
高阶函数
一个函数接收另一个函数作为参数
手写forEach,map
forEach接受一个函数,这个函数参数有遍历的那一项,序号
Array.prototype.myForeach=function(callback){
var len = this.length//数组长度,this代表调用函数的数组
if(typeof callback !== 'function'){
throw new Error("Invalid callback")
}//健壮性
for (let i = 0; i < len; i++) {
callback.call(this,this[i],i)
}
}
与forEach不同map会返回一个新数组
Array.prototype.myMap = function (callback) {
var len = this.length;
var arr = [];//与forEach相比只需要返回一个新数组
if (typeof callback !== "function") {
throw new Error("Invalid callback");
}
for (let i = 0; i < len; i++) {
arr.push(callback.call(this, this[i], i));//注意这里是浅引用,要换成深拷贝
}
return arr
};
手写reduce
累计求和,求积,或者对象的转化
reduce(pre,now,index) :pre是上次循环的返回值,now是当前循环值,index是数组下标,后面,还有一个参数作为开始的默认值,如果不指定 默认数组第一项为默认值,函数直接从第二项开始.
//对象转化
var arr = [
{
path: "/", component: "login" },
{
path: "/sign", component: "sign" },
];
console.log(
arr.reduce((pre, now) => {
pre[now.path] = now.component;
return pre;
}, {
})
);
Array.prototype.myReduce = function (callback, init) {
var i = 0;
var len = this.length;
var pre = init;
if (typeof callback !== "function") {
throw new Error("Invalid callback");
}
if (init == undefined) {
i = 1;
pre = this[0];
}
for (; i < len; i++) {
pre = callback.call(this, pre, this[i]);
}
return pre;
};
手写filter
var arr = [
{
path: "/", component: "login" },
{
path: "/sign", component: "sign" },
];
Array.prototype.myFilter = function (callback) {
var len = this.length;
var arr = [];
for (let i = 0; i < len; i++) {
if (callback.call(this, this[i], i)) {
arr.push(this[i]);//这里要深拷贝
}
}
return arr;
};
console.log(arr.myFilter((i) => i.path == "/sign"));
函数科里化
使用场景:函数在很多情况下调用参数固定
call()
立即调用一个函数,并允许您设置该函数的this
值。bind()
返回一个新的绑定了特定this
值的函数,而不是立即调用原始函数。- 不要写成test().bind(),因为我们需要是改变函数指向而不是执行这个函数在调用这两个方法
- 例如
function test(a, b, c, d) {
console.log(this.count);
console.log(a);
console.log(b);
console.log(c);
console.log(d);
}
test.bind({
count: 2 }, 3, 4)(1, 2);
// 结果是 2 3 4 1 2
手写bind
Function.prototype.myBind = function (obj) {
if (typeof this !== "function") {
return;
}
var fn = this;
var args = Array.prototype.slice.call(arguments, 1);
return function () {
return fn.apply(
obj,
args.concat(Array.prototype.slice.call(arguments))
);
};
};