执行环境和作用域链

javascript执行环境和作用域

执行环境

执行环境是一种机制,用来处理JavaScript运行时的作用域和生存期等;
1.执行环境的作用:定义了变量或函数有权访问的其他数据,决定了他们各自的行为。
2.全局执行环境
全局执行环境是最外围的一个执行环境;在浏览器中,全局执行环境是window对象;因此所有的全局变量和函数都是window对象的属性和方法,在第一次载入Javascript代码时会创建一个全局执行环境;
某个执行环境的所有代码执行完了之后,该环境就会被销毁(包括其中的所有变量和函数定义);全局执行环境直到应用程序退出才会被销毁;
3.函数执行环境
每个函数都有自己的执行环境,当调用一个函数时,该函数的执行环境就会被推入一个环境栈中;如果又调用了一个函数,则又会重新创建一个执行环境,也会被推入栈中,在函数执行之后,栈将其执行环境弹出,又重新回到之前的执行环境;

//执行代码
function F1() {
    //代码
    function F2() {
        //代码
    }
}
//执行代码

下图是执行环境进栈出栈的示意图:
这里写图片描述

变量对象

每个执行环境都有一个与之关联的变量对象(VO);环境中定义的所有变量和函数都保存在这个对象里;如果这个环境是函数,那么就将他的活动对象(AO)作为变量对象;活动对象是在进入函数执行环境是创建的,最先有的就是arguments属性;
变量对象中定义着这些:
(1)函数的形参
(2)var 声明的变量
(3)函数声明(不包括函数表达式)

var a = 1;
function fun(b) {
    var c = 3;
}
fun(2);

上面代码对应的变量对象是这样的:

全局环境的变量对象 = {
    a:1,
    fun:指向fun()函数
};
函数fun()执行环境的变量对象 = {//函数将他的活动对象作为变量对象
    b:2,//fun()的形参
    c:3
}
作用域

作用域简单来说就是一个范围,一个变量与函数的可访问范围;作用域分为全局作用域和局部作用域;
(1)拥有全局作用域的对象:
最外层函数和在最外层函数外面的变量;
未定义直接赋值的变量自动声明为全局变量(没有使用var声明的变量);
window对象的属性;
(2)拥有局部作用域的对象
在固定的代码片断才能访问到,比如函数作用域;

作用域链

在JavaScript中,一切都是对象,函数也是对象,
[[scope]]:有一个仅供JavaScript引擎访问的内部属性叫做[[Scope]],这个内部属性包含了函数被创建的作用域中对象的集合,这个集合就叫做函数的作用域链,他决定了哪些数据能被函数访问;这个属性在函数创建是被储存,一直不变,直到函数销毁;
作用域链是环境变量的一个属性,[[scope]]是函数的一个属性:上面例子的[[scope]]是这样的–

fun.[[scope]] = [全局执行环境.变量对象];

(1)当代码在一个环境中执行的时候,就会创建变量对象的一个作用域链;
(2)作用域链的作用:作用域链是当前执行环境中对象的列表,他保证对执行环境有权访问的所有变量和函数的有序访问;
(3)在作用域链的前端,始终是当前执行代码所在环境的变量对象,如果是函数执行环境,这个变量对象就是活动对象;
(4)作用域链中的下一个变量对象来自包含环境,在下一个变量对象来自下一个包含环境,全局执行环境的变量对象始终都是作用域链中的最后一个对象;

执行代码的时候,标识符的解析是沿着作用域链的前端逐级向后回溯,如果找不到就会返回错误;

var color = red;
function getColor() {
    document.write(color)
}
getColor();//red
document.write(color);//red
alert(a);//undefined

在上面的例子中,getColor()里面没有定义变量color,所以沿着作用域链逐级向后回溯;在全局作用域里面找到了变量color;而undefined是因为在作用域链里面找不到a这个变量;
延长作用域
有两个方法会延长作用域链:with和catch语句;他们会被添加到作用域链的最前端;
with:会将指定的对象添加到作用域链中;使用with,作用域链被临时加长,局部变量现在处于第二个作用域链对象中,访问性能降低,尽量少用;
catch:会创建一个新的变量对象,其中包含的是被抛出的错误对象的声明;

变量作用域

(1)在JavaScript中,函数中的变量(使用var定义的)为局部变量;函数外的变量是全局变量,函数内不使用var定义的变量也是全局变量;

var name = "susan";
function obj() {
    name = "jack";//全局变量
}
obj();
console.log(name);//jack

(2)内部环境可以通过作用域链访问外部环境,但是外部环境不能访问内部环境;每个环境都可以向上搜索作用域链来查询变量名和函数名,但是不能向下搜索;

var name="aaa";
function outer() {
    var age="100";
    function inner() {
        var job="doctor";
        //这里可以访问name,age,job
    }
    //这里可以访问name,age,不能访问job
}
//这里可以访问name,不能访问age,job

(3)javasvript是有编译过程的,执行每一段代码之前,会首先处理关键字var和function,声明会提到当前作用域的最前面;

function obj() {
    console.log(name);
    var name="susan";
}
obj();//undefined

因为变量name的声明提前,上面代码相当于这样:

function obj() {
    var name;//变量声明
    console.log(name);
    name = "susan";
}

所以会输出undefined;
JavaScript中的一个名字进入作用域的方式和优先级:
–语言内置:所有作用域中都有this和arguments关键字;
–形式参数;
–函数声明;
–变量声明;
(4)JavaScript中没有块级作用域,函数作用域里的变量在外部不能访问,但是在for语句或是在if语句里的变量在外面还是可以访问到的;

for(var i=0;i < 5;i++) {
    //执行操作
}
console.log(i);//4,在大括号外还是可以访问

==注意==:
由于查询标识符的时候是从作用域链的前端开始,标识符所在的位置越深,读写速度就会越慢,而全局变量处于作用域链的最末端,所以应尽量少使用全局变量,如果一个跨作用域的对象被引用了一次以上,应该使用局部变量储存了再用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值