住宅百科

JavaScript 变量作用域和内存问题的示例分析

本文通过示例介绍了 JavaScript 变量作用域和内存问题。分享给大家,供大家参考,详情如下:

学习要点:

1。变量和范围
2.内存问题

JavaScript 变量与其他语言中的变量有很大不同。 JavaScript 变量的松散类型(非类型)性质决定了它只是一个用于在特定时间保存特定值的名称。由于没有规则定义变量必须保存的数据类型,因此变量的值及其数据类型可以在脚本的生命周期内发生变化。

1。变量和范围

1。基本类型和参考类型的取值

ECMAScript 变量可能包含两种不同数据类型的值:原始类型值和引用类型值。原始类型值是指存储在堆栈内存中的那些简单数据段,即此类值完全存储在内存中的一个位置。引用类型值是指存储在堆内存中的对象,这意味着变量中存储的实际上只是一个指针。该指针指向内存中存储对象的另一个位置。

为变量赋值时,解析器必须确定该值是原始类型值还是引用类型值。基本类型值如下:UndefinedNullBooleanNumber》和绳子。这些类型各自在内存中占据固定大小的空间,它们的值存储在栈空间中,我们通过值来访问。

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编程有所帮助。