>웹 프론트엔드 >JS 튜토리얼 >js에서 메모리는 어떻게 사용되나요? js 메모리의 역할 이해

js에서 메모리는 어떻게 사용되나요? js 메모리의 역할 이해

不言
不言원래의
2018-08-16 15:09:251518검색

이 글의 내용은 js에서 메모리의 용도가 무엇인지에 관한 것입니다. js 메모리의 역할을 이해하는 데 일정한 참고 가치가 있습니다. 도움이 필요한 친구들이 참고할 수 있기를 바랍니다.

JS 개발 과정에서 JS 메모리 메커니즘을 이해하면 개발자가 자신이 작성한 코드를 실행하는 동안 어떤 일이 발생했는지 명확하게 이해할 수 있으며, 프로젝트의 코드 품질도 향상시킬 수 있습니다.

JS 메모리는 어떻게?

변수입니다. JS의 저장은 원래 값과 참조 값으로 구분됩니다:

  • 원래 값: 원래 데이터 유형: undefine, null, number, string, boolean 및 es6.undefinednullnumberstringboolean以及es6新加入的symbol.

  • 引用值: objectarrayfunction等类型的值便是引用值.

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에 새로 추가된 symbol

인용 값: 객체 code>, array, function 및 기타 유형의 값은 참조 값입니다. JS의 메모리도 다음과 같이 구분됩니다. 스택 메모리와 힙 메모리에 대한 자세한 이해는 여기를 참조하세요.

eg:

function fn1(cb) {
    var val = '你好';
    return cb(val);
};
function fn2(arg) {
    return arg;
};
var v = fn1(fn2);
메모리에서 저장 개체의 선언 주기는 무엇인가요?

MDN 소개:

    필요할 때 개체에 메모리를 할당합니다.

    할당된 메모리를 사용합니다(읽기, 쓰기 작업)
    개체에 더 이상 시간이 필요하지 않을 때 , 이 객체를 저장하는 메모리를 해제하세요

1 2는 모든 언어에서 동일하지만 3그렇지 않습니다 JS에서는 너무나 당연합니다메모리에서 무슨 일이 일어나는지 보세요?

rrreee🎜그런 다음 이 질문을 보세요:🎜rrreee🎜자세한 설명을 보세요. 기본 지식 포인트를 이해하는 데 여전히 매우 도움이 됩니다. 예: js 할당 연산 순서는 항상 오른쪽에서 왼쪽이지만 .가 우선순위가 가장 높은 연산자입니다.🎜🎜메모리 관점에서 함수 값 전송의 변화를 살펴보세요🎜🎜값/주소 전송에 대한 설명. 기본 유형과 참조 유형을 구분하여 사용하세요. 기본 유형은 값을 전달하고, 참조 유형은 주소를 전달합니다. 🎜rrreee🎜위의 예 #1에서 fn가 전달된 것은 <code>str의 값이고, 함수 매개변수와 그 값을 저장하기 위해 스택 메모리에 새로운 공간이 할당됩니다. (이 메모리 부분은 함수가 실행된 후 자동으로 해제됩니다. 실행, _가비지 수집 메커니즘_) 따라서 #2에서 출력 값은 I am the Modified string입니다. fn 값(스택 메모리에 새로 할당된 데이터)이며 str은 기본 유형입니다. #3의 출력은 초기화와 일치합니다. 🎜rrreee🎜위 예제의 두 함수는 모두 주소로 전달됩니다. 처음에 전달된 매개변수 arg는 참조(힙 메모리의 동일한 데이터를 가리키는 포인터)입니다. arg 변수에 새 개체(참조 유형)를 다시 할당합니다. fn1arg는 여전히 참조입니다( 힙 메모리 포인터의 데이터), fn1의 수정이 성공했습니다. 🎜🎜가비지 수집 메커니즘🎜🎜JS에는 가비지 수집 메커니즘이 있어 개발자에게는 큰 편의를 제공합니다. 메모리 해제 문제에 대해 너무 많이 걱정해야 합니다. (일부는 여전히 고려해야 합니다.) 🎜🎜🎜🎜함수의 변수는 함수 실행 중에만 존재합니다. 함수가 실행되면 이 변수를 메모리에서 자동으로 해제하여 다른 용도로 사용할 수 있는 공간을 남겨둡니다.🎜🎜🎜메모리의 변수가 더 이상 참조되지 않으면 JS가 정리합니다. 메모리의 이 부분 할당. 예:🎜rrreee🎜메모리 최적화🎜🎜전역 변수에 관한 한 JS는 나중에 사용될지 여부를 결정할 수 없으므로 항상 존재합니다. 선언된 순간부터 수동으로 해제되거나 페이지/브라우저가 닫힐 때까지 메모리가 필요하므로 다음과 같은 최적화를 수행할 수 있습니다.🎜🎜즉시 실행 기능을 사용하세요🎜🎜rrreee🎜🎜수동 터치 변수 참조🎜🎜rrreee🎜JS에서 클로저는 메모리 생성이 가장 쉽습니다. 문제는 클로저 대신 콜백 함수를 사용하여 내부 변수에 액세스할 수 있다는 것입니다. 콜백을 사용하면 액세스되는 내부 변수가 값이 됩니다. 함수가 매개변수를 전달할 때 값이 전달되기 때문에 기본 유형의 경우 실행 후 자동으로 변수를 해제하여 클로저처럼 내부 변수를 메모리에 유지하지 않습니다(그러나 참조 유형인 경우). 참조된 객체는 여전히 메모리에 있습니다. 🎜rrreee🎜위의 예에서는 fn 함수가 실행되었지만 함수의 변수 val에 대한 참조가 발생했습니다. 여전히 존재하므로 가비지 수집 메커니즘은 함수에서 val을 재활용하지 않습니다.🎜🎜 🎜콜백 사용🎜🎜rrreee🎜동시에 그렇게 하는 것이 클로저도 장점이 있지만 가장 적절한 시기에 사용하는 것을 구별하면 됩니다.🎜🎜관련 권장 사항: 🎜🎜🎜JS 메모리 관리 예시 설명🎜🎜🎜🎜🎜Analytics of memory release method in JS 클래스 라이브러리 Bindows1.3_javascript Skills🎜🎜🎜🎜🎜JS 메모리 관리 예시 설명🎜🎜

위 내용은 js에서 메모리는 어떻게 사용되나요? js 메모리의 역할 이해의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.