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. 操做大文件
相關文章
相關標籤/搜索