全局执行环境就是最外面的一个执行环境,即window对象;
执行环境(作用域):简单理解就是变量或者函数能够起作用的区域,出了这个区域就不能用,这个环境内代码执行完毕后,环境被销毁,内存被回收,其中所有的变量和函数都销毁;
每个执行环境都有一个与之关联的变量对象,环境中所有的变量,函数都在这个对象中,如果这个环境是函数,那变量对象的开始时只包含一个变量,就是arguments(参数对象),然后就是包含可以使用的所有变量即它的父层函数的变量,父函数的父函数。。以此类推,直到全局作用域;
作用域链:是从最内层作用域到最外层全局作用域,链的前端,始终都是当前执行代码的变量对象(包括作用域的变量)即最内层的作用域也是最小的;
举例:
var color="blue";
function changeColor(){
if(color === "blue"){
color = "red";
} else {
color = "blue";
}
}
changeColor();
alert("Color is now "+ color);
这个例子中,changeColor()的作用域链包含两个对象:自己的变量对象(其中定义着arguments对象)和全局变量对象。
结果:
var color = "blue";
function changeColor(){
var anotherColor="red";
function swapColors(){
var tempColor = anotherColor;
anotherColor = color;
color = tempColor;
//这里可以访问color,anotherColor和tempeColor
}
swapColors();
//这里可以访问color和anotherColor,但不能访问tempeColor
}
changeColor();
//只能访问color();
这个图就是上面代码的作用域链:swapColors()->changeColor()->window!
补充:
一:函数作用域:
var a = 1;
function t(){
console.log(a);
var a = 2;
console.log(a);
}
t();
第一个console.log输出的是”undefined” 而不是”1”;
第二个console.log输出的是”2”;
有些朋友可能会这么想,第一句会输出“1”,是因为还没有执行var a = 2;,所以肯定是“1”;
这个想法我之前也有,但是,今天看了一篇文章,里面写到了所谓函数作用域就是说:->变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的。
上面的代码可以等价于:
var a = 1;
function t(){
var a;
console.log(a);
a = 2
console.log(a);
}
t();
// 我们可以看到,由于函数作用域的特性,局部变量在整个函数体始终是有定义的,我们可以将变量声明”提前“到函数体顶部,同时变量初始化还在原来位置,这就又涉及到了声明提升的概念,大概就是变量,函数在使用时,会把声明语句提升到函数体顶部(这个我也不是很明白,有偏差请告知!!!)
而且我们要区分,我们平常在c/c++中学到的块级作用域,和JS中的函数作用域不一样,JS没有块级作用域;
var name="a";
if(true){
var name="b";
console.log(name)
}
console.log(name);
都输出是“b”,如果有块级作用域,明显if语句将创建局部变量name,并不会修改全局name,可是没有这样,所以Js没有块级作用域。
现在很好理解第一个代码为什么会得出那样的结果了,函数内a声明覆盖了全局的a,但是还没有赋值,所以输出:”undefined“。像常用的for,if,switch-case之类都一样;
二:变量作用域:
简单说,就是,函数体内,没有用var定义的都是定义的全局变量,而用var定义的是局部变量,在本函数体内局部变量的优先级比全局变量高,这是因为变量在作用域链上查找导致的;
三:作用域链:
name="a";
function A(){
var name="b";
function B(){
var name="c";
console.log(name);
}
function C(){
console.log(name);
}
B();
C();
}
A();
**当执行B时,将创建函数B的执行环境(变量对象:保存环境中定义的所有变量和函数),并将该环境的变量对象置于链表开头,然后将函数A的变量对象链接在之后,最后是全局对象。然后从链表开头寻找变量name,很明显name是 ” c “。
但执行C()时,作用域链是: C()->A()->window,所以name是”b “**
四:with/catch语句延长作用域链:
with/catch语句主要用来临时扩展作用域链,将语句中的对象添加到作用域的头部。
person = {name:"yhb",age:22,height:175,
wife:{name:"a",
age:21
}};
with(person.wife){
console.log(name);
}
with语句将person.wife添加到当前作用域链的头部,所以输出的就是:“a”.
with语句结束后,作用域链恢复正常,catch一样;