JavaScript 内存

JavaScript 中对内存的一些了解

在使用JavaScript进行开发的过程中,了解JavaScript内存机制有助于开发人员能够清晰的认识到自己写的代码在执行的过程中发生过什么,也能够提高项目的代码质量。其实关于内存的文章也有很多,写这篇文章也非"重弹老调",可以说是给自己理解的知识来一个总结,也顺便将知识分享给学习JavaScript的小伙伴们。

JavaScript内存是怎么样的?

JavaScript中的变量的存放有有原始值与引用值之分,原始值代表了原始的数据类型,如Undefined,Null,Number,String,Boolean类型的值;而Object,Function,Array等类型的值便是引用值了。

JavaScript中的内存也分为栈内存和堆内存。一般来说,栈内存中存放的是存储对象的地址,而堆内存中存放的是存储对象的具体内容。对于原始类型的值而言,其地址和具体内容都存在与栈内存中;而基于引用类型的值,其地址存在栈内存,其具体内容存在堆内存中。堆内存与栈内存是有区别的,栈内存运行效率比堆内存高,空间相对推内存来说较小,反之则是堆内存的特点。所以将构造简单的原始类型值放在栈内存中,将构造复杂的引用类型值放在堆中而不影响栈的效率。

var str = "Hello World"; // str:"Hello World"存在栈中
var obj = {value:"Hello World"}; // obj存在栈中,{value:"Hello World"}存在堆中,通过栈中的变量名obj(访问地址)访问

内存中的存储对象生命周期是怎么样的呢?

我们来看看MDN中的介绍:

1.当对象将被需要的时候为其分配内存

2.使用已分配的内存(读、写操作)

3.当对象不在被需要的时候,释放存储这个对象的内存

第一步和第二步在所有语言中都是一样的,第三步的操作在JavaScript中不是那么明显。

来看看内存中发生了什么?

var str_a = "a"; // 为str_a分配栈内存:str_a:"a"
var str_b = str_a; // 原始类型直接访问值,so,为str_b新分配栈内存:str_b:"a"

str_b = "b"; // 栈内存中:str_b:"b"。str_b的值为"b",而str_a的值仍然是"a"
// 分隔 str 和 obj -----------------------------------------------------------//
var obj_a = {v:"a"}; // 为obj_a分配栈内存访问地址:obj_a,堆内存中存对象值:{v:"a"};
var obj_b = obj_a; // 为obj_b分配栈内存访问地址:obj_b,引用了堆内存的值{v:"a"}

obj_b.v = "b"; // 通过obj_b访问(修改)堆内存的变量,这时候堆内存中对象值为:{v:"b"},由于obj_a和obj_b引用的是堆内存中同一个对象值,所以这时候打印都是{v:"b"}

obj_b = {v:"c"}; // 因为改的是整个对象,这里会在堆内存中创建一个新的对象值:{v:"c"},而现在的obj_b引用的是这个对象,所以这里打印的obj_a依旧是{v:"b"},而obj_b是{v:"c"}(两者在内存中引用的是不同对象了)。

然后看看这个问题:

var a = {n:1};
var b = a;
a.x = a = {n:2};
// a:{n:2} a.x=undefined b:{n:1,x:{n:2}} b.x:{n:2}

具体的解释可以看看某位园友的详细解释,对理解基础知识点还是很有帮助的哦。

从内存角度看函数传值的变化

网上不少文章是关于JavaScript传值/址的解说,根据上面对值的原始类型和引用类型的区分,也能够理解传的是值还是址。原始类型的值传的便是值,引用类型的传的是内存中对象的地址。

从代码看看区别:

var str_a = "Hello World";
function fn_a(arg){
    console.log(arg); // #1 --> Hello World
    arg = "Hai";
    console.log(str_a,arg); // #2 --> Hello World , Hai
};
fn_a(str_a);
// #3 这时候str_a:"Hello World"

从上面#1处可以看出,传入函数fn_a的是str_a的值(这时候和之前案例str_a/str_b的情况一样),并且内存中分配了新的空间来保存函数参数和其值(函数运行后自动释放这部分内存,后面或说回收机制),所以在#2处打印的是2个不同的字符串。也正是因为传值时候对str_a值进行了值的复制,而这又是原始类型的值,所以在#3处的str_a与早先声明的str_a保持一致。

