javascript 变量、作用域和内存问题
一、基本类型和引用类型的值
1.基本类型和引用类型的值
基本类型值:指那些保存在栈内存中的简单数据,即这种值完全保存在内存中的一个位置,他们所占据的空间大小是固定的。
引用类型值:指那些保存在堆内存中的对象,这些类型的真正数据是保存在堆内存中的,而同时在栈内存中保存的只是一个指针,这个指针指向的是这个对象在堆内存中的一块地址。
基本类型的复制:基本类型在内存中占有的空间大小是固定的,复制的时候会重新在栈内存中开辟一块空间,是按值来访问的。
引用类型的复制:由于这种对象所占的大小是不固定的,是放在堆内存中的,但是内存的地址大小是固定的,故栈中存放的是对象在堆内存的地址。这样当查找引用的时候,是先从栈内存中取出地址,然后再到堆内存中找到对应的值,这就是引用访问。复制的时候是复制的栈内存的值,也就是拷贝的一个引用而已,两个变量指向的堆内存中对象还是同一个对象。
2.复制变量的值
基本类型:复制的是变量本身的值,复制后是在栈空间重新开辟一块空间,复制完成后两个变量是相互独立的,各不相干,故一个变量的值改变后不会影响另外一个变量的值。
引用类型:复制的时候只是复制的栈空间的地址,而堆内存中还是同一个对象,也就是复制的引用,结果是两个变量指向的是同一个内存块,所以,复制后两个变量中某一个改变了对象的值那么另外一个变量输出的结果也会改变,因为他们是指向的内存中同一个对象。
3.传递参数
在js中参数传递都是值传递,不存在引用传递。
值类型:传递的是变量本身的值,和复制是一样的,函数中改变了变量的值,不会影响源变量值
引用类型:同样是值传递,传递的是变量再栈内存空间中的地址值,如果在函数中改变了对象某一个属性的值,源变量中的值也会改变,因为在堆内存中它们是指向的同一个对象。
function func(num){ num.name=123; } var box={}; box.name='abcd'; alert(box.name); //abcd func(box); alert(box.name); //123 值在函数func中被改变了
4.检测类型
检测变量的类型的时可以用 typeof 来实现,但是这个只是返回的几大基本的数据类型,在检测Object类型的时候则不是那么好用,因为Null,Object,Array,RegExp 等都会返回object,那样就不知道变量到底是什么类型。
可以 用instanceof 来确定变量是某种具体的引用类型。
var box = [1,2,3]
alert(box instanceof Object); //true alert(box instanceof Array); console.log(Array.isArray(box)) //true //准确检测类型是否是数组 如果是这种类型就返回true,否则就返回false
二、执行环境及作用域
执行环境也就是作用域在很多的编程语言中都是一个很重要的概念,规定了变量或者函数有权访问其他数据的权限,规定了各自的行为。
全局执行环境是最外围的执行环境,在web浏览器中,全局执行环境被认为是window对象,故所有的全局变量和函数都是以window对象的属性和方法创建的。
每个函数都有自己的执行环境,当执行环境中代码执行完成后,就会销毁该执行环境,也会销毁里面的变量和函数等。(全局执行环境是需要在网页关闭或者应用程序退出后才会被销毁。)
当代码在一个环境中执行时,会创建变量对象的一个作用域链,作用域链的用途是保证执行环境有权访问的所有变量和函数的有序访问。
函数体内还包含着函数,只有这个函数才可以访问内一层的函数。而内部函数的变量可以通过作用域链访问外部函数的变量,可以向上搜索作用域链,以查询变量。但是不能反过来。
没有块级作用域
块级作用域表示诸如if等有花括号封闭的代码段块,所以支持条件判断来定义变量。 像 if,for 等代码块中定义的变量在花括号外面是可以访问的,这和其他语言中有很大的差别。
if(true){ var color = 'red'; } console.log(color); //red
var关键字在函数中的区别
在函数中声明变量的时候,如果不加上关键字 var 那变量会被认为是全局的,函数外面也可以访问它,当然在访问之前要先执行一次函数,加了则是局部的。
最好不要不用var关键字就初始化变量,因为这种情况下可能会导致各种错误,所有初始化变量的时候一定要加上关键字 var。
一般确定某一个变量的时候是通过搜索来确定的,现在本级作用域上找,如果没有,在向上级作用域找,依次类推,故访问局部变量要比访问全局变量的效率更高。因为不需要向上收索作用域链
3、内存相关
js中也存在垃圾回收机制,我们不需要担心内存的泄露问题,垃圾回收机制会自动的管理内存的分配和无用内存的回收。
JS中最常用的垃圾回收的方式是标记清除,就是在运行的时候会给内存中的所有变量加上一个标记,然后去掉环境中正在使用的变量的标记,而没有被去掉标记的变量将被视为准备删除的变量。最后垃圾清理器完成内存清理的工作,销毁那些带有标记的变量,并且回收他们所占用的内存空间 。
垃圾收集器是周期性的运行,不是随时运行,这样可能会遇到一些性能问题,但是一般情况下不需要担心这个问题。
一般来说,确保页面占用的内存更少可以让页面获得更好的性能,最好的减少内存占用量的方式就是,一旦变量或者对象不再使用的时候,将其赋值为空,即:box=null; 来释放引用,这种方式叫做删除引用,这种方式使用大多数的全局变量和全局对象。