Js作用域与作用域链详解-转

转自:http://blog.csdn.net/yueguanghaidao/article/details/9568071

一直对Js的作用域有点迷糊,今天偶然读到Javascript权威指南,立马被吸引住了,写的真不错。我看的是第六版本,相当的厚,大概1000多页,Js博大精深,要熟悉精通需要大毅力大功夫。

一:函数作用域

先看一小段代码:

1 var scope="global";
2 function t(){
3     console.log(scope);
4     var scope="local"
5     console.log(scope);
6 }
7 t();

(PS: console.log()是firebug提供的调试工具,很好用,有兴趣的童鞋可以用下,比浏览器+alert好用多了)

第一句输出的是: "undefined",而不是 "global"

第二讲输出的是:"local"

你可能会认为第一句会输出:"global",因为代码还没执行var scope="local",所以肯定会输出“global"。

我说这想法完全没错,只不过用错了对象。我们首先要区分Javascript的函数作用域与我们熟知的C/C++等的块级作用域。

在C/C++中,花括号内中的每一段代码都具有各自的作用域,而且变量在声明它们的代码段之外是不可见的。而Javascript压根没有块级作用域,而是函数作用域.

所谓函数作用域就是说:-》变量在声明它们的函数体以及这个函数体嵌套的任意函数体内都是有定义的。

所以根据函数作用域的意思,可以将上述代码重写如下:

1 var scope="global";
2 function t(){
3     var scope;
4     console.log(scope);
5     scope="local"
6     console.log(scope);
7 }
8 t();

我们可以看到,由于函数作用域的特性,局部变量在整个函数体始终是由定义的,我们可以将变量声明”提前“到函数体顶部,同时变量初始化还在原来位置。

为什么说Js没有块级作用域呢,有以下代码为证:

1 var name="global";
2 if(true){
3     var name="local";
4     console.log(name)
5 }
6 console.log(name);

都输出是“local",如果有块级作用域,明显if语句将创建局部变量name,并不会修改全局name,可是没有这样,所以Js没有块级作用域。

现在很好理解为什么会得出那样的结果了。scope声明覆盖了全局的scope,但是还没有赋值,所以输出:”undefined“。

所以下面的代码也就很好理解了。

 1 function t(flag){
 2     if(flag){
 3         var s="ifscope";
 4         for(var i=0;i<2;i++)
 5             ;
 6     }
 7     console.log(i);
 8     console.log(s);
 9 }
10 t(true);

输出:2  ”ifscope"

二:变量作用域

还是首先看一段代码:

 1 function t(flag){
 2     if(flag){
 3         s="ifscope";
 4         for(var i=0;i<2;i++)
 5             ;
 6     }
 7     console.log(i);
 8 }
 9 t(true);
10 console.log(s);

就是上面的翻版,知识将声明s中的var去掉。

程序会报错还是输出“ifscope"呢?

让我揭开谜底吧,会输出:”ifscope"

这主要是Js中没有用var声明的变量都是全局变量,而且是顶层对象的属性。

所以你用console.log(window.s)也是会输出“ifconfig"

当使用var声明一个变量时,创建的这个属性是不可配置的,也就是说无法通过delete运算符删除

var name=1    ->不可删除

sex=”girl“       ->可删除

this.age=22    ->可删除

三:作用域链

先来看一段代码:

 1 name="lwy";
 2 function t(){
 3     var name="tlwy";
 4     function s(){
 5         var name="slwy";
 6         console.log(name);
 7     }
 8     function ss(){
 9         console.log(name);
10     }
11     s();
12     ss();
13 }
14 t();

当执行s时,将创建函数s的执行环境(调用对象),并将该对象置于链表开头,然后将函数t的调用对象链接在之后,最后是全局对象。然后从链表开头寻找变量name,很明显name是"slwy"。

但执行ss()时,作用域链是: ss()->t()->window,所以name是”tlwy"

下面看一个很容易犯错的例子:

 1 <html>
 2 <head>
 3 <script type="text/javascript">
 4 function buttonInit(){
 5     for(var i=1;i<4;i++){
 6         var b=document.getElementById("button"+i);
 7         b.addEventListener("click",function(){ alert("Button"+i);},false);
 8     }
 9 }
10 window.onload=buttonInit;
11 </script>
12 </head>
13 <body>
14 <button id="button1">Button1</button>
15 <button id="button2">Button2</button>
16 <button id="button3">Button3</button>
17 </body>
18 </html>

当文档加载完毕,给几个按钮注册点击事件,当我们点击按钮时,会弹出什么提示框呢?

很容易犯错,对是的,三个按钮都是弹出:"Button4",你答对了吗?

当注册事件结束后,i的值为4,当点击按钮时,事件函数即function(){ alert("Button"+i);}这个匿名函数中没有i,根据作用域链,所以到buttonInit函数中找,此时i的值为4,

所以弹出”button4“。

如果你想弹出123,有一种方法,代码如下:

1 function buttonInit(){
2     for(var i=1;i<4;i++){
3         var b=document.getElementById("button"+i);
4         b.i = i;
5         b.addEventListener("click",function(){
6             alert("Button"+ this.i);
7         },false);
8     }
9 }

四:with语句

说到作用域链,不得不说with语句。with语句主要用来临时扩展作用域链,将语句中的对象添加到作用域的头部。

