作用域

  (1)、作用域也叫执行环境(execution context)是JavaScript中一个重要的概念。执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。在JavaScript中变量的作用域有全局作用域和局部作用域,全局变量是指变量没有在函数体内声明或者在函数内声明的时候没有带var,即表示拥有全局作用域,相反变量在函数内声明带var称为局部变量,拥有局部作用域。特殊的虽然函数参数不带var但它也属于局部变量。

 var a = 1; //全局变量
function fn1( b ){ //局部变量
var c = 2; //局部变量
d = 3; //全局变量
}

  (2)、接下来介绍一下解析过程。浏览器有专门的一段程序用于解析JavaScript,暂且给他取个名字叫JavaScript解析工具,这个解析过程至少有两个过程(当然不仅仅有两个,像编译原理的词法分析什么的,这里不提):1.寻找目标(预解析)。包括var、function、参数等。 2.逐行解读代码。接下来通过实例来具体分析是怎么做的:

 alert( a ); //undefined
var a = 1;
function fn (){
alert( 2 );
}

  具体过程:

    1.寻找目标。包括var、function、参数等
      a = undefined       (所有的变量在正式运行代码之前,都会提前赋值一个值,即undefined)。
      fn = function() { alert(2) }    (所有函数在正式运行之前,都是整个函数块)
    2.逐行解读代码。
      解读代码时表达式会改变预解析中的值,如以上代码解读到第二行时,a = undefined 变为了 a = 1;
 
特殊的:如果预解析过程中遇到重名的,只留一个。如变量和函数重名了,就只留函数。再来一个例子说明:
 alert( b );  //function a() { alert( 4 ) };
var b = 1;
alert( b ); //
function b () {
alert( 2 );
}
alert( b ); //
var b = 3;
alert( b ); //
function b (){
alert( 4 );
}
alert( b ); //
解析过程还是那两步,只是多了重名的情况。1.预解析后只留下  b = function b(){ alert(4) }。2.逐行解读后,预解析中a的值变为3,若在代码的最后调用 b(), 则会在控制台报错。
 
  (3)JavaScript中没有块级作用域。先看一个例子:
 if( true ){
var a = 1;
}
alert( a ); //

在一个 if 语句中定义变量 a。如果是在 C、C++等语言中,a会在 if 语句执行完毕后被销毁。但在JavaScript中,if 语句中的变量声明会将变量添加到当前的作用域(在这里是全局作用域)中。特别是在使用 for 语句时:

 for( var i=0; i<10; i++ ){
doSothing(i);
}
alert(i); //

对于块级作用域的语言来说,for语句初始化变量的表达式所定义的变量,只会存在于循环的环境中。而对于JavaScript来说,有for语句创建的变量 i 即使在for循环执行结束后,也依旧会存在于循环外部的作用域中。

  (4)、函数是作用域,有预解析等过程,if  for语句不是作用域。尽量不要在 if  for中定义变量和函数调用,否则有浏览器问题:

 alert(fn1);   //chrome,FF 弹出 undefine. ie 弹出整段函数(function fn19=(){alert( 123 )})
if( true ){
var a = 1;
function fn1(){
alert( 123 )
}
}
作用域链
  1.当代码在一个环境中执行时,会创建变量对象的一个作用域链。它的作用是保证对执行环境有权访问所有变量和函数的有序访问。也就是说作用域链就像是一种绳索可以将各个作用域连接起来,已达到可以访问各个域中的变量,当然这种访问是要遵守一定的规则的,即局部作用域可以通过作用域链访问所有的全局作用域,但是全局作用域不能访问局部环境中的任何变量或函数。看下面的例子:
 
 var a = 1;
function change{
var b = 2;
function swap(){
var c = b;
b = a;
a = c; //这里可以访问a,b,c
} //这里可以访问a和b,但不能访问c
swap();
} //这里只能访问a
change();

通过上面例子我们知道作用域之间的 联系是线性、有次序的。每个作用域可以沿着作用域链向上搜索,但任何作用域不能通过向下搜索而进入另一个作用域中,搜索时先搜索自己作用域内是否存在该变量,若不存在则再一级一级往上搜索。

  2.既然我们不能通过这种方式去访问局部作用域,那我们也可以用以下方法去获取函数内部的值:

    (1)、通过设置全局变量获取

 var str = '';
function fn1(){
var a = '需要拿到的值';
str = a;
}
fn1();
alert( str ); //弹出'需要拿到的值'

    (2)、通过函数调用获取

 function fn2(){
var a = '需要拿到的值';
fn3( a );
}
fn2();
function fn3( a ){
alert( a );
}

作用域链的改变

  JavaScript里的 with语句和 catch语句可以在作用域的头部临时增加一个变量对象,该变量对象会在代码执行后被移除,具体来说就是当执行到这两个语句时,作用域链会得到加长。

  (1)、with语句的作用是避免重复书写代码,如:

 function fn(){
with(document){
var btn = getElementById('btn');
var input = getElementsByClassName('input');
}
}

  这里with语句接收一个document对象,因此它的变量对象中就包含了document对象的所有属性和方法,但这个变量对象就被添加到作用域的最前头,这样看似避免了重复书写,但是性能并不好。因为被推到作用域前头,其他的变量就处于第二个作用域当中了,若要逐级访问,访问代价比较大。可以用一局部变量代替document,即可解决,而不必用with语句。

  (2)catch语句与with相类似:

 try {
//可能出错的代码
} catch (error) {
//出错时怎么处理
}

  当出现错误执行catch语句,将出错对象放入作用域头部,然后catch中其他的变量就处于第二个作用域当中了。

  最后通过几个例子强化一下,每个例子都是在前一个例子的基础上做一些调整,但结果却不一样。

(1)、

 var a = 1;
function fn1(){
alert(a); //undefined
var a = 2;
}
fn1();
alert(a); //

(2)、将(1)的第四行改为 a = 2。

 var a = 1;
function fn1(){
alert(a); //
a = 2;
}
fn1();
alert(a); //

(3)、将(2)中的 fn1函数添加参数 a,虽然结果与(1)相同,但解析过程不同。

 var a = 1;
function fn1( a ){ //a相当于局部变量,相当于var a
alert(a); //undefined
a = 2;
}
fn1();
alert(a); //

(4)、在(3)的基础上给第6行添加参数 a。

 var a = 1;
function fn1( a ){ //a相当于局部变量,相当于var a
alert(a); //
a = 2;
}
fn1(a);
alert(a); //

有错误的地方请指正。

参考资料:《JavaScript高级程序设计》

  

