前言:最近恰逢毕业季,千千万万的学生党开始步入社会,告别象牙塔似的学校生活。往往在人生的各个拐点的时候,情感丰富,感触颇深,各种对过去的美好的总结,对未来的展望。与此同时,也让诸多的老“园”工看完这些小年轻的文章后感触良多,不禁也要写上几笔,所以就出来了很多类似“毕业两年小记”、“毕业五年有感”……

  可能就是某篇博文的一句话,某碗心灵鸡汤就拨动了你心里的那根尘封已久的弦,让你情不自禁的点了个赞,还忍不住的要在下面评论区留下自己此刻心潮澎湃的印记。

  我今天不是来送鸡汤的,鸡汤虽好,可不要贪杯哦。

正文

  截止上一篇关于Javascript的博文《初探JavaScript(三)——JS带我"碰壁"带我飞》已经写了三篇。前三篇主要是从一名纯小白的角度,结合《Javascript DOM编程艺术》这本书记录下自己的一些总结和感悟。

  大致的翻完《Javascript DOM编程艺术》后,感觉自己了解肯定还是太浅了,因为当时对于”原型”、”闭包”等这些完全没概念,我知道我缺的还很多。于是,我又伸出双手,接过另外一部经典之作《Javascript权威指南》,继续Javascript之行。

  今天首先介绍下Javascript的函数作用域的概念,然后了解下什么是作用域和声明提前,最后通过一个例子剖析Javascript的作用域链。

1.变量的作用域

  稍微有些编程背景的都知道,变量的作用域分为两种:全局变量局部变量

  Javascript是一门弱类型语言。所有的变量声明都是通过var来接收,如

  1. var num = 1;
  2. var str = string”;
  3. var flag = true;

  

  看似是一个非常省事的机制,但是也有让人头疼的时候,一些隐式的类型转换经常会把搞晕,这里不做展开,后面有时间可以单独开一篇详谈。先看看全局变量和局部变量:

  1. var g = "global";
  2. function f(){
  3. var l = "local";
  4. }

  

注意1.如果在函数f()中将去掉var声明,则变量l就会从局部变量升级为全局变量。

    2.局部变量的优先级高于同名的全局变量。如果在函数f()中声明一个局部变量也为g,则全局变量就会被局部变量覆盖

2.作用域和声明提前

  看到Javascript作用域这块,可以说颠覆了以前我对作用域的认识。类似Java和C等编程语言,在花括号“{}”内的代码都是有各自的作用域的,并且在这个范围以外,这些变量是不可见的,我们称这种作用域为块级作用域

  但是这完全不适用于Javascript,因为Javascript没有块级作用域,但是Javascript有函数作用域。函数作用域简言之就是:变量在声明他们的函数体以及这个函数体嵌套的任意函数体内都是有定义的

  对于“变量在声明他们的函数体以及这个函数体嵌套的任意函数体内都是有定义的”这句话的延伸理解:变量在声明之前就已经可用。我们称这种特性为声明提前,也就是函数里的所有变量都被“提前”至函数体的顶部

下面我们看一个经典的陷阱案例:

  1. var v = "yoyo";
  2. (function(){
  3. console.log(v);
  4. var v = "check now";
  5. console.log(v);
  6. })();

  

  对于第二次执行结果“check now”没有什么特别的,为什么第一次输出的不是“yoyo”而是“undefined”。

  对于这个问题的解释就用到上面的那句话,局部变量在整个函数体始终是有定义的,即在函数体内局部变量覆盖了同名全局变量,而且,程序只有在执行到var语句时,局部变量才会被真正赋值。所以,这时你大概会明白为什么是undefined了,因为此时还没有遇到var,即没有定义,等价于下面的形式:

  1. var v = "yoyo";
  2. (function(){
  3. var scope;
  4. console.log(v);
  5. var v = "check now";
  6. console.log(v);
  7. })();

  

疑问???

将上面的代码稍稍修改为:

  1. var v = "yoyo";
  2. (function(){
  3. console.log(v);
  4. })();

  运行结果为:

  相比于上面的代码只是少了一行添加一个局部变量v并赋值的语句,但是结果却是“yoyo”。

  这里之所以输出“yoyo”,不能按照上面的定式思维。上面有句话叫“局部变量在整个函数体始终是有定义的”,但是这里没有局部变量的定义,所以按照下面要提到的作用域链会逐层向上寻找变量,最后找到了全局变量v,从而最后的输出是“yoyo”。

  以上是我的个人理解,如果你对这两种情况有自己的理解,请在下方给出,望不吝指教。

3.作用域链

  全局变量在程序中始终是有定义的局部变量在声明它的函数体内以及其所嵌套的函数内始终是有定义的

  每一段Javascript代码(全局代码或函数)都有一个与之相关联的作用域链,这个作用域链就是一个对象列表或链表。比如当Javascript需要查找变量x的值时,它会从链中的第一个对象开始,如果该对象有一个名为x的属性,则直接使用,如果不存在名为x的属性,则会继续向链上的下一个对象查找,如此递归下去直到找到。如果整个链上都找不到,则认为不存在x这个属性。举例:

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

  

  本文链接:《初探JavaScript(四)——作用域链和声明提前》 

  如果对你有用,欢迎点赞^_^

友情赞助

如果你觉得博主的文章对你那么一点小帮助,恰巧你又有想打赏博主的小冲动,那么事不宜迟,赶紧扫一扫,小额地赞助下,攒个奶粉钱,也是让博主有动力继续努力,写出更好的文章^^。

    1. 支付宝                          2. 微信

                      

