JS中对内存的一些了解

JS中对内存的一些了解

JS中对内存的一些了解

在JS进行开发的过程中, 了解JS内存机制有助于开发人员能够清晰的认识到自己写的代码在执行的过程中发生过什么, 也能够提高项目的代码质量.

JS内存是怎么样的?

JS中变量存放有着原始值与引用值之分:

  • 原始值: 原始的数据类型: 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回收.

使用回调

function fn1(cb) {
    var val = '你好';
    return cb(val);
};
function fn2(arg) {
    return arg;
};
var v = fn1(fn2);

同时声明, 并不是说明这样做就一定比闭包好, 闭包也有其好处, 只是需要我们在分清在最恰当的时候使用.

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/101484.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 带你深入理解什么叫js闭包

    带你深入理解什么叫js闭包闭包用一句话总结:闭包就是作用域的延伸解释:函数内部的作用域属于局部作用域,导致只有函数内部才可以访问声明的变量。而闭包可以将局部作用域延伸,使其在函数外部也可以访问内部声明的变量等。关于变量的作用域js变量的作用域无非就两种局部变量和全局变量JavaScript在函数内部可以访问全局变量varn=0;//全局变量functionf1(){console.log(n)//0}f1();而在函数内部声明的局部变量,外部无法直接访问functionf1(

  • BM3D算法相关笔记

    BM3D算法相关笔记三维矩阵主要原因就在于,三维矩阵中,第一维度代表行,第二维度代表列,第三维度代表页,当第三维度为1时,代表只有1页,自然缩减为二维。当第一维度为1时,代表只有一行,但是每页都有,所以,从物理的角度没有缩减为二维。http://blog.sina.com.cn/s/blog_7519cf530101ahsb.html

  • PyTorch中torchvision介绍[通俗易懂]

    PyTorch中torchvision介绍[通俗易懂]TorchVision包包含流行的数据集、模型架构和用于计算机视觉的图像转换,它是PyTorch项目的一部分。TorchVison最新发布版本为v0.11.1,发布较频繁,它的license为BSD-3-Clause。它的源码位于:https://github.com/pytorch/visionTorchVision由C++(CUDA)和Python3实现,依赖Torch、PNG、JPEG,还依赖PIL(Pillow,PythonImagingLibrary)。推荐…

  • 卡方分布、方差分析

    卡方分布:首先我们先把现代数学中的数理统计中的卡方分布已经烂大街的定义先放下来,我先回到卡方检验的诞生的之地。在1900年,皮尔森发表了著名的关于卡方检验的文章,该文章被认为是现代统计学的基石之一。在该文章中,皮尔森研究了拟合优度检验:……(这里之所以加点的原因是因为,下面的话很不好理解,我们举一个实际一点的例子就容易理解了。)下面图片有个赌场的色子(注意阅读下面红色字体)…

  • Android小项目——新闻APP

    Android小项目——新闻APP前言:在公司学习了一段时间Android知识,决定做一个小项目,目的是学会运用所学的基础知识,在这里记录一下开发历程,大家可以把它看成一款入门级练手的Demo应用吧~项目介绍:类型:新闻APP(低仿今日头条)基本功能:欢迎页面加载(3s,点击可跳过)——Activity相关用户注册/登录——SQLite运用横向滑动列表显示新闻类别——TabLayout…

  • java锁cas(java锁的实现原理)

    一、Java锁1.常见的锁有synchronized和Lock()①synchronized是jvm层面实现的,可以直接用,不过要锁住某个对象;lock是属于j.u.c包下的接口,用的时候要实现lock接口。②synchronized一般和wait()、notify()、notifyAll()一起使用,使用完不用释放锁;lock必须在finally里面手动释放。@lock锁与s…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号