javaScript的变量由于本质是松散类型,所以决定了它只是在特定时间用于保存特定值的一个名称。由于不存在定义某个变量必须要保存何种类型的规则,所以变量的值及其数据可以在脚本的生命周期内改变。
4.1基本类型和应用类型
ECMAScript变量包含两种不同的数据类型的值:
1、基本类型-简单的数据段
(Null,undefined,String,Number, Boolean)这五种类型由于是按值访问的,可以直接操作保存在变量当中的值,所以是基本数据类型。
2、引用类型-由多个值构成的对象
引用类型的值是保存在内存中的对象。javaScript不允许直接访问内存中的位置,也就是不能直接操作对象的内存空间,操作对象时实际操作的是对象的引用。当复制保存着某个对象的某个变量时,操作的是对象的引用。但是当为对象添加属性时,操作的是对象。
4.1.1动态的属性
定义基本类型的值和引用类型的值的方式一样(创建一个变量并为该变量赋值),但是当值保存到变量后,对于基本类型值的变更可以直接再次赋值,引用类型的值,可以为其添加属性和方法,也可以改变和删除其方法。
4.1.2复制变量值
基本类型:从一个变量向另一个变量赋值基本类型的值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置(栈
)上。虽然两个变量的值相同,但是这两个变量可以参与任何操作,而不互相影响。
引用类型:从一个变量向另一个变量赋值引用类型的值,也会将存储在变量对象中的值复制一份放到为新变量分配的空间(堆)中。不同的是这个值的副本是一个指针,指向存储在堆中的一个对象。复制操作结束后,两个变量实际是引用的同一个对象,当改变任意一个变量,另一个变量也会影响。
4.1.3传递参数
ECMAScript中所有的函数的参数都是按值传递参数的。也就是把函数外部的值复制给函数的内部的参数,就如同把一个变量复制给另一个变量。基本类型值的传递就如同基本类型变量的复制,引用类型值的传递就如同引用类型的值复制一样。访问变量有按值和按引用两种方式,而参数只能按值传递。
在向参数传递基本类型的值时,被传递的值会被复制给一个局部变量(命名参数,ECMAScript概念-arguments对象中的一个参数)。在向函数传递引用类型的值时,这个值在堆内存中的地址复制一份给一个局部变量,所以这个局部变量的变化会反应到函数的外部。
引用类型参数传递当调用setName函数时,将对象person在堆内存中的地址复制给了局部变量,也就是命名参数obj,在函数的内部obj和person引用的是同一个对象,当在函数内部为obj添加name属性后,person对象也会有影响。但是在函数内部,重新为obj复制一个新的对象引用时,函数内部就会创建一个名为obj的局部变量,命名参数obj指向person的指针会变为指向这个局部对象,当函数执行完毕局部变量会被销毁。
4.1.4检测类型
检测基本类型:typeof操作符
当变量的值是一个对象或者null时,使用typeof检测类型会返回Object。
typeof检测引用类型:instanceof操作符
result = variable instanceof constructor
在检测对象或者构造函数时,始终会返回true。当检测基本类型时,会始终返回false
4.2执行环境和作用域
执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。每个执行环境都有一个与之关联的变量对象,环境定义的所有变量和函数都保存在这个对象中(我们编写的代码无法访问这个对象,但是解析器在处理数据时,会在后台使用它)。
全局执行环境是最外围的执行环境,根据ECMAScript实现所在的宿主环境不同,表示执行环境的对象也不一样。在Web浏览器中,全局执行环境被认为是window对象,因此所有的全局变量和函数都是作为window对象的属性和方法创建的。某个执行环境中的所有代码执行完毕后,该环境就会被销毁,保存在其中的所有变量和函数定义也随之销毁(全局执行环境直到应用程序退出—例如关闭网页或浏览器—时才会被销毁)
每个函数都有自己的执行环境(局部)。当执行流进一个函数时,函数的环境就会被推入到一个环境栈中,而在函数执行完毕后,栈将其环境弹出,把控制权返回给之前的执行环境。ECMAScript程序中的执行环境就死有这个机制控制。
当代码在一个环境中执行时,会创建变量对象的作用域链。作用域链的用途是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终是当前执行环境的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象作为变量对象,活动对象在最开始时只包含一个变量,即arguments对象(这个对象在全局环境中不存在)。作用域链中的下一个变量对象来自包含(外部)环境,而再下一个变量对象则来自下一个包含环境,一直延续到全局执行环境。全局执行环境始终是作用域链中的最后一个对象。
标识解析是沿着作用域链一级一级地搜索的标识符的过程。搜索过程始终从作用域链的最前端开始,然后逐级地向后回溯,知道查到标识符为止(如果查不到,通常会导致错误发生)
执行环境和作用域链内部环境可以通过作用域链访问外部环境,而外部环境不能访问内部环境中的变量和函数。这些环境之间的练习是线性的、有次序的。每个环境都可以向上搜索作用域链,来查询变量和函数,都是不能通过向下搜索作用域链而进入另一个执行环境。
4.2.1延长作用域链
在作用域链的前端临时增加一个变量对象,该变量对象会在代码执行后被移除。在执行流进入下列任何一个语句时,作用域链就会得到加长:
1:try-catch语句的catch块。 2:with语句
这两个语句都会在作用域链的前端添加一个变量对象。with会将指定对象添加到作用域链中,catch语句会创建一个新的变量对象,其中包含的是被抛出的错误对象的声明。
4.2.2没有块级作用域
1.声明变量
使用var声明的变量会自定添加到最近的执行环境中。在函数内部最接近的环境就是函数的局部环境,在with语句中就死函数环境。如果初始化变量没有使用var,便变量会自动被添加到全局环境中。
2.查询标识符
标识符,就是指变量、函数、属性的名字,或者是函数的参数。
挡在某个环境中为了读取或写入而引用一个标识符,必须通过搜索来确定该标识符实际代表什么。搜索过程从作用域链的前端开始,向上逐级查询与给定名字匹配的标识符。如果在局部环境中找到了该标识符,就停止搜索变量就绪。如果在局部环境中没有找到,则继续沿着作用域链向上搜索,搜索过程一直追溯到全局环境的变量对象。如果全局环境中也没有找到这个标识符,则意味着该变量没有定义。
在搜索过程中,如果存在一个局部变量的定义,就会停止搜索,不再进入另一个变量对象。
4.3垃圾收集
垃圾收集机制的原理:找出那些不再继续使用的变量,然后释放它们占用的内存。垃圾收集器会按照固定的时间间隔来周期性的执行这一操作。
函数中局部变量的声明周期:局部变量只在函数执行的过程中存在,而在这个过程中,会为局部变量在栈(或堆)内存中分配相应的空间用来储存它们的值。然后在函数中使用这些便利啊给,直到执行结束。执行结束后局部变量就没有存在的必要,可以释放它们的内存。
垃圾收集器就是用来追踪哪个变量有用,哪个变量没用,对于不再使用的变量打上标记,以备将来收回其占用的内存。
总共有两种在浏览器中的实现。
4.3.1标记清除
当变量进入环境时,会将这个变量标记为进入环境,而当变量离开环境时,就会被标记为离开环境。
标记变量方法:翻转某个特殊的位来记录一个变量何时进入环境,或者使用一个“进入环境”的列表以及一个“离开环境”的列表来追踪哪个变量发生了变化。
垃圾收集器在运行时会给存储在内存中的所有变量都加上标记,然后它会去掉环境中的变量以及被环境中的变量引用的变量的标记。而在此之后再被加上标记的变量被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后垃圾收集器完成内存清除的工作,销毁那些带有标记的值并回收它们的内存空间。
4.3.2引用计数
追踪记录每个值被引用的次数,当声明一个变量并将一个引用内型值赋值给该变量时,则这个值的引用次数就死1。如果同一个值又被赋给其它变量,该值引用次数加1,相反,如果包含对这个值引用的变量又取得另外一个值,则这个值的引用减1。当这个值的引用次数为0时,则将其占用的内存空间收回。当垃圾收集器下次再运行时,释放那些次数为0的值所占用的空间。
4.3.4内存管理
内存限制:通常分配给Web浏览器的可用内存数量通常比分配给桌面应用程序的要少。主要出于安全方面的考虑,是为了防止运行JavaScript的网页耗尽全部系统内存而导致系统崩溃。内存限制的问题不仅会影响变量分配内存,还会影响调用栈以及一个线程中能够同时执行的语句数量。
优化内存占用问题的最佳方式,就是为执行中的代码只保存必要的数据。一旦数据不再使用,应该通过null来释放其引用。——这个方法叫引用解除
解除一个值的引用并不意味着自动回收该值所占的内存空间,真正作用是让值脱离执行环境,以便垃圾收集器下次运行时将其回收。
网友评论