“咳咳,那我接下来继续长话短说了。要回答之前的那个问题,我必须把作用域的概念再说一说。这便是我所总结的函数七重关里面的第二重关。”叶小凡继续讲解,这些概念自己在叶老的教导下早就已经不知道折腾了多少遍。

“首先,作用域如果要深究的话,还是比较复杂和晦涩难懂的,我就用通俗的话来说明作用域的问题吧。在JavaScript中,可以简单的理解作用域分为两种,一个是全局作用域,一个是函数作用域。所谓作用域,就是当你要查找某一个变量的时候,可以在什么地方找到这个变量。这个寻找的范围,就是作用域了。不管是全局作用域,还是函数作用域,都是被定义在词法阶段。词法阶段,就是刚才所说JavaScript编译代码的第一个步骤——分词。所以,词法阶段也叫作分词阶段。关于全局作用域,看一个比较简单的例子。”

var a = 10;
function test(){console.log(a);
}

“a变量和test函数都是直接暴露在外面,因此,他们都属于全局作用域。而test函数的函数体,用花括号包起来的部分,则是函数作用域了。没有错,函数的函数体都属于函数作用域。又因为test函数属于全局作用于,而它自己又拥有一个函数作用域,那么这样一来,就形成了一个作用域的嵌套。也就是说,全局作用域里面嵌套了一个函数作用域。函数作用域里面可以访问全局作用域中的变量,但是反过来就不行。比如刚才的例子,如果我直接调用test函数。”

function test(){console.log(a);
}
var a = 10;
test();

“答案必然是10,在这个例子中,函数作用域里面的a会先去当前函数作用域里面寻找是否有一个a变量。如果找不到,就去上一层包着它的父级作用域中去寻找。那么,在这个例子里面,不难看出,外面的父级作用域,也就是全局作用域中确实有一个a变量。那么,在执行函数体的时候,就可以访问到外面的a变量啦。但是,如果反过来就不行,比如这样。”

function test(){var a = 10;
}console.log(a);

代码运行,结果如下:

ReferenceError: a is not defined

at Object.<anonymous> (G:\JavaScript百炼成仙\\hello.js:5:13)

at Module._compile (internal/modules/cjs/loader.js:689:30)

at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)

at Module.load (internal/modules/cjs/loader.js:599:32)

at tryModuleLoad (internal/modules/cjs/loader.js:538:12)

at Function.Module._load (internal/modules/cjs/loader.js:530:3)

at Function.Module.runMain (internal/modules/cjs/loader.js:742:12)

at startup (internal/bootstrap/node.js:279:19)

at bootstrapNodeJSCore (internal/bootstrap/node.js:752:3)

“刚才已经说了,全局作用域包着一个函数作用域,那么函数作用域里面可以访问到全局作用域里面的变量。但是反过来看的话,全局作用域想要去调用里面的函数作用域中定义的变量,却是做不到的。因此当发生作用域嵌套的时候,只能里面的访问外面的,外面的无法访问里面的。而且,需要额外注意一点,那就是作用域嵌套一般是针对全局作用域和函数作用域,或者函数作用域和其他函数作用域而言的。比如,下面这种方式就不算是作用域嵌套。”说着,叶小凡随手就打出了一段代码流。

if(true){var a = 20;
}console.log(a);

代码运行,结果是20!

“虽然a变量的定义是写在花括号里面,但是这里并没有函数的出现,因此不算做作用域嵌套。而且我刚才也已经说了,在JavaScript中,只有全局作用域和函数作用域,你可以认为这里的a也属于全局作用域,这样方便理解。既然都是在全局作用域里面,那么console.log方法自然可以访问到同为全局作用域里面的变量a了啊。”

叶小凡讲得有理有据,饶是林元青和尹曾琪也微微点了点头。也难怪叶小凡懂的比其他弟子多,毕竟,可是有一个老怪级别的叶老在教导自己啊。

“接下来,我把代码换一下。”

if(false){var a = 20;
}console.log(a);

