第三节:作用域链
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第三节:作用域链相关的知识,希望对你有一定的参考价值。
(转自老惠的博客)
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);
以上是关于第三节:作用域链的主要内容,如果未能解决你的问题,请参考以下文章