《JavaScript高级程序设计》 – 读书笔记 – 第4回 变量、作用域和内存问题

4.1 基本类型和援类型的价值

JavaScript变量是高枕无忧型的,它只是保存特定值的一个名字而已。

ECMAScript变量包含两种植多少列的价值:基本类型值和援类型值。基本项目值指的凡略的数据段,而引用类型值指那些由于多独价值构成的目标。

在将一个值赋给变量时,解析器必须确定这个价值是基本类型值还是引用类型值。基本数据列是按值访问的,因为可以操作保存在变量中之实际上的价值;而引用类型的价是保存在内存中之靶子,JavaScript不容许直接看内存中的岗位,所以在操作对象时,实际上是在操作对象的援而非是实际的对象。因此,引用类型的价是遵照引用访问的。

概念基本类型值和援类型值的措施是看似的:创建一个变量并也该变量赋值。但是对值可以进行的操作也不比,只能对援类型值动态添加、改变跟去属性和办法。

复制不同品种变量值的时刻,也是差之。
对于核心类型值,创建了初值然后复制到新变量。对于引用类型值,也开创了初值然后复制到新变量。不同之是,这个价实际上是一个“指针”,而这个指针指于存储于积着之一个目标。两单变量实际上以引用和一个对象。因此,改变中一个变量,就会见影响及另外一个变量的采取。

ECMAScript中具有函数的参数还是比照值传递的。也就是说,把函数外之值复制给函数内部的参数。
基本类型值的传递如同基本类型变量的复制一样,而引用类型值的传递,则像引用类型变量的复制一样。

每当传递引用类型的价值经常,会将这价当内存中的地方复制给一个有的变量,因此,这个局部变量的变化会反映在函数的标。

演示:函数传递引用类型值

1 function setName(obj){
2     obj.name = "Nicholas";
3 }
4 var person = new Object();
5 setName(person);
6 alert(person.name); // "Nicholas"

分析:创建了一个靶,并以那保存于了变量person中。然后,这个变量被传送及setName()函数中然后就是给复制给了obj。obj和person引用的是跟一个靶。

有些开发人员错误地以为:在有些作用域内修改的靶子见面当全局作用域中体现出去,就认证参数是遵照引用传递的。下面的例证是以印证对象是仍值传递的。

以身作则2:证明对象是依值传递的

1 function setName(obj){
2     obj.name = "Nicholas";
3     obj = new Object();
4     obj.name = "Greg";
5 }
7 var person = new Object();
8 setName(person);
9 alert(person.name); // "Nicholas"

解析:如果person是据引用传递的,那么person就会见活动为改动也对新的靶子,其name属性为”Greg”。但是,当属下还拜person.name时,显示的值仍然是”Nicholas”。这证明,即使以函数内部修改了参数的价,但原之援仍然维持不更换。

检测变量是不是一个骨干数据类,typeof操作符是顶尖的家伙。

 1 var s = "Nicholas";
 2 var b = true;
 3 var i = 22;
 4 var u;
 5 var n = null;
 6 var o = new Object;
 7 alert(typeof s); // string
 8 alert(typeof b); // boolean
 9 alert(typeof i); // number
10 alert(typeof u); // undefined
11 alert(typeof n); // object
12 alert(typeof o); // object

然而当检测引用类型的价时,这个操作符的用不老。通常,我们连无思了解某个值是目标,而是想知道它们是呀种的靶子。为这个,ECMAScript提供的instanceof操作符。其语法为:

1 result = variable instanceof constructor

 

4.2 执行环境和作用域

执行环境(execution context)是 JavaScript
中最好根本的一个概念。每个执行环境还有一个及的干的变量对象(variable
object),环境面临定义的有变量和函数都保留于是目标吃。

推行环境产生少数种:全局环境暨一些环境(函数)。
大局环境是极端外侧之一个履环境。在 Web 浏览器中,全局执行环境给看是
window 对象。每个函数都发自己之行环境。

当代码在一个环境遭受施行时,会创变量对象的一个企图域链(scope
chain)。作用域链的用处,是保对执行环境面临拥有变量和函数的平稳访问。
打算域链的前端,始终是眼前代码所在环境的变量对象。作用域链中之产一个变量对象来包含(外部)环境,而复下一个变量对象则来自下一个分包环境。这样,一直继承到全局执行环境。作用域链的末尾镇是大局执行环境之变量对象。

标识符解析是顺作用域链一级一级搜索标识符的历程。搜索过程一直从图域链的前端开始,然后逐级地往后回顾,直至找到标识符为止(如果搜索不顶标识符,通常会招错误产生)。

示例3:

 1 var color = "blue";
 2 
 3 function changeColor(){
 4         if(color == "blue"){
 5                 color = "red";
 6         } else {
 7                 color = "blue";
 8         }
 9 }
10 
11 changeColor();
12 alert("Color is now " + color);

解析:函数changeColor()包含两单变量对象:它好的变量对象同大局环境之变量对象。

内部条件得以通过作用域链访问有的外部环境,但外部环境不可知顾中条件遭到的旁变量和函数。这些环境中的牵连是线性、有次的。每个环境还足以发展搜索作用域链,以询问变量和函数誉为。但另外环境还非可知经过为下搜寻作用域链而上其他一个实施环境。

