JS作用域的一些理解

最近看了一本《你不知道的JavaScript》书籍,发现里面讲的很有意思:大部分人可能都只是在使用JavaScript,碰到某些问题直接百度一下就ok了,而碰到一些更难的可能就直接跳过了。

这本书中有句话我很赞同,“JavaScript不必有多理解就可以很好地使用,因此很多js开发者并没有多理解这门语言本身,这是我们面临的最大挑战”。

这本书并没有像各种教程一样,教我们实用的东西,而是更深入的理论性的东西,在看到作用域这一章节时,发现了一些有意思的东西,也知道了为什么JS经常会被诟病很多漏洞的冰山一角。

就拿我们最常见的关键字:变量的声明符开始,ES6中引入了letconst关键字,我们也养成了一种习惯准则:避免使用var来声明变量,使用let代替,对于确定不会改变或不需要改变的变量,优先使用const声明。

我们思考下为什么js的作者要引入这两个关键字呢,或者说var**有什么漏洞需要let来修复?

这里举一些很简单的例子,首先我们知道,在JavaScript中,function函数会带有作用域,(作用域我理解为限制变量的访问,规定他在哪里生效),代码块{}也会引入作用域,但为什么{}在之前却没有产生他该代入作用域的概念呢?以及我们所常说的var会带来变量提升的隐患具体指什么呢?

这里我们一个个来实践解答。

首先看这段简单的代码:

fucntion foo() {
	var a = 1
}
foo()
console.log(a) // Uncaught ReferenceError: a is not defined

这会是什么结果呢?大家都知道会报ReferenceError引用错误,因为函数foo引入了作用域,变量a在函数作用域声明,在外部作用域即全局顶层作用域没有找到a这个变量,所以直接报错。

我们进行简单的修改

function foo() {
	function bar (a) {
		i = 3
		console.log(a + i)
	}
	for (var i = 0; i < 5; i++) {
		bar(i)
	}
}
foo() // 死循环!很快电脑就卡死了!

出现了死循环!电脑一会就卡死了!为什么会这样呢,我们原本想的是变量i只在for循环的作用域中生效,即for(var i ){ // i应该只在这里生效 },因为{}引入块作用域,这是正常的思路。

但是由于var无视{}块作用域,导致变量i的作用域实际上为foo()函数中,所以在每次运行至bar(i)的时候,在bar函数中都会给i复制为3,导致出现了死循环。这便是var的缺陷之一:无视块作用域。

我们需要怎么调整这个代码让他正确运行呢?很简单,由于函数引入作用域,所以我们可以在bar函数中声明一个新的变量i,这样就不会影响for中的i

function foo() {
	function bar (a) {
		var i = 3
		console.log(a + i)
	}
	for (var i = 0; i < 5; i++) {
		bar(i)
	}
}
foo() // 3,4,5,6,7

最优的办法是什么呢,对,在for循环中使用let声明变量i,这样let限制了只在当前for循环块中可访问,bar函数中的i会隐式创建一个新的变量,正常运行。

function foo() {
	function bar (a) {
		i = 3
		console.log(a + i)
	}
	for (let i = 0; i < 5; i++) {
		bar(i)
	}
}
foo() // 3,4,5,6,7

在for循环中使用const声明变量i可以吗?当然不可以const声明的是当前作用域的常量,无法改变其值的,所以一如果这样做

function foo() {
	function bar (a) {
		i = 3
		console.log(a + i)
	}
	for (const i = 0; i < 5; i++) {
		bar(i)
	}
}
foo() // 3 , Uncaught TypeError: Assignment to constant variable.

输出第一次的打印结果3后,便会报类型异常给常量赋值的错误。

这里我们全面的展示一下

{
	var a = 1
}
console.log(a) // 块作用域, 打印1,var无视块作用域
{
	let a = 1
}
console.log(a) // 块作用域, 报错引用异常,let限制a在块作用域内访问,const也一样。

接下来我们看一下var带来的另一个隐患:变量提升
先看一下概念:
变量提升是指变量声明会被视为存在于其所在作用域的整个范围内。

var a
console.log(a) // undefined
console.log(a) // undefined
var a

第二种情况也输出了undefined,原因就是var进行了变量提升,将变量a的声明提升到了作用域的整个范围,而如果这里使用let则会提示我们需要先对变量初始化,使用const会优先提示需要先赋值

console.log(a) //  Uncaught ReferenceError: Cannot access 'a' before initialization
let a

这只是由JS作用域延伸出来的一些细节问题,让我更加清楚了var,let,const的区别。JavaScript还有更多看似简单却深层次的东西,值得我们深入的挖掘,而不是仅仅停留在应用表层。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值