第三节:作用域链

(转自老惠的博客

 

JavaScript采用的是静态作用域规则,也叫词法作用域,其解析过程是按照从上到下、从左到右的顺序加载,并分为两个阶段:预编译期(预处理)和执行期。预编译期对代码块中所有声明变量函数进行处理。注意关键字:代码块、声明、变量、函数。

 

1、代码块

 

代码块是指由<script>标签分割的代码段,JavaScript按照代码块来进行编译和执行,代码块间相互独立,但变量和函数共享。

 

<script type="text/javascript">

    var msg = "我在第一个代码块中定义";

    sayHello("张三");

    alert("我可以执行吗?");

</script>

<script type="text/javascript">

    alert("第二个代码块中调用第一个代码块中的变量:\n"+msg);

</script>

 

执行以上代码,首先报错:

 

点击是继续执行:

第一个代码块中,因为函数sayHello没有定义自然报错,程序终止,所以语句alert("我可以执行吗?")没有执行,但是第二个代码块的代码仍然可以执行,说明代码块间是相互独立的。而且,第二个代码块可以调用第一个代码块的变量msg,说明代码快间的共享性。

 

因此,JavaScript的执行流程是:

 

 

2、变量的预处理

 

预编译期,变量只是进行了声明但未进行初始化以及赋值。

    alert(msg);

    var msg = "预处理不会进行初始化";

执行结果为:

这里显示msg没有赋值,说明变量msg已经声明了,但没有初始化赋值。如果msg没有声明,则应该报错:msg未定义。

 

3、函数的预处理

 

预编译期只是对声明式函数进行处理。

 

fn();

function fn(){

    alert("我是函数");

}

上面的例子说明,在预编译期声明式函数已经被处理了,所以即使fn()调用函数放在声明函数前也能执行。

 

fn();

var fn =function(){

    alert("我是函数");

}

而赋值式函数却不会被预处理,所以fn()调用函数放在赋值函数前执行就会报错:缺少对象。再看下面的例子:

 

fn();

function fn(){

    alert("我是函数一");

}

function fn(){

    alert("我是函数二");

}

 

两个同名的声明式函数都会被预处理,后面的函数覆盖了前面的函数。

 

fn();

function fn(){

    alert("我是函数一");

}

var fn = function(){

    alert("我是函数二");

}

 

虽然两个函数同名,但是,因为赋值式函数不会被预处理,所以执行的是第一个函数。如果fn()调用函数放在函数的定义之后,那么:

 

function fn(){

    alert("我是函数一");

}

var fn = function(){

    alert("我是函数二");

}

fn();

 

在执行的时候,赋值式函数已经被处理了,后面的函数覆盖了前面的函数,所以执行了第二个函数。

 

4、作用域链

 

执行下面的代码:

 

function fn(){

    alert(msg);

}

var msg = "我是一个变量";

fn();

 

正常显示“我是一个变量”,说明内部环境可以通过作用域链访问外部环境。

 

function fn(){

    var msg = "我是一个变量";

}

alert(msg);

 

执行报错“msg未定义”,说明外部环境不能访问内部变量环境中的任何变量和函数。

 

function fn(){

    msg = "我是一个变量";

}

fn();

alert(msg);

 

这段代码成功执行,正常显示“我是一个变量”,说明了什么呢?

 

两段代码的函数fn有一点细微的差别,第一个函数中代码var msg = "我是一个变量",有关键字var,说明在这里声明一个变量并初始化。而第二个函数中的代码msg = "我是一个变量",少了关键字var,说明这里给变量msg赋值。但是,在我们的代码中并没有声明变量msg的语句,那么,为什么赋值成功了呢?而且后面的语句还以调用这个变量?

 

在第二段代码中,我们执行函数fn()的时候,JavaScript引擎在变量表中找不到变量msg,就会沿着作用域链一直往上查找,一直到最外围的全局执行环境,在Web浏览器中,全局执行环境被认为是window对象。如果都没找到,那么,对于读操作,就会产生运行期错误;而对于写操作,就会等价为 window.msg = "我是一个变量" ,给window对象新增了一个属性。

 

所以,第一段代码中,在函数fn内部声明了一个变量msg,函数的外部环境无法访问这个变量。而第二段代码,执行函数fn,其实是给window全局对象设置了一个属性msg,并赋值初始化,所以我们可以访问它。其实,这段代码标准的写法应该是:

 

function fn(){

    window.msg = "我是一个变量";

}

fn();

alert(window.msg);

转载于:https://www.cnblogs.com/huiyong/p/7262601.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值