首頁  >  文章  >  web前端  >  js中記憶體有什麼用?了解一下js記憶體的作用

js中記憶體有什麼用?了解一下js記憶體的作用

不言
不言原創
2018-08-16 15:09:251513瀏覽

這篇文章帶給大家的內容是關於js中記憶體有什麼用?了解一下js記憶體的作用,有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。

在JS進行開發的過程中, 了解JS記憶體機制有助於開發人員能夠清晰的認識到自己寫的程式碼在執行的過程中發生過什麼, 也能夠提高專案的程式碼品質.

JS記憶體是怎麼樣的?

JS中變數存放有著原始值與引用值之分:

  • 原始值: 原始的資料型別: undefinednullnumberstringboolean以及es6新加入的symbol.

  • 引用值: object#arrayfunction等類型的值便是引用值.

JS中的記憶體也分為堆疊記憶體與堆疊記憶體.堆與堆疊詳細了解檢視這裡.
eg:

const str = '我是说明内存的文档'; // 这里 str 以及 '我的说明内存的文档' 都存储在栈内存当中
const obj = { a: 1 }; // 这里 obj(指向存储在堆中的数据的指针) 是存储在栈内存 而 { a: 1 } 则存储在堆当中

記憶體中的儲存對象宣告週期是怎麼樣的呢?

MDN中的介紹:

  1. #當物件將被需要的時候為其分配記憶體.

  2. 使用已分配的記憶體(讀、寫操作)

  3. 當物件不再被需要的時候, 釋放儲存這個物件的記憶體

#1 2在所有語言中都是一樣的, 3在JS當中不是那麼明顯

看看內存中發生了什麼?

let str1 = 1; // 为str1分配栈内存 str1: 1
let str2 = str1; // 原始类型直接访问值, 即为str2新分配栈内存: str2: 1

str2 = 2; // 栈内存: str2: 2. str2的值为2, 而str1的值仍然是1

/************************ 我是分割线: 上面为原始类型 下面为复杂类型 *******************************/

let obj1 = { a: 1 }; // 为obj1分为栈内存访问地址的指针: obj1. 堆内存中存储对象值: { a: 1 }
let obj2 = obj1; // 为obj2分配栈内存访问地址的指针: obj2. 引用了堆内存中的值{ a: 1 }

obj2.a = 2; // 通过obj1修改堆内存的数据, 由于obj2与obj2都是指向堆内存中的同一个数据的指针(也叫引用). 所以堆内存中的值{a: 1}修改为{a: 2} 即 obj1.a 为 2; obj2.a 也为 2; (这里它们是指向了堆内存中的同一个数据的不同指针)

obj2 = { a: 3 }; // 因为改的是整个对象, 这里会在堆内存中创建一个新的对象值: {a:3}, 而obj2引用的是这个新对象, 所以obj1.a 依旧为 2; 而obj2.a 则为 3了. (这里它们是指向了堆内存中的不同数据的不同的指针)

然後看看這個問題:

let a = { n: 1 };
let b = a;
a.x = a = { n: 2 };

具體查看詳細解釋, 對理解基礎知識點還是很有幫助的. 例如: js的賦值運算順序永遠都是從右往左的,但是.是優先權最高的運算子.

從記憶體角度看函數傳值的變化

關於傳值/址的解說. 用原始型別來區分. 原始型別傳的是值, 引用型別傳的則為址.

let str = '我是初始字符串';
let fn = (arg) => {
    console.log(arg); // #1 我是初始字符串

    arg = '我是修改后的字符串';
    console.log(arg); // #2 我是修改后的字符串
    console.log(str); // #3 我是初始字符串
};
fn(str);

上面範例#1可以看到傳入fn的是str的值, 在堆疊記憶體中分配了新的空間來保存函數參數和其值(函數運行後自動釋放這部分記憶體, _垃圾回收機制_).所以在#2出輸出的值為我是修改過的字串. 在呼叫函數fn時給參數arg傳了值(在堆疊記憶體中新分配的資料), 而str又為原始型別. 在#3處輸出與初始化定義保持一致.

let obj = { a: 1 };
let fn = (arg) => {
    arg = { a: 2 };
};

fn(obj);
// 这个时候obj还是{a: 1}

let fn1 = (arg) => {
    arg.a = 2;
};
fn1(obj);
// 这个时候obj则为{a: 2}

上面這個範例中的兩個函數都是傳址, 起初傳入的參數arg都是引用(指向堆記憶體中的同一個資料的指標), 在fn#中重新為變數arg賦值新的物件(引用型別). 而在fn1#中的arg依舊是引用(指向堆記憶體中資料的指標),所以fn1中是修改成功的.

垃圾回收機制

JS具有垃圾回收機制, 這給開發人員帶來了極大的方便, 至少不用太考慮記憶體釋放的問題(有部分還是要考慮的).

  • 函數的變數只在函數執行過程中存在. 在函數執行過程中, 函數內部的變數將會在記憶體中分配一定的空間, 當函數執行完畢後, 自動將這些變數從記憶體釋放, 以留出空間作其它用處.

  • 當記憶體中某個變數不再被引用, JS將清理掉這部分記憶體的分配. eg:

let obj = { a: 1 }; // 内存中存在{a: 1}对象, 以及obj这个引用地址
obj = { a: 2 }; // 垃圾回收机制自动清理{a: 1}, 并为新的有用的{a: 2}分配空间

記憶體最佳化

#就全域變數而言, JS不能確定它在後面是否用到, 所有它從聲明之後就一直存在於內存中, 直至手動釋放或者關閉頁面/瀏覽器, 這就導致了某些不必要的內存消耗. 我們可以進行以下優化.
使用立即執行函數

(() => {
    // do something...
})();

手動接觸變數的引用

let obj = { a: 1, b: 2, c: 3 };
obj = null;

在JS中, 閉包是最容易產生記憶體問題的, 我們可以使用回呼函數代替閉包來存取內部變數. 使用回呼的好處就是(針對存取的內部變數時原始類型的值, 因為在函數傳參的時候傳的是值), 在執行後會自動釋放其中的變數, 不會像閉包一樣一直將內部變數存在於記憶體中(但如果是引用型別, 那麼這個被引用的物件依舊在記憶體中).

function fn() {
    var val = '你好';
    return function() {
            return val
        };
};
var getVal = fn();
var v = getVal(); // 你好

上面範例, 雖然函數fn已經執行完畢, 但是對於函數中變數val的參考還在, 所以垃圾回收機制不會將函數中的val回收.

##使用回呼

function fn1(cb) {
    var val = '你好';
    return cb(val);
};
function fn2(arg) {
    return arg;
};
var v = fn1(fn2);
同時聲明, 並不是說明這樣做就一定比閉包好, 閉包也有其好處, 只是需要我們在分清在最恰當的時候使用.

#相關推薦:

JS記憶體管理實例講解

#JS類別庫Bindows1.3中的記憶體釋放方式分析_javascript技巧

JS記憶體管理實例講解#

以上是js中記憶體有什麼用?了解一下js記憶體的作用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn