JavaScript的作用域和作用域链。在初学JavaScript时,觉得它就和其他语言没啥区别,尤其是作用域这块,想当然的以为“全局变量就是在整个程序的任何地方都可以访问,也就是写在函数外的变量,局部变量也就是写在函数内部或循环体内部,出了循环体和函数就不可访问”,但是在JavaScript中并不是这么简单,需要去深入的学习。


一. 什么是作用域
任何程序语言都有作用域的概念,简单的说,作用域就是变量与函数的可访问范围。比如C/C++等,都是块级作用域,也就是说在每一个代码块内声明的变量,出了这个代码块就是不可见的,而JS根本就没有块级作用域这个概念,而是函数作用域。如下面的例子:

1
2
3
4
5
6
7
8
9
10
function test(){
    var sum = 0;
    for (var i = 0; i < 2; i++)
    {
        sum = sum + i;
    }
    console.log(i);
}
 
test();

上面的程序输出结果为2,知道c和c++或java的同学都会知道,在for循环体块中定义的变量,在循环体外部是不可访问的,可是在JS中,没有块作用域这个概念,只有函数作用域的概念,所以只要是在函数体内部定义的变量,在这个函数体内都是可访问的。

二. 函数作用域
看到上面的小例子,应该对函数作用域有一点模糊的理解吧。其实也就是说,变量和函数在声明它们的函数体中,和这个函数体嵌套的任意函数体内部都是可访问的。下面再看个小例子:

1
2
3
4
5
6
7
8
9
10
11
function test(){
    var name = "xiyangyang";
 
    function showname(){
        console.log(name);
    }
    showname();
}
 
test(); //xiayangyang
showname(); //ReferenceError: showname is not defined

结果如代码后的注释,可见变量和函数只在当前运行函数体的内部有效,在当前运行函数体的外部是无效的。

三. 变量作用域
JS的变量计较特殊,下面是一些小例子,请看它的特殊点:
(1)全局变量被覆盖:

1
2
3
4
5
6
7
8
9
var name = "huitaiyang"
 
function test(){
    console.log(name);
    var name = "xiyangyang";
    console.log(name);
}
 
test();

如上代码不知道JS变量作用域的肯定会觉得,第一个输出“huitaiyang”,第二个输出“xiyangyang”,其实不是的,第一个会输出“undefined”,第二个是“xiyangyang”(其实一开始我也是这么想的),无论如何请随时谨记,JS是函数作用域,也就是它首先会在函数内部寻找name属性,找不到才会继续往上一层寻找。
这个小例子中,在test函数内部有name的定义,也就是找到了,它不会在往上层寻找了,但是name在打印时并没有赋值,所以就输出了undefined,第二次打印时已经赋值,就是正常的“xiyangyang”。

(2)没有var声明的局部变量,上升为全局变量:

1
2
3
4
5
6
7
function test(){
    name = "xiyangyang";
    console.log(name);
}
 
test();
console.log(name);

上面代码两次打印结果都是“xiyangyang”,所以没有var声明的变量都是全局变量,是window对象的属性,console.log(name); 这样和上面的结果一致。

四. 作用域链
一旦函数创建,函数的作用域就确定了,作用域链就由作用域中对象的集合组成。当函数执行时,它会把当前正在执行的函数内部的所有变量(包括this)置于作用域链的首部,会把该函数外部的对象置于第二,第三…层,window对象置于最外层。作用域链的层数和函数的层数有关。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var name = "huitaiyang"
 
function test(){
    var name = "xiyangyang";
    function show1(){
        var name = "lanyangyang";
        console.log(name);
    }
    function show2(){
        console.log(name);
    }
    show1();
    show2();
}
 
test();

在上面的例子中,打印出来的结果是“langyangyang”和“xiyangyang”。
解析:当test()执行时,运行到show1,创建show1的执行环境,将show1的所有内部变量都置于作用域链的首部,然后将函数test的所有对象都置于show1的后面,最后是window对象,然后从作用域链的头部开始查找name属性,所以show1()的作用域链是:show1()->test()->window
,所以name就是lanyangyang;同理show2()的作用域链是:show2()->test()->window
,所以name就是xiyangyang。

五. 作用域链和代码优化
看完上面的所有内容的同学就会很容易理解这里,如下这个小例子:

1
2
3
4
5
function changeColor(){
    document.getElementById('btn').onclick = function(){
        document.getElementById('obj').style.backgroundColor = 'red';
    };
}

大家都知道document是全局变量,也就是在作用域链的最尾部,查找是很消耗资源的,所以需要优化,优化后代码为:

1
2
3
4
5
6
function changeColor(){
    var doc = document;
    doc.getElementById('btn').onclick = function(){
        doc.getElementById('obj').style.backgroundColor = 'red';
    };
}

这样找一次就够了,所以当一个对象被跨作用域访问时,可以把它存储为局部变量使用,这样就起到了优化的作用。

