参考文献:javascript
Nicholas C.Zakas 《JavaScript》高级程序设计前端
仅为我的学习参考文献的内容记录的笔记。存在部分直接拿来的成分。不得不说,这本书写得很透彻。java
执行环境是JS中很重要的概念,其定义了变量或者函数访问其余数据的权限,决定了其各自的行为。函数
每一个函数都有本身的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行以后,栈将其环境弹出,把控制权返回给以前的执行环境。 ECMAScript 程序中的执行流正是由这个方便的机制控制着。学习
当代码在一个环境中执行时,会建立变量对象的一个做用域链(scope chain)。做用域链的用途,是保证对执行环境有权访问的全部变量和函数的有序访问。做用域链的前端,始终都是当前执行的代码所在环境的变量对象。若是这个环境是函数,则将其活动对象(activation object)做为变量对象。活动对象在最开始时只包含一个变量,即 arguments 对象(这个对象在全局环境中是不存在的)。做用域链中的下一个变量对象来自包含(外部)环境,而再下一个变量对象则来自下一个包含环境。这样,一直延续到全局执行环境;全局执行环境的变量对象始终都是做用域链中的最后一个对象。优化
标识符解是沿着做用域链一级一级往下拓展的,而搜素则是从做用域链的前端开始逐级回溯,直到找到标识符为止(找不到的话固然就报错了)。ui
下面请看简单的事例:url
var color = "blue"; function changeColor(){ if (color === "blue"){ color = "red"; } else { color = "blue"; } } changeColor(); alert("Color is now " + color);
经过运行能够发现,存行结果是红色,运行changecolor时,会去搜索color的值,在函数体内,未发现color的定义,因而会往外找,结果找到了color="blue"的定义,因而就会访问,获取,修改。debug
var color = "blue"; function changeColor(){ var anotherColor = "red"; function swapColors(){ var tempColor = anotherColor; anotherColor = color; color = tempColor; //color, anotherColor, and tempColor are all accessible here } //color and anotherColor are accessible here, but not tempColor swapColors(); } changeColor(); //anotherColor and tempColor aren't accessible here, but color is alert("Color is now " + color);
上面涉及了3个执行环境,全局、changecolor()和swapColors三种。其中全局变量是color和anotherColor。changeColor()的局部环境中有一个名为anotherColor 的变量和一个名为swapColors()的函数,但它也能够访问全局环境中的变量 color。 swapColors()的局部环境中有一个变量tempColor,该变量只能在这个环境中访问到。不管全局环境仍是 changeColor()的局部环境都无权访问 tempColor。然而,在 swapColors()内部则能够访问其余两个环境中的全部变量,由于那两个环境是它的父执行环境。设计
下列两种状况能够延长做用域链:
try-catch语句的catch语句块
with语句
这两个语句都会在做用域链的前端添加一个变量对象。对 with 语句来讲,会将指定的对象添加到做用域链中。对 catch 语句来讲,会建立一个新的变量对象,其中包含的是被抛出的错误对象的声明。
function buildUrl() { var qs = "?debug=true"; with(location){ var url = href + qs; } return url; } var result = buildUrl(); alert(result);
with接受了location对象,因而变量对象就是location的全部属性和方法,变量对象就被添加在做用域链前端。buildUrl()函数中有局部变量qs。with引用变量href时,即location.href,可以被找到。引用变量qs,指的则是buildUrl()函数内部的qs。至于 with 语句内部,则定义了一个名为 url 的变量,于是 url 就成了函数执行环境的一部分,因此能够做为函数的值被返回。
JS中并无块级做用域。在类C语言中是有的,如条件语句,循环语句之类,大括号内属于一种做用域,可是在JS中并无。
if (true) { var color = "blue"; } alert(color); //"blue"
显然,color在条件语句的大括号中,var定义的也是局部变量,但实际上,大括号外面能访问到想访问的color。
使用 var 声明的变量会自动被添加到最接近的环境中。并且仅限于该环境。反之,若是该声明没有带,var会被自动认为是全局变量。
function add(num1, num2) { var sum = num1 + num2; return sum; } var result = add(10, 20); //30 alert(sum);
这里报错了,由于sum在add函数中为局部变量,从大括号外面并不能访问到sum。若是改为下面这样,就能正常运行。
function add(num1, num2) { sum = num1 + num2; return sum; } var result = add(10, 20); //30 alert(sum); //30
当在某个环境中为了读取或写入而引用一个标识符时,必须经过搜索来肯定该标识符实际表明什么。搜索过程从做用域链的前端开始,向上逐级查询与给定名字匹配的标识符。若是在局部环境中找到了该标识符,搜索过程中止,变量就绪。若是在局部环境中没有找到该变量名,则继续沿做用域链向上搜索。搜索过程将一直追溯到全局环境的变量对象。若是在全局环境中也没有找到这个标识符,则意味着该变量还没有声明,会报错。
var color = "blue"; function getColor(){ return color; } alert(getColor()); //"blue"
alert中调用了getcolor()函数,这个函数中访问了color,然而函数中并无声明color,因而会向上找color,在全局中找到了,因而就取值。
var color = "blue"; function getColor(){ var color = "red"; return color; } alert(getColor()); //"red" alert(color);//"blue"
getcolor函数体内有color的声明,并且是局部的,因此并不会覆盖全局的blue。在getcolor内部,color就是内部声明的color,一旦搜索到定义以后就不会再搜索,因此就是red;而外面的color则并不会由于里面的color是red而改变,因此仍是blue。
补充: 变量查询也不是没有代价的。很明显,访问局部变量要比访问全局变量更快,由于不用向上搜索做用域链。 JavaScript 引擎在优化标识符查询方面作得不错,所以这个差异在未来恐怕就能够忽略不计了。