看下面代码

1 person={name:"yhb",age:22,height:175,wife:{name:"lwy",age:21}};
2 with(person.wife){
3     console.log(name);
4 }

with语句将person.wife添加到当前作用域链的头部,所以输出的就是:“lwy".

with语句结束后,作用域链恢复正常。

时间: 2024-10-10 10:13:55

Js作用域与作用域链详解-转的相关文章

js中的原型与原型链详解

js中的原型与原型链详解 记住下面三句话就可以理解原型: 所有的函数数据类型都天生自带一个属性Prototype(原型)这个属性的值是一个对象,浏览器会默认给他开辟一个堆内存 在浏览器给prototype开辟的堆内存当中有一个天生自带的属性是constructor,这个属性存储的值是当前函数本身 每一个对象都有一个__proto__的属性,这个属性指向当前实例所属类的prototype(如果不能确定他是谁的实例,都是Object的实例) 原型链:如果引用构造函数的实例想要查找某个属性p的话: 首

js的offsetParent属性用法详解

js的offsetParent属性用法详解:此属性是javascript中较为常用的属性,对于它的良好掌握也是非常有必要的,下面就通过代码实例介绍一下它的用法,希望能够给需要的朋友带来一定的帮助.一.基本介绍:此属性可以返回距离指定元素最近的采用定位(position属性值为fixed.relative或者absolute)父级元素,如果父级元素中没有采用定位的元素,则返回body对象的引用.语法结构: obj.offsetParent 二.代码实例: <!DOCTYPE html> <

js对象浅拷贝和深拷贝详解

js对象浅拷贝和深拷贝详解 作者:i10630226 字体:[增加 减小] 类型:转载 时间:2016-09-05我要评论 这篇文章主要为大家详细介绍了JavaScript对象的浅拷贝和深拷贝代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 本文为大家分享了JavaScript对象的浅拷贝和深拷贝代码,供大家参考,具体内容如下 1.浅拷贝 拷贝就是把父对像的属性,全部拷贝给子对象. 下面这个函数,就是在做拷贝: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 var

js 中中括号,大括号使用详解

一.{ } 大括号,表示定义一个对象,大部分情况下要有成对的属性和值,或是函数.如:var LangShen = {"Name":"Langshen","AGE":"28"};上面声明了一个名为"LangShen"的对象,多个属性或函数用,(逗号)隔开,因为是对象的属性,所以访问时,应该用.(点)来层层访问:LangShen.Name.LangShen.AGE,当然我们也可以用数组的方式来访问,如:Lang

JS中的event 对象详解

JS中的event 对象详解 JS的event对象 Event属性和方法: 1. type:事件的类型,如onlick中的click: 2. srcElement/target:事件源,就是发生事件的元素: 3. button:声明被按下的鼠标键,整数,1代表左键,2代表右键,4代表中键,如果按下多个键,酒把这些值加起来,所以3就代表左右键同时按下:(firefox中 0代表左键,1代表中间键,2代表右键) 4. clientX/clientY:事件发生的时候,鼠标相对于浏览器窗口可视文档区域的

Node.js开发入门—Stream用法详解

Stream是Node.js中非常重要的一个模块,应用广泛.一个流是一个具备了可读.可写或既可读又可写能力的接口,通过这些接口,我们可以和磁盘文件.套接字.HTTP请求来交互,实现数据从一个地方流动到另一个地方的功能. 所有的流都实现了EventEmitter的接口,具备事件能力,通过发射事件来反馈流的状态.比如有错误发生时会发射"error"事件,有数据可被读取时发射"data"事件.这样我们就可以注册监听器来处理某个事件,达到我们的目的. Node.js定义了R

AME_Thoy_Oracle自带AME审批链详解AME Standard Handler

Oracle 自带了3大类,13个子类的审批链Action Type, 对应了13个标准的AME Standard Handler 1. 按主管层次审批 absolute job level / chains of authority based on absolute job levelfinal approver only / chains of authority containing only the final job-level approvermanager then final

js 面向对象日历实现原理详解

对于前端开发来说,日历空间在网站里应用的很多,比如:填写表单时,是选取一下事件了--等等.下面就来分析一下怎么用js来写一个自己万年历. 在没有开始之前,我们是先弄明白是什么原理,要通过几个步骤来实现. 第一,我们的知道某一个月的某第一天是星期几. 第二,我们得知道某一个月有一共有几天, 只要有了这两部就可以循环出来了,下面看一下代码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http

AME_Oracle自带AME审批链详解AME Standard Handler(概念)

Oracle 自带了3大类,13个子类的审批链Action Type, 对应了13个标准的AME Standard Handler 1. 按主管层次审批 absolute job level / chains of authority based on absolute job levelfinal approver only / chains of authority containing only the final job-level approvermanager then final

js showModalDialog参数的使用详解(转)

js showModalDialog参数的使用详解_javascript技巧_脚本之家 http://www.jb51.net/article/45281.htm 本篇文章主要是对js中showModalDialog参数的使用进行了详细的分析介绍,需要的朋友可以过来参考下,希望对大家有所帮助 基本介绍: showModalDialog()              (IE 4+ 支持)showModelessDialog()           (IE 5+ 支持)window.showModa