JavaScript作用域那些事的更多相关文章

  1. [译] 你该知道的javascript作用域 (javascript scope)(转)

    javascript有一些对于初学者甚至是有经验的开发者都难以理解的概念. 这个部分是针对那些听到 : 作用域, 闭包, this, 命名空间, 函数作用域, 函数作用域, 全局作用域, 变量作用域( ...

  2. JavaScript作用域闭包简述

    JavaScript作用域闭包简述 作用域 技术一般水平有限,有什么错的地方,望大家指正. 作用域就是变量起作用的范围.作用域包括全局作用域,函数作用域以块级作用域,ES6中的let和const可以形 ...

  3. JavaScript 作用域和变量提升

    本文是这篇文章的简单翻译. 如果按照下面的代码按照JavaScript程序的执行方式执行,alert函数会显示什么? var foo = 1; function bar() { if (!foo) { ...

  4. 深入理解JavaScript作用域和作用域链

    前言 JavaScript 中有一个被称为作用域(Scope)的特性.虽然对于许多新手开发者来说,作用域的概念并不是很容易理解,本文我会尽我所能用最简单的方式来解释作用域和作用域链,希望大家有所收获! ...

  5. 理解 JavaScript 作用域(转)

    简介 JavaScript 有个特性称为作用域.尽管对于很多开发新手来说,作用域的概念不容易理解,我会尽可能地从最简单的角度向你解释它们.理解作用域能让你编写更优雅.错误更少的代码,并能帮助你实现强大 ...

  6. JavaScript作用域

    JavaScript作用域 JavaScript作用域一直是前端开发的难题,现在只要用五句话就可解决. 一.“JavaScript中无块级作用域” 在Java或C#中存在块级作用域,即:大括号也是一个 ...

  7. 关于Javascript作用域及作用域链的总结

    本文是根据以下文章以及<Javascript高级程序设计(第三版)>第四章相关内容总结的. 1.Javascript作用域原理,地址:http://www.laruence.com/200 ...

  8. JavaScript作用域链

    之前写过一篇JavaScript 闭包究竟是什么的文章理解闭包,觉得写得很清晰,可以简单理解闭包产生原因,但看评论都在说了解了作用域链和活动对象才能真正理解闭包,起初不以为然,后来在跟公司同事交流的时 ...

  9. Python自动化 【第十六篇】:JavaScript作用域和Dom收尾

    本节内容: javascript作用域 DOM收尾 JavaScript作用域 JavaScript的作用域一直以来是前端开发中比较难以理解的知识点,对于JavaScript的作用域主要记住几句话,走 ...

随机推荐

  1. linux中的颜色控制

    \033[031m  xxx  \033[0m  ---------------------->中间的xxx部分显示为红色,不接后面的\033[0m,则以后显示的都是红色,\033表示开始和结束 ...

  2. Git分支(3/5) -- 禁用 Fast Forward 合并

    添加一个分支, 并且换到该分支: git checkout -b add-text 然后我再index.html里面添加点文字, 并commit. 然后再修改README.md, 添加文字, comm ...

  3. Git分支(5/5) -- 解决合并的冲突

    如果两个分支上都对同一个文件进行了修改, 那么就有可能发生冲突. 首先创建一个分支, 并切换到该分支上: 然后修改index.html, 修改几个地方吧. 然后查看状态, 并commit: 然后切换到 ...

  4. 关于IPFS, 你想知道的都在这里

        IPFS的的"宏伟"目标是取代HTTP, 那么先来看看IPFS是如何工作的? IPFS为每一个文件分配一个独一无二的哈希值(文件指纹: 根据文件的内容进行创建), 即使是两 ...

  5. 笔记:Spring Cloud Ribbon RestTemplate 详解

    详细介绍RestTemplate 针对几种不同请求类型和参数类型的服务调用实现,示例代码中的 restTemplate 都是通过Spring 注入方式创建的,相关代码如下: @Autowired pr ...

  6. POJ-1256 next_permutation函数应用

    字典序列: 在字典序中蕴含着一个点,就是大小的问题,谁先出现,谁后出现的问题.譬如a<b<c,出现顺序就是a,b,c. 本题中字符集是所有大小写字母,而题目中规定的谁大谁小已经不是按asc ...

  7. Linux远程连接工具

    Linux远程连接可以使用SecureCRT工具完成 SecureCRT下载地址 修改虚拟机中的网络适配器---改为桥接模式 一,配置:在Linux终端上获取IP地址----ifconfig 二,同时 ...

  8. MySQL使用和操作总结

    简介 MySQL是一种DBMS,即它是一种数据库软件.DBMS可分为两类:一类是基于共享文件系统的DBMS,另一类是基于客户机——服务器的DBMS.前者用于桌面用途,通常不用于高端或更关键应用. My ...

  9. 每天学习点js(2)

    在日常开发中可能有很多不被重视但有关系着基础的知识,下面我们就来看看这几道题吧 题1 ["1","2","3"].map(parseInt) ...

  10. 访问限制:由于对必需的库 C:/Program Files/Java/jre6/lib/rt.jar 具有一定限制,因此无法访问类型。。

    在项目上单击右键选择 属性 Java编译器 错误或警告 选择启用特定于项目的设置 建议不要使用和限制使用的API将 禁止的引用(访问规则) 设置为 警告 然后应用即可解决