深入了解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中sort()方法的用法
Nov 04 Javascript
XENON基于JSON变种
Jul 27 Javascript
理解Javascript_15_作用域分配与变量访问规则,再送个闭包
Oct 20 Javascript
javascript针对DOM的应用分析(二)
Apr 15 Javascript
js中使用replace方法完成某个字符的转换
Aug 20 Javascript
bootstrap table sum总数量统计实现方法
Oct 29 Javascript
使用Vue制作图片轮播组件思路详解
Mar 21 Javascript
在Vue项目中引入腾讯验证码服务的教程
Apr 03 Javascript
详解javascript中的变量提升和函数提升
May 24 Javascript
vue.js实现标签页切换效果
Jun 07 Javascript
JS实现获取word文档内容并输出显示到html页面示例
Jun 23 Javascript
ES6模板字符串和标签模板的应用实例分析
Jun 25 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笔试题
2009/08/04 PHP
在PHP中操作Excel实例代码
2010/04/29 PHP
PHP详解ASCII码对照表与字符转换
2011/12/05 PHP
PHP实例分享判断客户端是否使用代理服务器及其匿名级别
2014/06/04 PHP
如何通过Linux命令行使用和运行PHP脚本
2015/07/29 PHP
Yii2配置Nginx伪静态的方法
2017/05/05 PHP
php中青蛙跳台阶的问题解决方法
2018/10/14 PHP
JavaScript 原型学习总结
2010/10/29 Javascript
仿猪八戒网左下角的文字滚动效果
2011/10/28 Javascript
js动态添加onload、onresize、onscroll事件(另类方法)
2012/12/26 Javascript
JS保留两位小数,多位小数的示例代码
2014/01/07 Javascript
浅析node.js中close事件
2014/11/26 Javascript
JavaScript中计算网页中某个元素的位置
2015/06/10 Javascript
IE下JS保存图片的简单实例
2016/07/15 Javascript
KnockoutJS 3.X API 第四章之数据控制流with绑定
2016/10/10 Javascript
JS基于递归实现倒计时效果的方法
2016/11/26 Javascript
js replace()去除代码中空格的实例
2017/02/14 Javascript
原生js二级联动效果
2017/06/20 Javascript
swiper.js插件实现pc端文本上下滑动功能示例
2018/12/03 Javascript
vue如何搭建多页面多系统应用
2020/06/17 Javascript
使用Python解析JSON数据的基本方法
2015/10/15 Python
python cx_Oracle模块的安装和使用详细介绍
2017/02/13 Python
win10下Python3.6安装、配置以及pip安装包教程
2017/10/01 Python
python中requests库session对象的妙用详解
2017/10/30 Python
Python爬取当当、京东、亚马逊图书信息代码实例
2017/12/09 Python
python pytest进阶之fixture详解
2019/06/27 Python
Python学习之路之pycharm的第一个项目搭建过程
2020/06/18 Python
详解Css3新特性应用之过渡与动画
2017/01/10 HTML / CSS
全球在线商店:BerryLook
2019/04/14 全球购物
迎新晚会策划方案
2014/06/13 职场文书
公司法定代表人授权委托书
2014/09/29 职场文书
表彰大会新闻稿
2015/07/17 职场文书
MySQL 百万级数据的4种查询优化方式
2021/06/07 MySQL
详细分析PHP7与PHP5区别
2021/06/26 PHP
Nginx配置之实现多台服务器负载均衡
2021/08/02 Servers
python数字类型和占位符详情
2022/03/13 Python