则独自来个别种植实施环境——全局和一些(函数),但出有限个报告句可以延长作用域链:try-catch语句的catch块和with语句。这片只报告句子都见面当打算域链前端临时添加一个变量对象。
本着with语句来说,会用指定的对象上加到意向域链中。对于catch语句来说,会创一个新的变量对象,其中带有的是给撇下来的缪对象的扬言。

示例4:

1 function builderUrl(){
2         var qs = "Hello world!";
3         
4         with(location){
5                 var url = href + qs;
6         }
7         
8         return url;
9 }

浅析:with语词以打算域链的前端添加了一个变量对象,此变量对象被蕴含了location对象的持有属性与道。当当with语词被引用变量href时(实际引用的是location.href),可以在目前施行环境之变量对象吃找到。当引用变量qs时,该变量在函数环境之变量对象中。而在with语句内部定义的变量url,则是函数执行环境之同一有的。

JavaScript
没有块作用域。使用for语句时如果铭记在心这或多或少,for语句子创建的变量i即使在for循环执行完毕晚,也依然会满怀在于循环外部的履行环境被。

下var声明的变量会自行为上加到绝相近的环境遭受。在函数内部,最接近的环境是函数局部环境;在with语句内部,最相近的条件是函数局部环境。
假定初始化变量时无因此var声明,该变量就吃上加至全局环境。

在JavaScript中,不声明如一直初始化变量是一个广泛的错误做法,这非常可能会见促成意外。建议在初始化变量之前,一定要优先声明。

查询标识符
当当某某环境受到为了读取或摹写副如果引用一个标识符时,必须通过搜索来确定拖欠标识符实际代表什么。搜索过程从图域链的前端开始,向上逐级询问以及给定名字匹配的标识符。如果找到了拖欠标识符,搜索过程停止,变量就绪。如果无找到,则继续沿着作用域链向上搜索。搜索过程用一直追溯到全局环境之变量对象。如果以大局环境呢从不找到这个标识符,则象征该变量尚未声明。

4.3 垃圾收集

JavaScript有自动垃圾收集体制。执行环境会负责管理代码执行过程中应用的内存。
渣收集体制的法则非常简短:找有那些不再采用持续运用的变量,然后放其占用的内存。垃圾收集器需要定期执行该操作。

函数中之一部分变量,在函数执行完毕后,就没有在的不可或缺了,所以垃圾收集器很爱看清是否回收该变量。但对此另外情形便不那么容易看清了。

JavaScript中最为常用之排泄物收集方式是标志清除(mark-and-sweep)。目前,IE、Firefox、Opera、Chrome、和Safari和JavaScript实现应用的还是符号清除式的废料收集策略(或相近之政策)。

记清除策略:当变量进入环境时,就将以此变量标记为“进入环境”。而当变量离开环境时,则用那标志为“离开环境”。

可以运用其它方式来标记变量。比如通过记录有位的状态,或者应用一个“进入环境”的变量列表及一个“离开环境”的变量列表来跟哪些变量发生了变更。

污染源收集器运行时见面于内存中所有变量加上记号;然后,为环境被的变量和受环境引用的变量去除标记。之后,剩余的变量被看是若刨除的变量。最后,垃圾收集器完成内存清除工作。

外一样种植不绝普遍的污染源收集策略叫做引用记数(reference
counting)。引用计数的含义是跟记录每个值为引用的次数。但这种办法在一个“循环引用”的深重问题。

Netscape Navigator 3.0
是极端早以引用计数策略的浏览器,因为“循环引用”的题材,Netscape Navigator
4.0被放弃了援记数方式,转而采取标记清除方式。

IE的BOM和DOM中之对象就是是用C++以COM对象的款式落实之。而COM对象的垃圾收集体制下的哪怕是引用记数策略。因此,只要在IE中关系COM对象,就会见有循环引用的问题。
IE9将BOM和DOM对象都更换成为了确实的JavaScript对象。这样,就败了科普的内存泄漏现象。

确定垃圾收集之年月距离是一个老大重要之题目。会潜移默化JavaScript的习性。

JavaScript于展开内存管理以及废弃物收集时面临的题材和一般程序有点不同,主要就分配受Web浏览器的可用内存数量通常如果比较分配为桌面应用程序的掉。这样做的凡为安全考虑,防止运行JavaScript的网页耗尽系统内存而导致系统崩溃。

之所以,要力保占用最少的内存可以被页面获得重新好之习性。而优化内存的绝好的方式,就是仅仅保留必要数据。一旦数据不再有效,最好通过将该价设置也null的点子来释放其引用,这个主意给解引用。解除引用的用意在于让值脱离执行环境,以便垃圾收集器下次运行时以那回收。
消除引用适用于大部分全局变量和大局对象的特性。局部变量会在它离开执行环境时自动为辟引用。

示例5:

1 function createPerson(name){
2         var localPerson = new Object();
3         localPerson.name = name;
4         return localPerson;
5 }
6 
7 var globalPerson = createPerson("Nicholas");
8 
9 globalPerson = null

分析:localPerson在createPerson()函数执行了后便去了彼尽环境,因此无论是需我们显式地失去为它消除引用。但对全局变量globalPerson,则需我们当非使其的时手动为它们脱引用。