执行环境是JavaScript中比较重要的概念。执行环境定义了变量或者函数有权访问的其他数据决定了他们各自的行为,每个执行环境都有一个与之关联的变量,环境中定义的所有变量和函数保存在这个对象中。
全局执行环境是最外围的一个执行环境。在web浏览器中,全局执行环境被认为是windows对象,因此所有全局变量和函数都是作为window对象的属性和方法创建的。某个执行环境所有代码执行完毕后,该环境被销毁,保存在其中的变量和函数也一同被销毁。全局执行环境,当应该程序退出或者浏览器关系的时候被销毁。
每个函数都有自己的执行环境,当任务流进入函数时,函数的执行环境会被推入一个栈中。函数执行完毕后,栈会环境推出,把控制权返回执行的函数。
当代码在一个环境中执行时,会创建变量对象的一个作用域链。作用域链的作用是保证执行环境有权访问的变量和函数的有序访问。作用域链的前端,始终都是当前执行的代码有在环境的变量对象。如果这个对象是函数,则将其活动对象作为变量对象。作用域链中的下一个变量对象来自包含环境,一直延续到全局执行环境。因此,全局执行环境的变量对象始终在作用域链的最后一个对象。
标识符解析是沿着作用域链一级一级的搜索标识符的过程。搜索过程始终是从作用域链的顶端开始,然后逐级地向后回溯,直到找到标识符。如果找不到标识符,则会产生错误。
var name ="jams";
function changeName(){
if(name=="jams"){
name="jack";
}
else{
name="jones";
}
}
changeName();
console.log(name);//输出jack
上面的函数通过作用域链,能够访问到全局作用域链中的name。也可以访问函数内部的作用域链。
var name="james";
function changeName(){
var anthorName="jordan";
function changeNames(){
var tempName=anthorName;
anthorName=name;
name=tempName;
//函数内部可以访问name、anthorName、tempName
}
//可以访问name,anthorName
changeNames();
}
changeName();
//只能访问name
console.log(name);//输出jordan
上面的例子可以清楚看到各个变量的作用域,changeNames(),处在作用域链的最顶端,可以访问到name、anthorName和tempName。函数changeName可以访问到name、anthorName.最外部的执行环境只能访问到一个变量name。
作用域链的延长,有些语句可以在作用域的顶端临时增加一个变量对象,该变量对象会在代码执行后被移除。当执行语句执行到下面的语句,作用域链就会被延长。try-catch,with。
这两个语句都会在作用域链的前端增加一个变量对象。对于with语句来说,会将指定的对象添加到作用域链中。对catch语句来说,会添加一个新的变量对象。with关键字为改变变量的作用域。但是with会导致代码的效率下降,并造成语义不明。
({
//属性
x: 10,
//函数
foo: function () {
function bar() {
//undefined,函数bar、foo并不存在局部变量x
console.log(x);
//30,with语句在函数内部定义了局部变量y
console.log(y);
//20,this中的x已经被修改
console.log(this.x);
}
with (this) {
//向上找x,对象中找到x,则x修改为20;
var x = 20;
//向上找y,对象中找不到y,则相当于在foo的函数内部定义了一个局部变量y
var y = 30;
bar.call(this);
}
}
}).foo();
上面的代码大家可以看看with的详细用法。with会从当前的环境,当底端查找,如果查询不到,则相当于将with中的声明语句提升一级。
在JavaScript中使用var 声明的变量会添加到最近的执行环境中。在函数中声明的变量,最接近的是函数的执行环境。没有使用var声明的变量,则会添加到全局变量中。
function getName(){
name="haha";
}
getName();
console.log(name);//输出name
需要牢记的事,JavaScript中在块里面的声明的变量,依然会被添加到函数中。
function getName(){
name="haha";
for(var i=0;i<10;i++){
}
console.log(i);//输出10
}
getName();
上面的代码,我们并没有声明i,但是依然能够访问到。所有我们在下次使用变量时候,要注意。