本文通过示例介绍了 JavaScript 变量作用域和内存问题。分享给大家,供大家参考,详情如下:
学习要点:
1。变量和范围
2.内存问题
JavaScript 变量与其他语言中的变量有很大不同。 JavaScript 变量的松散类型(非类型)性质决定了它只是一个用于在特定时间保存特定值的名称。由于没有规则定义变量必须保存的数据类型,因此变量的值及其数据类型可以在脚本的生命周期内发生变化。
1。变量和范围
1。基本类型和参考类型的取值
ECMAScript 变量可能包含两种不同数据类型的值:原始类型值和引用类型值。原始类型值是指存储在堆栈内存中的那些简单数据段,即此类值完全存储在内存中的一个位置。引用类型值是指存储在堆内存中的对象,这意味着变量中存储的实际上只是一个指针。该指针指向内存中存储对象的另一个位置。
为变量赋值时,解析器必须确定该值是原始类型值还是引用类型值。基本类型值如下:Undefined、Null、Boolean、Number》和绳子。这些类型各自在内存中占据固定大小的空间,它们的值存储在栈空间中,我们通过值来访问。
PS:在某些语言中,字符串表示为对象,因此被视为引用类型。 ECMAScript 放弃了这一传统。
如果分配的值是引用类型,则必须在堆内存中为该值分配空间。由于此类值的大小不固定,因此无法将它们保存到堆栈内存中。但内存地址的大小是固定的,因此内存地址可以保存在栈内存中。这样,在查询引用类型的变量时,首先从栈中读取内存地址,然后通过该地址找到堆中的值。为此,我们将其称为“按引用访问”。
2.动态属性
定义基本类型值和引用类型值的方式类似:创建变量并为变量赋值。然而,当值保存在变量中时,对不同类型的值可以执行的操作有很大不同。
var box = new Object(); //创建引用类型 www.gestaodocondominio.net = '李'; //添加一个新属性 警报(框.名称); //输出
如果给基本类型值添加属性,就会出现问题。
var box = '李'; //创建一个基本类型 盒子.年龄 = 27; //给基本类型添加属性 警报(框.年龄); //不明确的
3。复制变量值
在变量复制方面,基本类型和引用类型也有所不同。基本类型复制值本身,而引用类型复制地址。
var box = '李'; //在栈内存中生成一个盒子'Lee' var box2 = 盒子; //在栈内存中生成另一个box2 'Lee'
Box2 是 box1 的副本,但从图中可以看到,它是完全独立的。也就是说,两个变量单独操作时不会互相影响。
var box = new Object(); //创建引用类型 www.gestaodocondominio.net = '李'; //添加一个新属性 var box2 = 盒子; //将引用地址赋给box2
在引用类型中,box2实际上是box,因为它们指向同一个对象。如果修改了该对象中的name属性,则www.gestaodocondominio.net和www.gestaodocondominio.net输出的值也会相应修改。
4。传递参数
ECMAScript中所有函数的参数都是按值传递的。这意味着参数不是通过引用传递的,尽管变量分为基本类型和引用类型。
function box(num) { //按值传递,传递的参数为基本类型 数字 += 10; //这里的num是局部变量,全局无效 返回数字; } 变量数 = 50; var 结果 = box(num); 警报(结果); //60 警报(数字); //50
PS:上面的代码中,传递的参数是一个基本类型值。函数中的num是局部变量,与外部num没有联系。
以下是参数作为引用类型的示例。
function box(obj) { //按值传递,传递的参数为引用类型 www.gestaodocondominio.net = '李'; } var p = new Object(); 框(p); 警报(www.gestaodocondominio.net);
PS:如果有引用传递,那么函数中的变量将是全局变量,也可以被外部访问。例如,在PHP中,必须在参数前面添加&符号,以表示按引用传递。 ECMAScript没有这些,只能是局部变量。您可以在 PHP 中了解它。
PS:所以通过引用传递和传递引用类型是两个不同的概念。
函数框(obj) { www.gestaodocondominio.net = '李'; var obj = new Object(); //函数内部创建另一个对象 www.gestaodocondominio.net = '先生'; //原obj不被替换 }
最后得出结论,ECMAScript函数的参数将是局部变量,即不是通过引用传递。
5.检测类型
要检测变量的类型,我们可以使用typeof运算符来判断。如:
var box = '李'; 警报(框类型); //细绳
虽然 typeof 运算符在检查基本数据类型时非常有用,但在检查引用类型时就没那么有用了。通常,我们不想知道它是否是一个对象,而是想知道它是什么类型的对象。因为数组也是对象,所以 null 也是对象,等等。
这时候我们应该使用instanceof操作符来查看。
var 框 = [1,2,3]; 警报(数组的框实例); //它是一个数组吗? var box2 = {}; 警报(对象的box2实例); //是否是对象 var box3 = /g/; 警报(正则表达式的 box3 实例); //是否为正则表达式 var box4 = new String('李');警报(box4实例字符串); //是否为字符串对象
PS:使用instanceof检查基本类型的值时,返回false。
5。执行环境及范围
执行环境是JavaScript中最重要的概念。执行环境定义函数可以访问的变量或其他数据,确定它们各自的行为。
全局执行环境是最外围的执行环境。在 Web 浏览器中,全局执行环境被视为 window 对象。因此,所有全局变量和函数都被创建为窗口对象的属性和方法。
var box = '蓝色'; //声明一个全局变量 函数 setBox() { 警报(框); //函数中可以访问全局变量 } 设置框(); //执行函数
全局变量和函数是窗口对象的属性和方法。
var box = '蓝色'; 函数 setBox() { 警报(窗口框); //全局变量是window的属性 } window.setBox(); //全局函数是window的方法
PS:当执行环境中的所有代码执行完毕后,环境被销毁,其中保存的所有变量和函数定义也被销毁。如果是全局环境,直到程序执行或者网页关闭后才会被销毁。
PS:每个执行环境都有一个与之关联的变量对象,就像全局窗口可以调用变量和属性一样。本地环境也有一个类似于window的变量对象,环境中定义的所有变量和函数都存储在这个对象中。 (我们无法访问这个变量对象,但是解析器在处理数据时会在后台使用它)
函数内局部作用域的变量替代了全局变量,但作用域仅限于函数体内的局部环境。
var box = '蓝色'; 函数 setBox() { var box = '红色'; //这是一个局部变量,出来你就认不出来了 警报(框); } 设置框(); 警报(框);
通过传递参数,可以替换函数体内的局部变量,但范围仅限于函数体内的局部环境。
var box = '蓝色'; function setBox(box) { //通过传递参数,替换全局变量 警报(框); } setBox('红色'); 警报(框);
函数体也包含函数。只有该函数才能访问内层的函数。
var box = '蓝色'; 函数 setBox() { 函数设置颜色(){ var b = '橙色'; 警报(框); 警报(b); } 设置颜色(); //setColor()的执行环境在setBox()内部 } 设置框();
PS:每个函数在调用时都会创建自己的执行环境。当这个函数执行时,函数的环境会被压入环境栈中执行,执行完毕后会在环境栈中弹出(退出),将控制权交给上层执行环境。
PS:当代码在环境中执行时,会形成一种称为作用域链的东西。其目的是保证对执行环境中具有访问权限的变量和函数的有序访问。作用域链的前端是执行环境的变量对象。
6。无块级范围
块级作用域表示 if 语句等用大括号括起来的代码块,因此支持条件判断来定义变量。
if (true) { //if 语句代码块没有本地作用域 var box = '李'; } 警报(框);
for循环语句也是如此
for (var i = 0; i < 10; i ++) { //无局部作用域 var box = '李'; } 警报(一); 警报(框);
函数中var关键字的区别
函数框(num1, num2) { var sum = num1 + num2; //如果去掉var,它就变成了全局变量 返回总和; } 警报(框(10,10)); 警报(总和); //错误报告
PS:强烈不建议不使用var来初始化变量,因为这种方法会导致各种意外。因此初始化变量时必须加上var。
通常,确定变量是通过搜索确定标识符实际表示的内容来完成的。
var box = '蓝色'; 函数 getBox() { 退货箱; //代表全局框 } //如果在函数体中添加var box = 'red' 警报(getBox()); //那么最终返回值为红色
PS:在变量查询中,访问局部变量比全局变量更快,因为不需要向上搜索作用域链。
二。内存问题
JavaScript有自动垃圾回收机制,这意味着执行环境负责管理代码执行过程中使用的内存。其他语言,例如C和C++,必须手动跟踪内存使用情况并及时释放,否则会引起很多问题。 JavaScript不需要这个,它会自己管理内存分配和无用内存的回收。
JavaScript 中最常用的垃圾回收方法是标记和清除。垃圾收集器在运行时标记存储在内存中的变量。然后,它删除环境中正在使用的变量的标签,未标记的变量将被视为准备删除的变量。最后,垃圾收集器完成内存清理工作,销毁那些标记值并回收它们占用的内存空间。
垃圾收集器定期运行,这可能会导致整个程序出现性能问题。例如,在 IE7 的早期版本中,其垃圾收集器根据内存分配量运行。例如,垃圾收集器在 256 个变量后开始运行。这样就不得不频繁运行,从而降低了性能。
一般来说,保证页面占用内存最少,可以获得更好的性能。所以优化内存的最好方法就是将其设置为null,以便一旦数据不再有用就释放引用。这种做法称为解除引用。这种方法适用于大多数全局变量和全局对象。
var o = { 姓名:‘李’ }; o = 空; //取消引用该对象并等待垃圾收集器回收它
更多JavaScript相关内容,请查看本站专题:《JavaScript常用函数技巧汇总》、《javascript面向对象入门教程》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》和《JavaScript数学运算用法总结》
希望这篇文章对大家JavaScript编程有所帮助。