详解js的作用域、预解析机制
虽然,ES6在我们工作中应用得越来越广泛,但是还是很多项目保留着ES5的写法,所以,今天,带着大家重新巩固下ES5下的作用域及预解析机制。
概念:
作用域:域,指的是一个空间、范围、区域,作用指的是在域内可进行读写操作。一个变量的作用域是程序源代码中定义的这个变量的区域。
在ES5中,只存在全局和函数级作用域,在ES6中,引入了块级作用域,js的预解析机制大概分为两个过程:预解析和自上而下逐行解读
预解析:js解析器会先把var定义的变量、function、参数等一些东西存储进仓库里面(内存)。变量var在正式运行之前,都赋值为undefined,function函数在运行之前,就是整个函数块
逐行解读
表达式=、+、-、*、/、++、--、!、%.....number()、参数都可以赋值
遇到重名的,只留下一个,变量和函数重名,函数优先级高于变量,只留下函数
函数调用(函数是一个作用域,遇到作用域都会按照先进行预解析,然后逐行解读的过程执行),先局部找参数,局部找不到就自下向上找(作用域链)
概念扯了一大段,估计初学者还是有点晕乎乎,老司机就可以提前下车了,接下来,咋们举几个小栗子,结合上面的理论,深入理解。
实践
例1:
alert(a); //error: a is not defined a = 3;
分析:
预解析
上面说过,预解析时只会把var 、 function 、参数等存储起来,所以:
整个作用域没有找到var function 参数
逐行解读
预解析后,内存中存在a且被赋值了underfind整个变量,所有,代码执行过程中程序直接报错。
例2:
alert(a); //undefined var a = 3;
分析:
预解析
上面说过,预解析时只会把var 、 function 、参数等存储起来,所以:
执行到第二行时,a 的值是未定义。
逐行解读
第一行:预解析后,内存中存在a且被赋值了underfined
例3:
alert(a); // function a (){ alert(4); } var a = 1; alert(a); // 1 function a (){ alert(2); } alert(a); // 1 var a = 3; alert(a); // 3 function a (){ alert(4); } alert(a); // 3
分析:
域解析
上面说过,预解析时只会把var 、 function 、参数等存储起来,所以:
执行到第二行时,a 的值是未定义。
执行到第四行时,a 的值是函数本身,也就是function a(){alert(2);}。
执行到第六行时,a 的值还是第四行时的值,也就是function a(){alert(2);},因为函数的优先级比变量高。
执行到第八行时,a 的值就变成了function a(){alert(4);} ,因为当两个函数重名时,遵循代码从上往下执行。
逐行解读
预解析完成之后,就是代码逐行执行了,
第一行:会弹出function a(){alert(4);} ,因为预解析完成之后,被存进内存的a 的值就是function a(){alert(4);}
第二行:第二行里有表达式,a 被赋了一个新的值1 表达式会改变变量的值。表达式可以改变预解析的值。
第三行:a现在被赋值为1,所有会弹出1
第四行:只是函数的声明,并没有用到表达式,而且也没有函数的调用,所以不会改变a 的值。
第五行:因为a的值没有变化,所以还是1
第六行:使用了表达式,a 被赋了一个新的值3
第七行:会弹出3
第八行:函数的声明,不会改变a 的值。
第九行:a的值没有改变,所以还是3
通过上面的栗子,相信大家应该对变量作用域的预解析过程有一定的了解了,接下来,咋们再举几个函数作用域的栗子
例4:
var a=1; function fn1(){ alert(a); //undefined var a = 2; } fn1(); alert(a) //1
例5:
var a=1; function fn1(a){ alert(a); //1 var a = 2; } fn1(a); alert(a) //1
例6:
var a=1; function fn1(a){ alert(a); //1 a = 2; } fn1(a); alert(a) //1
例7:
var a=1; function fn1(){ alert(a); //1 a = 2; } fn1(a); alert(a) //2
这几个栗子想必不用在一步步分析吧,不过就一点小改动,可能结果就截然不同,所以,大家还是需要仔细琢磨下。
- Hadoop和Spark的异同
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(62)-EF链接串加密
- sl 2.0 重要更新
- 云计算技术原理
- WCF技术剖析之五:利用ASP.NET兼容模式创建支持会话(Session)的WCF服务
- 进入AI时代,你准备好了吗?
- TiDB 在 G7 的实践和未来
- 投资钛值的你,知道钛链是什么吗?
- Is this a MS EnterLib DAAB BUG or not?
- Silverlight 2 has a Timer (DispatcherTimer)
- 难道调用ThreadPool.QueueUserWorkItem()的时候,真是必须调用Thread.Sleep(N)吗?
- silverlight 《Hands-On-Labs》教程系列
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(67)-MVC与ECharts
- 2018年机器学习和数据科学重要会议概览
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- AkShare-使用教程-R语言调用数据接口
- AkShare-重要机构-美国商品期货交易委员会
- AkShare-重要机构-伦敦金属交易所
- 为什么不推荐使用BeanUtils属性转换工具
- 从源码打造云原生时代的「Linux」——Kubernetes
- Django-models & QuerySet API
- [奇淫巧技]GitHub个人页的新玩法
- 一致性哈希的分析与实现
- 关系型数据库查询语言 SQL 和图数据库查询语言 nGQL 对比
- 目标检测之评价指标 - mAP
- sass的基础用法
- Golang Gin 实战(十四)| 文件托管、反向代理百度网站、自实现API网关
- ceph修复pg inconsistent
- 基于kubernetes的kong网关实战
- 移动端适配必须掌握的基本概念和适配方案