深入了解JavaScript词法作用域


Posted in Javascript onJuly 29, 2020

JavaScript并不是传统的块级作用域,而是函数作用域!

一、作用域

  • JavaScript引擎在代码执行前会对其进行编译,在这个过程中,像var a = 2 这样的声明会被分解成两个独立的步骤:

第一步(编译阶段):var a 在其作用域中声明新变量。这会在最开始的阶段,也就是代码执行前进行。
第二步(运行阶段):a = 2 会查询变量a(LHS查询)并对其进行赋值。

  • LHS & RHS(当前作用域->上级作用域->...->全局作用域)

LHS(左侧):试图找到变量的容器本身
RHS(右侧):查找某个变量的值

示例:

function foo(a){
	var b = a;
	return a + b;
}
var c = foo(2);
// LHS(3处):c;a(隐式变量分配);b;
// RHS(4处):foo(2);=a;a;b;
  • 异常
function foo(a){
	console.log(a + b);
	b = a;
}
foo(2);

(1)在ES5“严格模式”下,LHS抛出ReferenceError;“非严格模式”下,LHS会自动隐式的创建一个全局变量。

(2)RHS查询在所有嵌套的作用域中遍寻不到所需遍历会抛出ReferenceError。

(3)RHS查询到一个变量,但你尝试对其不合理的操作(引用null或undefined类型中的属性),会抛出TypeError。

一句话概括之:ReferenceError同作用域判别失败相关;而TypeError则代表作用域判别成功了,但是对结果的操作是非法或不合理的。

PS:从原理上阐述了博客中《JavaScript函数及其prototype》函数执行覆盖等问题!!!

二、词法作用域

词法作用域意味着作用域是由书写代码时函数声明的位置来决定的。JavaScript中有两个机制可以“欺骗”词法作用域:eval(...)和with。

  • eval

eval函数可以接受一个字符串参数,并将其中的内容视为好像在书写时存在于程序中这个位置的代码(在当前位置,可生成代码,并运行)。
eval可以对一段包含一个或多个声明的“代码”字符串进行演算,并借此修改已经存在的词法作用域(运行阶段)。

function foo(str,a){
	eval(str);
	console.log(a, b);			//1 , 3
	console.log(a, window.b);	//1 , 2
}
var b = 2;
foo("var b = 3;", 1);

解释:上述全局变量b被覆盖了,由于b是全局的,可以window.b获取到;但非全局的变量如果被覆盖,就无法访问到了!
严格模式下:

function foo(str,a){
	"use strict";
	eval(str);
	console.log(a, b);			//1 , 2
	console.log(a, window.b);	//1 , 2
}
var b = 2;
foo("var b = 3;", 1);
  • with

with通常被当作重复引用一个对象中的多个属性的快捷方式,可以不需要重复引用对象本身。
with将对象的属性当作作用域中的标识符来处理,从而创建了一个新的词法作用域(运行阶段)。

function foo(obj){
	with(obj){
		a = 2;
	}
}
var o1 = { a : 3 };
var o2 = { b : 3 };

foo(o1);
console.log( o1.a );	// 2

foo(o2);
console.log( o2.a );	// undefined
console.log( a );		// 2,表明a泄漏到全局作用域上了!

这两个机制的副作用是引擎无法在编译时对作用域查找进行优化,导致代码运行速度变慢,建议不要使用它们!

PS:从原理上阐述了博客《 JavaScript语言精粹【糟粕、毒瘤】》中with不能使用的原因!!!

三、函数作用域和块作用域

匿名和具名

/* 匿名(引用自身只能用过期的arguments.callee引用) */
setTimeout(function(){
	console.log("i wait 1 second!")
},1000);
/* 具名(可读性好) */
setTimeout(function timeoutHandler(){
	console.log("i wait 1 seco nd!")
},1000);

立即执行函数表达式

/* IIFE模式 */
var a = 2;
(function IIFE(global){
	var a = 3;
	console.log(a);			//3
	console.log(global.a);	//2
})(window);
/* UMD模式 */
var b = 2;
(function UMD(def){
	def(window);
})(function tmpF(global){
	var b = 3;
	console.log(b);			//3
	console.log(global.b);	//2
});

块作用域

try/catch会创建一个块作用域

try{
	undefined();
}catch(err){
	console.log(err);	//可以正常使用
}
console.log(err);	//ReferenceError: err is not defined

/* 坑1 */
for(var i=0;i<10;i++){}
console.log(i);		//10
/* 坑2 */
{
	console.log(bar);	//undefined 不会报错!!
	var bar = 2;
}

ES6中引入新的let关键字!!

