作用域

  (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. SpringMVC的工作流程以及组件说明

    1. SpringMVC处理流程 2. SpringMVC架构 2.1 框架结构 2.2 框架流程 1. 用户发送请求至前端控制器DispatcherServlet. 2. DispatcherSer ...

  2. Python 中 mySQL 中的语句

    class DeleteInventorybusiness(BaseBusiness): def DeleteInventory(self,Delete_goodsID): DeleteInvento ...

  3. react开发

    webpack.config.js var webpack=require("webpack"); var htmlWebpackPlugin=require('html-webp ...

  4. (转)SQLite内置函数

    一.聚合函数: SQLite中支持的聚合函数在很多其他的关系型数据库中也同样支持,因此我们这里将只是给出每个聚集函数的简要说明,而不在给出更多的示例了.这里还需要进一步说明的是,对于所有聚合函数而言, ...

  5. maven库

    1.本地仓库 本地仓库是你本地的一个山寨版,只有你看的到,主要起缓存作用. 当你向仓库请求插件或依赖的时候,会先检查本地仓库里是否有.如果有则直接返回,否则会向远程仓库请求,并做缓存. 本地仓库默认在 ...

  6. Sagit.Framework For IOS 开发框架入门教程6:网络请求STHttp

    前言: IOS的文章,今天,再来补一篇,Sagit的教程: 虽然感觉IOS的文章没什么观众,还是努力写吧,-_-〜 Sagit 开源地址:https://github.com/cyq1162/Sagi ...

  7. 打印十字图 JAVA 递归实现

    这个是我自己想的,头疼了一个下午,不过还好.做出来了.在网上找这道题但没有找到用递归的做法. /*递归思想实现 * 标题:打印十字图 小明为某机构设计了一个十字型的徽标(并非红十字会啊),如下所示(可 ...

  8. Jquery点击除了指定div元素其他地方,隐藏该div

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <script ty ...

  9. Java基础学习笔记八 Java基础语法之接口和多态

    接口 接口概念 接口是功能的集合,同样可看做是一种数据类型,是比抽象类更为抽象的”类”.接口只描述所应该具备的方法,并没有具体实现,具体的实现由接口的实现类(相当于接口的子类)来完成.这样将功能的定义 ...

  10. 关于hadoop集群下Datanode和Namenode无法访问的解决方案

    HDFS架构 HDFS也是按照Master和Slave的结构,分namenode,secondarynamenode,datanode这几个角色. Namenode:是maseter节点,是大领导.管 ...