六. 修改作用域链
这里就简单的提一下,能理解就有好了,with和catch会修改函数的作用域链。
(1)如果代码块中有with,则with中的所有对象会置于当前作用域链的最顶层,当前函数会被置于第二层。会降低代码的性能,所以不推荐使用。
(2)catch语句会把异常对象置于作用域链的顶部,当前执行函数会被置于第二层。同样影响代码的性能。

js的作用域与作用域链的更多相关文章

  1. 了解 JS 作用域与作用域链

    (1)作用域 一个变量的作用域(scope)是程序源代码中定义的这个变量的区域. 1. 在JS中使用的是词法作用域(lexical scope) 不在任何函数内声明的变量(函数内省略var的也算全局) ...

  2. Js作用域与作用域链详解

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

  3. Js作用域与作用域链详解[转]

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

  4. js作用域与作用域链

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

  5. JS的作用域和作用域链

    每个函数都有自己的作用域,当执行流进入一个函数时,函数就会被推入栈中,而在函数执行之后,栈将其执行环境弹出,把控制权放回给之前的作用域,全局作用域是最外围的一个作用域,因此,所有全局变量和函数都是作为 ...

  6. js学习--变量作用域和作用域链

    作为一名菜鸟的我,每天学点的感觉还是不错的.今天学习闭包的过程中看到作用域与作用域链这两个概念,我觉得作为一名有追求的小白,有必要详细了解下. 变量的作用域 就js变量而言,有全局变量和局部变量.这里 ...

  7. JS -- The Scope Chain 作用域链

    The Scope Chain JavaScript is a lexically scoped language: the scope of a variable can be thought of ...

  8. js 作用域,作用域链,闭包

    什么是作用域? 作用域是一种规则,在代码编译阶段就确定了,规定了变量与函数的可被访问的范围.全局变量拥有全局作用域,局部变量则拥有局部作用域. js是一种没有块级作用域的语言(包括if.for等语句的 ...

  9. Js 作用域与作用域链与执行上下文不得不说的故事 ⁄(⁄ ⁄•⁄ω⁄•⁄ ⁄)⁄

    最近在研究Js,发现自己对作用域,作用域链,活动对象这几个概念,理解得不是很清楚,所以拜读了@田小计划大神的博客与其他文章,受益匪浅,写这篇随笔算是自己的读书笔记吧~. 作用域 首先明确一个概念,js ...

  10. 理解js中的作用域,作用域链以及闭包

    作用域变量作用域的类型:全局变量和局部变量全局作用域对于最外层函数定义的变量拥有全局作用域,即对任何内部函数来说,都是可以访问的 <script> var outerVar = " ...

随机推荐

  1. SPOJ - FAVDICE 简单期望

    dp[0]=0; // rep(i,1,n) dp[i]=(double)(n-i)/n*dp[i-1]+1+(double)(i)/n*dp[i]; // (n-i)/n dp[i]= n-i / ...

  2. Apache Shiro(五)-登录认证和权限管理ssm

    创建一个web动态项目 jar包 web.xml web.xml做了如下几件事情1. 指定spring的配置文件有两个 applicationContext.xml: 用于链接数据库的 applica ...

  3. nginx 反向代理导致的session丢失的问题

    [原文链接] https://blog.csdn.net/xiaweiyidengzhewo/article/details/80921750 注意这篇文章解释的是“丢失”而不是“一致性”

  4. docker 部署disconf 以及将其做成镜像

    1.需要一台服务器(阿里云,腾讯云.实体服务器都行,本次是以实体服务器为依照做的) 2.安装docker   https://www.cnblogs.com/shijunjie/p/10436293. ...

  5. 2019.03.28 读书笔记 关于try catch

    try catch 在不异常的时候不损耗性能,耗损性能的是throw ex,所以在非异常是,不要滥用throw,特别是很多代码习惯:if(age<0) throw new Exception(& ...

  6. rsync 问题总结

    Rsync服务常见问题汇总讲解:==================================1. rsync服务端开启的iptables防火墙  [客户端的错误]   No route to ...

  7. shell 括号的区别

    $() 用于命令交换 里面会会执行命令,如果你写其他的: 会直接报错的 ` ` 也是用于命令交换的哦   和$() 的操作是一样的 ${ } 用于变量替换 每次调用环境的时候是需要带一个${ } 但是 ...

  8. RBAC基于角色的权限访问控制

      RBAC是什么,能解决什么难题?ThinkPHP中RBAC实现体系安全拦截器认证管理器访问决策管理运行身份管理器ThinkPHP中RBAC认证流程权限管理的具体实现过程RBAC相关的数据库介绍Th ...

  9. 如何在vue && webpack 项目中的单文件组件中引入css

    引入方式很简单,就是在script下使用require()即可. 因为import 是import...from 的形式,所以是不需要的. <script> import {mapStat ...

  10. 如何让JS变量和字符串拼接后,是变量而不是字符串

    今天有个非常有趣的事,因为我需要用JS去实现多语言,就是我在JS文件里定义了不同的变量,尝试用变量拼接字符串组成之前定义好的变量名称,结果拼接之后,显示的却是字符串,而不是变量,所以无法解析 zh_t ...