var obj_a = {value:1};
function fn_a(arg){
    arg={value:2};
};
fn_a(obj_a);
// 这时候obj_a还是{value:1}
function fn_b(arg){
    arg.value=3;
};
fn_b(obj_a);
// 这时候obj_a是{value:3}

上面这个问题也可以从内存角度去理解,两个函数都是传址,而这个址引用了obj_a在内存中对应的对象,所以两个函数中的arg起初都是引用和obj_a同一个内存中的对象值,但是在fn_a中重新为arg赋值新的对象(和之前例子中的obj_a/obj_b情况一样),而fn_b中访问的依旧是和obj_a同一个内存对象,所有fn_b修改是成功的。

垃圾回收机制(简单带过)

JavaScript具有自动进行垃圾回收的机制,这便造成了开发人员极大的方便,至少不用太考虑内存释放的问题(有部分还是要考虑的)。

1.函数的变量只在函数执行过程中存在。在函数执行过程中,函数内部的变量将会在内存中被分配一定的空间,当函数执行完毕后,自动将这些变量从内存中释放,以留出空间作其他用处。

2.当内存中某个变量不再被引用,JavaScript也将清理掉这部分内存的分配。如:

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

某园友的JavaScript垃圾回收机制文章,介绍的也挺详细。同时这点在《JavaScript高级程序设计》中也有介绍。

内存优化

就全局变量而言,JavaScript不能确定它在后面不能够被用到,所以它会从声明之后就一直存在于内存中,直至手动释放或者关闭页面/浏览器,这就导致了某些不必要的内存消耗。我们可以进行以下的优化。

使用立即执行函数

(function(){
    // 你的代码
})();

或者:

(function(window){
    // 你的代码
})(window);

如果你的某些变量真的需要一直存在 可以通过上面的方法挂载在window下。同样,你也可以传入jQuery进行使用。

手动解除变量的引用

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

在JavaScript中,闭包是最容易产生内存问题的,我们可以使用回调函数代替闭包来访问内部变量。使用回调的好处就是(针对访问的内部变量是原始类型的值,因为在函数传参的时候传的是值),在执行完后会自动释放其中的变量,不会像闭包一样一直将内部变量存在于内存中(但如果是引用类型,那么这个被引用的对象依旧存在内存中)。

function fn_a(){
    var value = "Hello World";
    return function(){
            return value;
        };
};
var getValue = fn_a();
var v = getValue(); // --> "Hello World"

在上面的代码中,虽然函数已经执行完毕,但是对于函数中变量value的引用还在,所以垃圾回收机制不会将函数中的value清理。
使用回调:

function fn_a(callback){
    var value = "Hello World";
    return callback(value);
};
function fn_b(arg){
    return arg;
};
var v = fn_a(fn_b);

需要注意,使用回调将会导致异步。同时声明,并不是说明这样做就一定比闭包好,闭包也有其好处,只是需要我们分清何时何地去使用才是恰当的。

这些知识感觉很绕,有疑惑的小伙伴也可留言交流/赐教。本兽也仅刚入门的一只小前端,不喜勿喷。

标签: JavaScript

时间: 2024-08-10 11:51:04

JavaScript 内存的相关文章

javascript内存模型分析猜想

/* * 这里我是利用分析java内存模型的方法来猜想javascript的内存模型, * 由于没有看到国内有关于分析javascript的书籍,但是可以借鉴java的 * 内存模型结构来帮助理解javascript的内存模型中的原型机制,下面先 * 给出一个简单的原型例子 * */ "use strict"; function PrototypeModel(name,author,time){ } PrototypeModel.prototype.name = "Proto

JavaScript 内存释放测试