初探JavaScript(四)——作用域链和声明提前的更多相关文章

  1. javascript篇-----函数作用域,函数作用域链和声明提前

    在一些类似C语言的编程语言中,花括号内的每一段代码都具有各自的作用域,而且变量在声明它们的代码段之外是不可见的(也就是我们不能在代码段外直接访问代码段内声明的变量),我们称之为块级作用域,然而,不同于 ...

  2. JavaScript系列----作用域链和闭包

    1.作用域链 1.1.什么是作用域 谈起作用域链,我们就不得不从作用域开始谈起.因为所谓的作用域链就是由多个作用域组成的.那么, 什么是作用域呢? 1.1.1作用域是一个函数在执行时期的执行环境. 每 ...

  3. 从零开始讲解JavaScript中作用域链的概念及用途

    从零开始讲解JavaScript中作用域链的概念及用途 引言 正文 一.执行环境 二.作用域链 三.块级作用域 四.其他情况 五.总结 结束语 引言 先点赞,再看博客,顺手可以点个关注. 微信公众号搜 ...

  4. 理解JavaScript的作用域链

    上一篇文章中介绍了Execution Context中的三个重要部分:VO/AO,scope chain和this,并详细的介绍了VO/AO在JavaScript代码执行中的表现. 本文就看看Exec ...

  5. 从函数作用域和块级作用域看javascript的作用域链

    在ES6之前,javascript只有全局作用域和函数作用域.所谓作用域就是一个变量定义并能够被访问到的范围.也就是说如果一个变量定义在全局(window)上,那么在任何地方都能访问到这个变量,如果这 ...

  6. 理解JavaScript中作用域链的关系

    javascript里的关系又多又乱.作用域链是一种单向的链式关系,还算简单清晰:this机制的调用关系,稍微有些复杂:而关于原型,则是prototype.proto和constructor的三角关系 ...

  7. javascript 之作用域链-07

    复习作用域 上一节我们说到作用域:是指变量可以访问的范围,他规定了如何查找变量,以及确定当前执行代码对变量的访问权限:也说到静态作用域即词法作用域,是在编译阶段决定变量的引用(由程序定义的位置决定,和 ...

  8. javascript 之作用域链-10

    前言 在<执行环境>文中说到,当JavaScript代码执行一段可执行代码时,会创建对应的执行上下文(execution context). 变量对象(Variable object,VO ...

  9. JavaScript的作用域链

    /* js当中 每个函数都是一个执行环境 函数调用函数会进入新的执行环境结束之后再回来当前 作用域链: 在内部的作用域中可以访问和修改外部的变量 在外部作用域不能修改或者访问内部的变量 */ var ...

随机推荐

  1. cent7内核升级4.9

    一.手动档 手动档就是从源码开始编译内核安装,好处是可以自己选择任意版本的内核,缺点就是耗时长,编译安装消耗系统资源 1.1.获取 kernel 源码 这世界上最伟大的 Linux 内核源码下载地址是 ...

  2. 《利用Python进行数据分析》第6章学习笔记

    数据加载.存储与文件格式 读写文本格式的数据 逐块读取文本文件 read_xsv参数nrows=x 要逐块读取文件,需要设置chunksize(行数),返回一个TextParser对象. 还有一个ge ...

  3. 关于提高webview里下载apk安装包成功率的研究

    移动互联网发展到当下,各种领域出现了一些大的app平台,这些app共同的特点就是拥有大量忠实活跃用户, 要知道,有用户就有money啊~ 于是乎其他不太知名的平台,开始在他们上面疯狂投放广告,做活动推 ...

  4. ListView用法总结

    前言 列表,它作为一种非常重要的显示形式,不管是在web端还是在移动平台上,都是一种非常友好的,功能强大的展现形式.在Android中,ListView就接管了这一重任.尽管在Android5.X时代 ...

  5. Java构建工具Ant小记(一)

    Ant简介 Ant是基于java的构建工具.理论上来说它类似与make工具,但是却克服了make的一些固有的缺陷. 传统的Make是基于操作系统shell的构建工具,虽然也可以基于工作的os对make ...

  6. Android(蓝牙)

    因近期项目需求调试了Android蓝牙通讯接口,主要是两个终端作为服务端和客户端的通信,本文将部分重要知识点记录如下. 蓝牙是短距离无线通信,通常分经典蓝牙和低功耗蓝牙(即蓝牙4.0),两类蓝牙协议各 ...

  7. System.load(String filename)和System.loadLibrary(String libname)的区别

    前言 之前一篇文章在写Native方法的时候,第一个步骤里面有这么一段代码 static { System.load("D:" + File.separator + "H ...

  8. git 合并分支

    当前git有PreRelease和Release两个分支,现在需要将前者合并到后者. Clone版本 在本地clone一个目标工程. 然后右键 Tortoise-->Switch/Checkou ...

  9. java提高篇(十一)-----强制类型转换

    在java中强制类型转换分为基本数据类型和引用数据类型两种,这里我们讨论的后者,也就是引用数据类型的强制类型转换. 在Java中由于继承和向上转型,子类可以非常自然地转换成父类,但是父类转换成子类则需 ...

  10. Cassandra中的数据一致性

       Cassandra中数据一致性指的是数据行在各个复制节点(replicas)上的更新和同步程度.通过提供tunable consistency,Cassandra扩展了eventual cons ...