“瞧,我现在把if判断中的true改为了false,那么你说,下面的a打印出来是多少呢?”

“这,应该是报错吧,因为定义a变量的语句不会执行了啊。”对面弟子想了想,不是很确定地说道。

“错了,你看好。”叶小凡微微一笑,运功将代码执行了一下。得到的结果是undefined!

“什么,竟然是undefined,为什么?”对面弟子惊呼。

var a = 20;这句话在if判断中,而if判断的条件是false,所以的确不会执行。但是,执行代码是运行阶段的时候,在代码的分词阶段和解析阶段,a变量依然会被获取,并且系统会默认给它一个undefined。又因为a变量不是在某一个函数的函数体中,而是在全局作用域里面,所以console.log方法依然可以访问到这个a变量,而且获取a变量的值就是undefined。”

“接下来可以解释之前的那个问题了。”

console.log(a);
var a = function(){alert("函数被调用了!");
}
console.log(a);

“第一次执行console.log方法的时候,a变量还没有被赋值为一个函数,但是JavaScript引擎还是会把它提取出来,放入全局作用域中,并且默认给它一个undefined。所以,第一次打印出来的就是undefined。接下来,就是一个赋值语句了。”

var a = function(){alert("函数被调用了!");
}

“这个赋值语句,把一个匿名函数赋给了变量a,那么从此变量a就指向了这个函数,换句话说,一个名字叫做a的函数就已经产生了。这句话一旦执行,那么a就不再是undefined了,而是一个函数。接下来,执行第二个console.log方法,这个时候a自然是已经有值了,所以打印出来的是一个函数。”

“好,好,好!”尹曾琪连说三个号,显然对叶小凡的实力不吝赞赏,看下叶小凡的眼光中明显多了好几分神采。

“小娃娃,没想到你小小年纪,竟然可以对函数这么了解。既然你提到了作用域分为全局作用域和函数作用域,那么老夫就再来考你一考。”说着,尹曾琪随手一挥,一段代码就显示在了众人眼前。

var a = 1;function test(){var a;var inner = function (){console.log(a);}inner();}test();

“这道题,你来回答看看,答案是什么呢?”

“答案是undefined,这是函数作用域里面嵌套了函数作用域,那么最里面的inner函数中要访问一个变量啊,就会优先从inner函数里面去找,结果发现找不到。既然当前函数作用域里面找不到,那么就网上翻一层,去它的父级作用域中,也就是test函数的作用域里面去找,结果发现找到了,test函数里面定义了一个a变量,但是没有赋值,那么a就是undefined。既然已经找到了,那么就不会去全局作用域里面找a变量了。所以,全局作用域里面的a变量其实就是一个摆设。”

“好小子,回答得不错。那你再说说如果函数有参数传递的时候,会怎样?”尹曾琪点点头,认可了叶小凡的回答。

场外的观众一下子又沸腾了,要知道,在千鹤宗,尹曾琪可是出了名的抠门,每次考试都喜欢各种鸡蛋里面挑骨头,很少会像现在这样去赞同一个弟子。

“弟子遵命。关于函数的传参,其实我是把它归结到函数七重关里面的第三重关的。”

“哈哈哈,好,好,好。那你就讲讲你的第三重关吧!”尹曾琪哈哈大笑,赞赏之意更浓。

叶小凡抬头看了林元青一眼,得到授意后,就开始讲起了第三重关。

