作用域
JavaScript 的变量被作用域所限制,如果超出了作用域,那么变量就没有办法再被使用,这样做的优点是:
- 可以避免当前的变量转为全局变量
- 有效限制变量的作用区域
而变量作用域也会按照声明方式的不同,产生不同的作用域:
- 未声明:全局变量
var
声明:作用域在 函数 中let
、const
声明:作用域在{}
中
var 声明的变量
在函数内声明的变量其作用域会被限制在该函数的调用栈中,在外部无法直接得到该作用域内的变量。下面的例子中, fn
函数内的变量在全局下是没有办法查看的。
function fn() {
var a = 1;
}
fn();
console.log(a); // 无法得到 fn 函数內的 a 变量
所以常用 "立即函数" 来限制变量的作用域,主要是避免全局变量的产生。
(function() {
var b = 1;
})();
console.log(b); // 无法得到 fn 函数內的 b 变量
let、const 声明的变量
ES6 之后所新增的let
、const
作用域则与过去不同,改用 {}
作为作限制用域的方式,这让 for 循环及部分语法避免产生多余的变量来影响作用域。
与 var
不同的是 const
所定义的变量作用域被限制在 {}
中。所以这个例子中的变量 c
可在外部得到值,d
则无法取到。
{
var c = 1;
const d = 1;
}
console.log(c); // 1
console.log(d); // Uncaught ReferenceError: d is not defined,无法取到变量 d
内存管理机制
每当我们新增一个变量时,在内存中就会占用一个位置来保存它的值,以便在程序后续运行时可以多次使用。
下面的代码会在内存中开辟一个 a
的空间来存储数字 1 的值。
var a = 1
流程如下:
- 开辟一个内存空间来存放变量
a
,不过这时还没有赋值(原因可参考:Hoisting)
- 为
a
赋值。
所有的变量都会占用内存空间,除此之外对象、数组的属性以及函数参数等也会用相同的概念进行占用。调用一个函数时,每一个函数的作用域也都会反复的进行内存占用。随着应用程序越来越复杂的情况下,如果持续的占用内存而没有进行适当的释放,那么内存可能会被耗尽。
JavaScript 引擎具有内存回收的机制,会释放不再使用的变量内存,其基本概念为:当没有任何的引用指向它时就会释放内存。
来自MDN:collectible if there are zero references pointing to it.
内存释放的验证
下面用一个例子来说明及验证内存释放的机制,首先用一段函数来产生一个非常长的字符串,长字符串会占用大量的内存空间。
在调用 randomString
函数后会返回一个很长的字串:
function randomString(length) {
var result = '';
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = characters.length;
for (var i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
案例一:使变量维持在可引用的状态(不会释放内存)
定义一个全局变量 demoData
,这个变量会持续维持可被引用的状态。
var demoData = []; // 全局变量
function getData() {
for (let i = 0; i < 1000; i++) {
demoData.push(randomString(5000))
}
}
getData()
console.log(demoData); // 可引用 demoData 值
执行这段代码后,进入 Chrome DevTools 中的 Memory 页,这个功能可以得到当前页面所占用的内存状况。接下来点击 "Take snapshot" 按钮。
可以看到目前执行完这段代码后占用了 6.2MB 的内存空间(注意:任何浏览器环境和插件都会影响所占用的内存状态)。
案例二:使变量无法再次被引用(执行后释放内存)
限制变量的作用域,使变量无法再被外部引用。
此段代码依然会执行这个函数,也会将值赋值给变量,但外部无法再次引用 demoData
的值。
function getData() {
var demoData = []; // 局部变量
for (var i = 0; i < 1000; i++) {
demoData.push(randomString(5000))
}
}
getData();
然后回到 Memory 页点击 "Take snapshot" 重新取得内存的状态,接下来会得到与前面不同的结果,这次只占用了 1.2MB 的内存(其中 5MB 被释放掉了)
总结
通过前面的例子,我们知道了作用域以及内存之间的关系,而内存管理也是前端打工人必须要掌握的知识(除了控制内存的使用大小,还需在必要时保留而不被释放)。
本文首发微信公众号:前端先锋
欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章
欢迎继续阅读本专栏其它高赞文章:
- 深入理解Shadow DOM v1
- 一步步教你用 WebVR 实现虚拟现实游戏
- 13个帮你提高开发效率的现代CSS框架
- 快速上手BootstrapVue
- JavaScript引擎是如何工作的?从调用栈到Promise你需要知道的一切
- WebSocket实战:在 Node 和 React 之间进行实时通信
- 关于 Git 的 20 个面试题
- 深入解析 Node.js 的 console.log
- Node.js 究竟是什么?
- 30分钟用Node.js构建一个API服务器
- Javascript的对象拷贝
- 程序员30岁前月薪达不到30K,该何去何从
- 14个最好的 JavaScript 数据可视化库
- 8 个给前端的顶级 VS Code 扩展插件
- Node.js 多线程完全指南
- 把HTML转成PDF的4个方案及实现
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。