JavaScript作用域和闭包问题

ES5的作用域

  1. 全局作用域

    全局作用域,就是全局都可以访问的,这个很好理解。

    例如在index.js中写入以下代码

    var a = "a";
    consoel.log(a);//a
    function (){
    	console.log(a);//a
    }
    fn();
    

    这个变量a在全局都可以访问到,它起效的范围是全局,这个就是全局作用域

  2. 函数内作用域

    函数内作用域,即只在本函数内起效

    function fn() {
        var b = "b";
        console.log(b);//b
    }
    fn();
    console.log(b);//b is not defined
    

    b只能在函数fn内被访问到

  3. 弊端

for (var i = 0; i < 10; i++) {

}
console.log(i);//10

直观的感受一下,感觉i是在for里声明的,为什么还会输出来呢?实际上因为ES5没有块级作用域,for循环里的i被泄露到了全局成了全局变量,

实际变成了这样的代码

var i;
for (i = 0; i < 10; i++) {

}
console.log(i);//10

下面这种情况更为常见

for (var i = 0; i < 10; i++) {
    setTimeout(()=>{
        console.log(i);//10
    },1000 * i);
}
console.log('over');

// 实际代码相当于
var i ;
// 第一次循环
{
	i = 0;
	setTimeout(()=>{
        console.log(i);//异步,JavaScript引擎还没有编译它,只标记了它叫i
   },1000*i);// i = 0;
}
// 第二次循环
{
	i = 1;
	setTimeout(()=>{
         console.log(i);//异步,JavaScript引擎还没有编译它,只标记了它叫i
   },1000*i); // i = 1;
}
// ...第十次循环
{
	i = 9;
	setTimeout(()=>{
         console.log(i);//异步,JavaScript引擎还没有编译它,只标记了它叫i
   },1000*i);// i = 9
}
// 结束循环还会执行依次 i++
i ++; // 此时i的值是10

setTimeout中的代码在0 1 2 … 9秒后依次执行,这个时候i已经变成10了,当执行完console.log(‘over’)之后,会去执行异步任务里的console.log(i),而i是全局变量,值为10,所以每次执行setTimeout对应的代码会输出10。怎么修复这个问题呢

for (var i = 0; i < 10; i++) {
	function inner(innerParam){
		console.log(innerParam)
		setTimeout(()=>{
        console.log(innerParam);//10
	    },1000*innerParam);
	}
	inner(i);
}
// 实际代码相当于
var i ;
// 第一次循环
{
	i = 0;
	function inner(innerParam){// innerParam = 0;
		console.log(innerParam)// innerParam = 0;
		setTimeout(()=>{
        console.log(innerParam);//异步,JavaScript引擎还没有编译它,只标记了它叫innerParam,它从参数里过来 
	    },1000*innerParam); // innerParam = 0;
	}
	inner(i); // 参数 = 0;
	
}
// 第二次循环
{
	i = 1;
	function inner(innerParam){// innerParam = 1;
		console.log(innerParam)// innerParam = 1;
		setTimeout(()=>{
        console.log(innerParam);//异步,JavaScript引擎还没有编译它,只标记了它叫innerParam,它从参数里过来 
	    },1000*innerParam); // innerParam = 1;
	}
	inner(i); // 参数 = 1;
}
// ...第十次循环
{
	i = 9;
	function inner(innerParam){// innerParam = 9;
		console.log(innerParam)// innerParam = 9;
		setTimeout(()=>{
        console.log(innerParam);//异步,JavaScript引擎还没有编译它,只标记了它叫innerParam,它从参数里过来 
	    },1000*innerParam); // innerParam = 9;
	}
	inner(i); // 参数 = 9;
}
// 结束循环还会执行依次 i++
i ++; // 此时i的值是10

全局变量的i值依然是10,但是inner中的参数innerParam依次是0 1 2 …9,当执行到console.log(innerParam);时,会优先现在本函数作用域内查找innerParam变量或者参数,结果他依次找到就是0 1 2 … 9,因此输出结果也是0 1 2 … 9,最后优化一下把,把ineer的声明和调用合起来,改成自执行函数

for (var i = 0; i < 10; i++) {
	((innerParam){
		console.log(innerParam)
		setTimeout(()=>{
        console.log(innerParam);//10
	    },1000*innerParam);
	})(i);
}

ES6的块及作用域

ES6新增了块级作用域。可以简单的理解为一个大括号就是一块,在块内声明的变量只能在块内被访问到。这个时候就需要我们用let或者const来声明变量了。使用let、const和使用var有很大的区别,这里暂时不讨论。

先来看一下怎么使用的

function f1() {
  let n = 5;
  if (true) {
    let n = 10;
    console.log(n); // 10 内层的n
  }
  console.log(n); // 5 当前层的n
}

这里输出n可以看到都是我们期望的值,当然子块可以父块,父块是访问不到子块的。

再看另一个问题

for (let i = 0; i < 10; i++) {
    setTimeout(()=>{
        console.log(i);//0,1,2,3,4,5,6,7,8,9
    },1000*i);
}
console.log('over');

使用let时,由于块级作用域的存在,代码相当于

// 实际代码相当于
// 第一次循环
{
	let i = 0;
	setTimeout(()=>{
        console.log(i);//异步,JavaScript引擎还没有编译它,只标记了它叫i
	},1000*i); // i= 0;
}
// 第二次循环
{
	let i = 1;
	setTimeout(()=>{
        console.log(i);//异步,JavaScript引擎还没有编译它,只标记了它叫i
	},1000*i); // i= 1;
}
// ...第十次循环
{
	let i = 9;
	setTimeout(()=>{
        console.log(i);//异步,JavaScript引擎还没有编译它,只标记了它叫i
	},1000*i); // i= 9;
}
// 结束循环还会执行依次 i++
{
	let i = i ++;// 此时i的值是10
} 

当执行到console.log(i);时,会优先在本块级作用域内找变量或者参数,结果依次找到就是0 1 2 … 9,因此输出结果也是0 1 2 … 9

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值