JavaScript百炼成仙 1.20 函数七重关之二 (作用域)相关推荐

  1. JavaScript百炼成仙 1.18 函数七重关之一 (函数定义)

    "这小娃娃到真是好大的口气,一口气说了函数有七重关,这老夫倒要好好品鉴一番."尹曾琪目录精光,口气中带着一丝丝嘲讽,却也有一丝好奇. 林元青也被叶小凡的话吓了一跳,全场更是炸开了锅 ...

  2. JavaScript百炼成仙 函数七重关之三(参数传递)

    前些天发现了一个巨牛的人工智能学习博客,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转 "所谓的参数,就是当函数调用的时候会传进来的值,也就是说,我们在定义参数的时候,并不知道调用的过程 ...

  3. JavaScript百炼成仙 后续章节

    因为本书已经出版了,后续章节我就不太好发出来啦,有喜欢的小伙伴可以去网上购买哦~~ 打开天猫,搜索 清华大学出版社官方旗舰店,搜索 JavaScript百炼成仙 即可~ 目录 第1章 . 掌握Java ...

  4. JavaScript百炼成仙读书笔记

    JavaScript百炼成仙 作者:剽悍一小兔 没事儿的时候多读书还是蛮好的,这本书给我的感觉就像是真的在看修仙人士不断修炼去打怪升级一样,以不一样的视角重新温习了一遍前端的知识,和以往所读的其他书籍 ...

  5. JavaScript 异步编程--Generator函数、async、await

    JavaScript 异步编程–Generator函数 Generator(生成器)是ES6标准引入的新的数据类型,其最大的特点就是可以交出函数的执行的控制权,即:通过yield关键字标明需要暂停的语 ...

  6. javascript百炼成仙 第一章 掌握JavaScript基础1.4数据类型

    修行还在继续,随着对要诀的深入,叶小凡明白,在编程世界,刚才的直接量都属于一种数据.和人有男女一样,数据也是有类型的. 在JavaScript中,数据可分为两类,分别为原生数据类型(primitive ...

  7. JavaScript百炼成仙阅读笔记

    第一章 JavaScript基础 原生数据类型5种:数字.字符串.布尔值.null.undefined 对象数据类型var x={}创建,接下来是一些对属性的操作(书上有) typeof() cons ...

  8. JavaScript百炼成仙 1

    alert("函数被调用了!"); } "我刚才就定义了一个函数,函数的定义需要用到function关键字,然后空格,加上函数的名字.刚才的代码中,函数的名字就是test ...

  9. JavaScript初学者应注意的七个细节

    每种语言都有它特别的地方,对于JavaScript来说,使用var就可以声明任意类型的变量,这门脚本语言看起来很简单,然而想要写出优雅的代码却是需要不断积累经验的.本文利列举了JavaScript初学 ...

最新文章

  1. 基于深度学习的位姿估计方法
  2. maven引入本地jar包
  3. python自动化测试面试题代码_Python自动化测试常见面试题(四)
  4. K-序列求和 (逆元)
  5. 通过QuartzCore/CoreAnimation.h实现让玫瑰花飞舞
  6. 第 6-7 课:Java 分布式框架面试题合集
  7. 千亿市场空间开启!网络安全开启新纪元步入黄金时代
  8. 新年新服务: MVP 播客上线
  9. 常用头文件的可调用函数的归类笔记
  10. 归并排序(递归实现+非递归实现+自然合并排序)
  11. memcached—如何在Windows操作系统中安装、启动和卸载memcached
  12. mysql三台部署_使用三台主机部署LNMP
  13. 虚幻引擎3命令行参数
  14. window10运行不了1stopt_1stOpt批量处理方程
  15. 360浏览器设置语言国际化
  16. user后面的计算机名更改,更改电脑用户名(可更改C:\Users\用户名)
  17. GIS应用技巧之景观格局分析(四)
  18. Minigui实现半透效果
  19. 25 条客户服务名言激励您的团队
  20. HDU6848改编题(弱化)——客星璀璨之夜(stars)

热门文章

  1. Python初级学习教程—从入门开始学习(函数、组合数据类型、文件操作、异常、模块)
  2. 完整VI项目设计书(转)
  3. 如何下载ArcGis Server离线地图瓦片数据
  4. PostgreSQL测试套-pg_regress使用
  5. OneDrive登录问题
  6. SvnAnt authentication cancelled 的解决
  7. 全球顶级的14位程序员!膜拜!
  8. CSR867x — TWS模式的应用笔记【转载】
  9. 古巴比伦理财五大金科定律
  10. 一文分析 快应用技术架构及业务前景