第三节:作用域链

(转自老惠的博客

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);

时间: 2024-08-29 22:31:26

第三节:作用域链的相关文章

JavaScript作用域链

JavaScript作用域链 之前写过一篇JavaScript 闭包究竟是什么的文章理解闭包,觉得写得很清晰,可以简单理解闭包产生原因,但看评论都在说了解了作用域链和活动对象才能真正理解闭包,起初不以为然,后来在跟公司同事交流的时候发现作用域和执行环境确实很重要,又很基础,对理解JavaScript闭包很有帮助,所以在写一篇对作用域和执行环境的理解. 作用域 作用域就是变量和函数的可访问范围,控制着变量和函数的可见性与生命周期,在JavaScript中变量的作用域有全局作用域和局部作用域. 单纯

js的变量,变量作用域,作用域链

变量声明: 使用var关键字声明,如果使用没有声明的变量,则JS会自动声明此变量根据变量作用域.如果变量只声明为赋值,则此时值是undefined.重复声明变量,在JS不会报错,会依据最后一次的声明来处理变量. 变量作用域: 一个变量的作用域是,程序代码定义这个变量的区域,全局变量在程序代码内任何地方都可以访问. 包括在{}函数,对象内的变量(属性)成为局部变量. 在函数体内定义的变量成为局部变量,作用域也是局部,函数参数也是局部变量. 他们只在函数体内有意义. 在函数体内,局部变量优先于全局变

JavaScript 作用域链

作用域链 当查找变量的时候,会先从当前上下文的变量对象中查找,如果没有找到,就会从父级(词法层面上的父级)执行上下文的变量对象中查找,一直找到全局上下文的变量对象,也就是全局对象.这样由多个执行上下文的变量对象构成的链表就叫做作用域链. 函数创建 这是因为函数有一个内部属性[[scope]],当函数创建的时候,就会保存所有父变量对象到其中,你可以理解[[scope]]就是所有父变量对象的层级链.(注意:[[scope]]并不代表完整的作用域链!) 举个栗子: function foo() { f

作用域链

JavaScript 开发进阶:理解 JavaScript 作用域和作用域链 作用域是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域链的工作原理.今天这篇文章对JavaScript作用域和作用域链作简单的介绍,希望能帮助大家更好的学习JavaScript. JavaScript作用域 任何程序设计语言都有作用域的概念,简单的说,作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期.在JavaScript中,变

作用域、作用域链、闭包

作用域.作用域链 一.Js以前没有块级作用域,不过在ES6中有let了. 二.Js使用函数作用域 function aaa(){ var a = "a"; } console.log(a);//报错 三.声明提前 console.log(aaa)//报错 console.log(aaa);//undefined 声明未定义 var aaa; console.log(aaa); //undefined 声明未定义 var aaa = "aaa"; 四.Js的作用域链

JavaScript中的作用域链原理

执行环境 作用域链的形成与执行环境(Execution Environment)相关,在JavaScript当中,产生执行环境有如下3中情形: 1 进入全局环境 2 调用eval函数 3 调用function 在一个执行环境A上可以创建执行环境B,执行环境B又可以创建执行环境C...,这一系列的执行环境构成执行环境栈,最新创建的执行环境位于栈顶(栈底永远是全局执行环境),当栈顶执行环境结束之后(与之相关的代码执行结束)就会被弹出站外,底下的执行环境就会成为新的栈顶.如下图所示: 一个执行环境由3

JS_ 垃圾回收、变量与属性、作用域链

1.垃圾回收 js的垃圾回收机制不同于java,c等语言需要手工回收,js中的垃圾回收是自动启动的,大致过程:js解释器会判断一个对象是否是可达的,即是否有变量指向它,没有,则启动回收机制,释放该对象占用的内存,eg: 1 var s = "hello world"; 2 var s2 = s.toUpperCase();//创建新字符串:"HELLO WORLD" 3 s2 = s;//"hello world"没有变量指向,占用内存被释放 这

方法链、作用域链和原型链(三)——原型链

每一个javascript对象(null除外)都有一个prototype属性,这个属性引用了一个对象,即原型对象,都从原型继承属性. 所有通过对象直接量创建的对象都具有同一个原型对象,并可以通过javascript代码Object.prototype获得对原型对象的引用.通过关键字new和构造函数调用创建的对象的原型就是构造函数的prototype属性的值.因此,使用{}和通过new Object()创建的对象,都继承自Object.prototype. 没有原型的对象不多,Object.pro

JS高级心法——作用域链

首先我们来看两个js中的代码: <script type="text/javascript"> var c=5; function t1(){ var d=6 function t2(){ var e=7 alert(c+d+e); } t2(); } t1(); </script> 这个你很快会得出结论:18: <script language="javascript" type="text/javascript"