一.环境:
定义了变量或者函数有权访问其他数据,决定了他们的行为。每一个执行环境都有一个与之相关联的变量对象(例如全局的就是window),环境中定义的变量和函数都保存在这个对象中。
1.全局执行环境:
web浏览器中,全局执行环境--window对象;在Web浏览器中,全局执行环境被认为是window对象。因此所有的全局变量和函数都是作为window对象的属性和方法创建的。
var box = 'blue'; //声明一个全局变量
function setBox() {
alert(box); //全局变量可以在函数里访问
}
setBox(); // 'blue'
var box = 'blue';
function setBox() {
alert(window.box); //全局变量即window的属性
}
window.setBox(); //'blue',全局函数即window的方法
PS:当执行环境中的所有代码执行完毕后,该环境被销毁,保存在其中的所有变量和函数定义也随之销毁。如果是全局环境下,需要程序执行完毕,或者网页被关闭才会销毁。
PS:每个执行环境都有一个与之关联的变量对象,就好比全局的window可以调用变量和属性一样。局部的环境也有一个类似window 的变量对象,环境中定义的所有变量和函数都保存在这个对象中。当执行进入到一个函数的内部时,函数的环境就会被推到一个环境栈中,函数执行完后,栈将期环境弹出,把控制权返回给之前的执行环境。(我们无法访问这个变量对象,但解析器会处理数据时后台使用它)
2.局部执行环境:
函数里的局部作用域里的变量替换全局变量,但作用域仅限在函数体内这个局部环境。
var box = 'blue';
function setBox() {
var box = 'red'; //这里是局部变量,出来就不认识了
alert(box);
}
setBox(); //red
alert(box); //blue
通过传参,可以替换函数体内的局部变量,但作用域仅限在函数体内这个局部环境。
var box = 'blue';
function setBox(box) { //通过传参,替换了全局变量
alert(box);
}
setBox('red'); //red
alert(box); //blue
函数体内还包含着函数,只有这个函数才可以访问内一层的函数。
var box = 'blue';
function setBox() {
function setColor() {
var b = 'orange';
alert(box); //'blue'
alert(b); //'orange'
}
setColor(); //setColor()的执行环境在setBox()内
alert(b); // b is not defined
}
setBox();
PS:每个函数被调用时都会创建自己的执行环境。当执行到这个函数时,函数的环境就会被推到环境栈中去执行,而执行后又在环境栈中弹出(退出),把控制权交给上一级的执行环境。
PS:当代码在一个环境中执行时,就会形成一种叫做作用域链的东西。它的用途是保证对执行环境中有访问权限的变量和函数进行有序访问。作用域链的前端,就是执行环境的变量对象。
3.作用域链:
当代码在环境中执行的时候,会创建变量对象的一个作用域链。作用域链的用途是保证对执行环境有权访问的所有的变量和函数的有序访问。作用域链的最前端为当前执行代码所在的环境对象。如果这个环境是函数,则活动对象就是变量对象,活动对象最开始只有一个变量,即arguments对象(全局不存在)。作用域链中对的下一个变量来自于包含环境,下一个变量对象也来自包含环境。一直到全局执行环境,全局执行环境的变量对象永远是作用域链的最后一个对象。
标识符的解析是顺着作用域链,从头逐渐向后的,直到找到标识符为止(如果没有找到通常会报错)
例如:
var color = "blue";
function changeColor(){
if(color === "blue"){
color = "red";
}else{
color = "blue";
}
}
changeColor()
alert(color) //red
changeColor函数可以通过作用域链获取到全变变量color
var colora = "blue";
function changeColor(){
var colorb = "red";
function move(){
var tempColor =colora ;
colora = colorb;
colorb = tempColor;
//这里可以访问tempColor,colora,colorb
}
move()
//这里可以访问colora,colorb
}
changeColor()
alert(colora) //red
//这里可以访问colora
//也就是说只能向外层访问变量
4.延长作用域链
虽然执行环境只有两种,全局和局部,但是有办法延长作用域链,就是在作用域链的前端增加一个变量,该变量会在代码执行后被移除。
1.try-catch语句中的catch语句
创建一个新的对象,其中包含的是被抛出错误的声明
with语句
将指定的对象添加到作用域链中
二.没有块级作用域
在类c的语言中{}包围起来的都有自己的作用域。而js并不是这样。
if(true){
var color = "blue";
}
alert(color) //blue
if语句{}内定义了color,在C,C++,JAVA等语言中,在if声明的变量会在if结束后被销毁,在js中会被添加到当前执行环境,如这个if在window下执行的
for(var i = 0;i < 10;i++){
console.log("ppppppppp")
}
alert(i)//10,i依旧会存在于循环外部的执行环境中
三.内存问题
JavaScript具有自动垃圾收集机制,也就是说,执行环境会负责管理代码执行过程中使用的内存。其他语言比如C和C++,必须手工跟踪内存使用情况,适时的释放,否则会造成很多问题。而JavaScript则不需要这样,它会自行管理内存分配及无用内存的回收。
JavaScript最常用的垃圾收集方式是标记清除。垃圾收集器会在运行的时候给存储在内存中的变量加上标记。然后,它会去掉环境中正在使用变量的标记,而没有被去掉标记的变量将被视为准备删除的变量。最后,垃圾收集器完成内存清理工作,销毁那些带标记的值并回收他们所占用的内存空间。
垃圾收集器是周期性运行的,这样会导致整个程序的性能问题。比如IE7以前的版本,它的垃圾收集器是根据内存分配量运行的,比如256个变量就开始运行垃圾收集器,这样,就不得不频繁地运行,从而降低的性能。
一般来说,确保占用最少的内存可以让页面获得更好的性能。那么优化内存的最佳方案,就是一旦数据不再有用,那么将其设置为null来释放引用,这个做法叫做解除引用。这一做法适用于大多数全局变量和全局对象。
var o = {
name : 'Lee'
};
o = null; //解除对象引用,等待垃圾收集器回收