JS内部实现垃圾回收机制 无法立即释放 只要数据没有依赖关系 就会在下轮内存回收时清理 ! function () { var array = [] , count = 100*100*100*5 , time = +new Date ; while( count-- ) array.push( document.createElement('el') ) ; console.log( +new Date - time, array.length ) ; count = 100*100*100*

[ Javascript ] 内存泄露以及循环引用解析

内存泄露 在javascript中,我们非常少去关注内存的管理. 我们创建变量,使用变量,浏览器关注这些底层的细节都显得非常正常. 可是当应用程序变得越来越复杂而且ajax化之后,或者用户在一个页面停留过久,我们可能须要去注意一些问题.如一个浏览器花费了1G以上的内存,而且在不断的添加. 这些问题经常都是由于内存泄露引起. Javascript 内存泄露 这个javascript内存管理的核心概念就是具不具有可达性的概念. 1 一个明显的对象集合将会被觉得是可达的:这些对象是被知道的像roots

JavaScript内存优化

相对C/C++ 而言,我们所用的JavaScript 在内存这一方面的处理已经让我们在开发中更注重业务逻辑的编写.但是随着业务的不断复杂化,单页面应用.移动HTML5 应用和Node.js 程序等等的发展,JavaScript 中的内存问题所导致的卡顿.内存溢出等现象也变得不再陌生. 1. 语言层面的内存管理 1.1 作用域 作用域(scope)是JavaScript 编程中一个非常重要的运行机制,在同步JavaScript 编程中它并不能充分引起初学者的注意,但在异步编程中,良好的作用域控制技

Chrome开发者工具之JavaScript内存分析

内存泄漏是指计算机可用内存的逐渐减少.当程序持续无法释放其使用的临时内存时就会发生.JavaScript的web应用也会经常遇到在原生应用程序中出现的内存相关的问题,如 泄漏和溢出,web应用也需要应对 垃圾回收停顿. 尽管JavaScript使用垃圾回收进行自动内存管理,但有效的(effective)内存管理依然很重要.在这篇文章中我们将探讨分析JavaScript web应用中的内存问题.在学习有关特性时请确保尝试一下相关案例以提高你对这些工具在实践中如何工作的认识.请阅读 内存 101(M

javascript 内存泄漏的学习

概念 内存泄漏: 用动态存储分配函数动态开辟的空间,在使用完毕后未释放, 木有任何指针指向他,结果导致一直占据该内存单元.直到程序结束.(其实说白了就是该内存空间使用完毕之后未回收, 占着茅坑不**)即所谓内存泄漏. 等所有内存都被占完之后, 系统就跪了. 内存分配方式 说道内存泄露,就不得不谈到内存分配的方式.内存分配有三种方式,分别是: 一.静态分配( Static Allocation ):静态变量和全局变量的分配形式.如果把房间看做一个程序,我们可以把静态分配的内存当成是房间里的耐用家具

Chrome开发者工具之JavaScript内存分析(转)

尽管JavaScript使用垃圾回收进行自动内存管理,但有效的(effective)内存管理依然很重要.在这篇文章中我们将探讨分析JavaScript web应用中的内存问题.在学习有关特性时请确保尝试一下相关案例以提高你对这些工具在实践中如何工作的认识.请阅读内存 101(Memory 101)页面来帮助你熟悉这篇文章中用到的术语.注意:我们将要用到的某些特性目前仅对Chrome Canary版浏览器可用.我们推荐使用这个版本来获得最佳的工具,以分析你的应用程序的内存问题. 你需要思考的问题

关于JavaScript内存泄漏的质疑

近几天看了些关于JavaScript内存管理的文章,相对于Java JVM的内存管理,显得简单些. 在学习的过程中,发现有不少网友谈到了循环引用,说循环引用会造成内存泄漏,垃圾回收器无法回收. 实际上,并没有这么可怕,根据小菜目前的了解,这种循环引用造成的内存泄漏,仅仅会发生在低版本的IE浏览器上,现代浏览器是不会这么蠢的. 举个例子,网络上流行的说法大致有如下两种: 1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta char

4类 JavaScript 内存泄露及如何避免

原文:4 Types of Memory Leaks in JavaScript and How to Get Rid Of Them笔记:涂鸦码龙 译者注:本文并没有逐字逐句的翻译,而是把我认为重要的信息做了翻译.如果您的英文熟练,可以直接阅读原文. 本文将探索常见的客户端 JavaScript 内存泄露,以及如何使用 Chrome 开发工具发现问题. 简介 内存泄露是每个开发者最终都要面对的问题,它是许多问题的根源:反应迟缓,崩溃,高延迟,以及其他应用问题. 什么是内存泄露? 本质上,内存泄