/* 填坑1 */
for(let i=0;i<10;i++){}
console.log(i);		//SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode
/* 填坑2 */
{
	console.log(bar);	//SyntaxError 报错!!
	let bar = 2;
}

推荐两个将ES6代码转化成兼容ES6之前的环境(大部分是ES5,但不全是)工具:Traceur和let-er

以上就是深入了解JavaScript词法作用域的详细内容,更多关于JavaScript词法作用域的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
用js生产批量批处理执行命令
Jul 28 Javascript
js表数据排序 sort table data
Feb 18 Javascript
javascript 设置某DIV区域内的checkbox复选框
Nov 30 Javascript
javascript实现tabs选项卡切换效果(自写原生js)
Mar 19 Javascript
文件编码导致jquery失效的解决方法
Jun 26 Javascript
jquery.fastLiveFilter.js实现输入自动过滤的方法
Aug 11 Javascript
jQuery 1.9.1源码分析系列(十)事件系统之主动触发事件和模拟冒泡处理
Nov 24 Javascript
jquery判断iPhone、Android设备类型
Sep 14 Javascript
mpvue小程序仿qq左滑置顶删除组件
Aug 03 Javascript
jQuery实现鼠标移到某个对象时弹出显示层功能
Aug 23 jQuery
用云开发Cloudbase实现小程序多图片内容安全监测的代码详解
Jun 07 Javascript
iview实现动态表单和自定义验证时间段重叠
Jan 10 Javascript
vue监听dom大小改变案例
Jul 29 #Javascript
VUE实时监听元素距离顶部高度的操作
Jul 29 #Javascript
详解JavaScript作用域 闭包
Jul 29 #Javascript
Angular+ionic实现折叠展开效果的示例代码
Jul 29 #Javascript
Vue 监听元素前后变化值实例
Jul 29 #Javascript
使用eslint和githooks统一前端风格的技巧
Jul 29 #Javascript
vue-cli或vue项目利用HBuilder打包成移动端app操作
Jul 29 #Javascript
You might like
献给php初学者(入门学习经验谈)
2010/10/12 PHP
PHP实现GIF图片验证码
2015/11/04 PHP
7个Javascript地图脚本整理
2009/10/20 Javascript
浅析Js中的单引号与双引号问题
2013/11/06 Javascript
javascript实现在指定元素中垂直水平居中
2015/09/13 Javascript
Javascript 引擎工作机制详解
2016/11/30 Javascript
Vue中使用vux的配置详解
2017/05/05 Javascript
JS中将多个逗号替换为一个逗号的实现代码
2017/06/23 Javascript
vue+vux实现移动端文件上传样式
2017/07/28 Javascript
vue2利用Bus.js如何实现非父子组件通信详解
2017/08/25 Javascript
javascript修改浏览器title方法 JS动态修改浏览器标题
2017/11/30 Javascript
js和jQuery以及easyui实现对下拉框的指定赋值方法
2018/01/23 jQuery
深入理解移动前端开发之viewport
2018/10/19 Javascript
解决使用layui的时候form表单中的select等不能渲染的问题
2019/09/18 Javascript
JavaScript Dom 绑定事件操作实例详解
2019/10/02 Javascript
vue.js使用v-model实现父子组件间的双向通信示例
2020/02/05 Javascript
Vue中实现回车键切换焦点的方法
2020/02/19 Javascript
js实现飞机大战游戏
2020/08/26 Javascript
VUE UPLOAD 通过ACTION返回上传结果操作
2020/09/07 Javascript
解决idea开发遇到javascript动态添加html元素时中文乱码的问题
2020/09/29 Javascript
python清除字符串里非数字字符的方法
2015/07/02 Python
详解如何管理多个Python版本和虚拟环境
2019/05/10 Python
Python button选取本地图片并显示的实例
2019/06/13 Python
numpy按列连接两个维数不同的数组方式
2019/12/06 Python
python爬虫开发之selenium模块详细使用方法与实例全解
2020/03/09 Python
vue.js刷新当前页面的实例讲解
2020/12/29 Python
单位提档介绍信
2014/01/17 职场文书
差生评语大全
2014/05/04 职场文书
爱国主义教育活动总结
2014/05/07 职场文书
会计演讲稿范文
2014/05/23 职场文书
小学生植树节活动总结
2014/07/04 职场文书
个人授权委托书格式
2014/08/30 职场文书
Python scrapy爬取起点中文网小说榜单
2021/06/13 Python
使用Oracle跟踪文件的问题详解
2021/06/28 Oracle
JavaScript高级程序设计之变量与作用域
2021/11/17 Javascript
Java设计模式之享元模式示例详解
2022/03/03 Java/Android