JS底层机制

JavaScript的执行机制-eventloop


setTimeout(function(){
    console.log('set1');
})

new Promise(function(resolve){  
    console.log('pr1');  // 至关于同步代码
    resolve()
}).then(function(){
    console.log('then1');
})

setTimeout(function(){
    console.log('set2');
})

new Promise(function(resolve){  
    resolve()
}).then(function(){
    console.log('then2');
})

console.log(3)
// 微 ['then1','then2'],宏 ['set1','set2']
// pr1 3 then1 then2 set1 set2
// 加强版
setTimeout(function(){
    console.log('set1');
    new Promise(function(resolve){  
        resolve()
    }).then(function(){
        console.log('then3');
    })
})

new Promise(function(resolve){  
    console.log('pr1');  // 至关于同步代码
    resolve()
}).then(function(){
    console.log('then1');
})

setTimeout(function(){
    console.log('set2');
})

new Promise(function(resolve){  
    resolve()
}).then(function(){
    console.log('then2');
})

console.log(3)
// 第一遍 微 ['then1','then2'],宏 ['set1','set2']
// 第2遍,执行宏任务时会再次把微任务插入到微任务队列 微 ['then3'],宏 ['set1','set2']
// pr1 3 then1 then2 set1 then3 set2

image

  • 微任务:Promise,process.nextTick
  • 宏任务:总体代码script,setTimeout,setInterval

微任务会先于宏任务执行node

微任务队列空了才去执行下一个宏任务c++

async function a(){
    console.log('async')
}
a();
console.log(3)  // async 3
async function a(){
    // await 从使用上来讲,必须等待一个promise
    var b = await new Promise(function(resolve){
        resolve(7)
    })
    console.log(5)
    console.log(b)
}
a();
console.log(3)   // 3 5 7
for(var i = 0; i < 10; i++){
    setTimeout(() => {
        console.log(i)
    });
}
// 10次10

// 闭包解决
for(var i = 0; i < 10; i++){
    (function(i){
        console.log(i)
    })(i)
}
// 最好是使用let造成块级做用域

做用域链与引用类型


var a = [1,2,3]
function f(){
    a[3] = 4;
    a = [100];
}
f();
console.log(a)  // [100]
var a = [1,2,3]
function f(a){
    a[3] = 4;
    a = [100];
}
f(a);  
console.log(a)  // [1,2,3,4]
// 解读
var a = [1,2,3]
function f(a){
    var a = a;  // 隐式有句代码 把外部的a 赋值给 局部的a
    a[3] = 4;  // 由于是引用类型,外部的a和内部的a都变成 [1,2,3,4]
    a = [100];  // 给局部的a赋值,局部的a切断了和外部a的联系
    console.log(a)  // 打印的是局部的a [100]
}
f(a);  
console.log(a)  // 是所有的 a [1,2,3,4]

原理:

  • 对象是引用类型
let a = [1,2,3];
let b = a;  // 是把数组的内存地址指向b
a[3] = 4; // 全部修改a b也会变,由于内存地址变掉了
  • 参数在方法内,至关于一个局部变量

思考:

问题一:js是如何查找变量的?

从当前做用域出发,逐级向上查找,直到window,若是window也没有,那就是undefined数组

var c = 123;
function a(){
    console.log(c);  // 123
}
var c = 123;
function a(){
    var c = 456;
    console.log(c);  // 456
}
问题二:JavaScript的数组并非数据结构意义上的数组,为何?
  1. 数据结构意义上的数组是连续相等的内存变量,定义的时候就规定大小,类型
  2. 真正的数组是不能够扩容的。
问题三:数据结构上扩容一个数组,内存作了啥?
  1. 从新申请一个要扩容大小的内存
  2. 再把扩容前的内容复制过来,而后再写入要扩容的内容

难题来喽

var a = {
    n: 1
}
var b = a;
a.x = a = {
    n: 2
}
console.log(a.x);  // undefined
console.log(b.x);  // {n: 2}
// 解析
var a = {
    n: 1
}
var b = a;
// a.x .号运算优先级别最高
// a.x 会在原来的内存地址中,申请一块新的内存地址
a.x = a = {
    n: 2
}

image

V8引擎内存问题


var size = 20*1024*1024;
var arrAll = [];
for(var i = 0; i < 20; i++){
    arrAll.push(new Array(size));
}

image

知识点:v8引擎64位只有1.4g的内存能够支配,node可使用C加加的内存,node源码是用c++写的promise

内存如何回收?

image

为何等内存满了后才回收?

由于JavaScript进行一次回收要把整个js暂停,因此不能常常回收,回收100m内存,大概须要10ms浏览器

内存查看

  • 浏览器 window.performance
  • Node process.memoryUsage()
function getMemory() {
    var mem = process.memoryUsage();
    var format = function(bytes) {
        return (bytes / 1024 / 1024).toFixed(2) + "MB";
    }
    console.log('heapTotal' + format(mem.heapTotal) + 'heapUsed:' + format(mem.heapUsed));
}

var size = 20 * 1024 * 1024;
var arrAll = [];
for (var i = 0; i < 20; i++) {
    getMemory();
    arrAll.push(new Array(size));
}

image

如何解决

若是不确认本身的数据放在全局是否是很大,可作一些大小的限制缓存

var size = 20 * 1024 * 1024;
var arrAll = [];
for (var i = 0; i < 20; i++) {
    if(arrAll.length > 4){
        arrAll.shift();
    }
    arrAll.push(new Array(size));
    getMemory();
}

image

由上图能够看出,虽然数据进行了限制,被删除了,仍是在内存中的,尚未被回收,直到内存快满的时候才进行的回收,把没用的回收掉,内存才回归到一个真实可以使用的状态数据结构

总结:在用node写服务时,只要服务开着,全局就不会回收闭包

容易引起内存使用不当的场景

  1. 滥用全局变量
  2. 缓存不限制
  3. 操做大文件
相关文章
相关标签/搜索