ES5的作用域
-
全局作用域
全局作用域,就是全局都可以访问的,这个很好理解。
例如在index.js中写入以下代码
var a = "a"; consoel.log(a);//a function (){ console.log(a);//a } fn();
这个变量a在全局都可以访问到,它起效的范围是全局,这个就是全局作用域
-
函数内作用域
函数内作用域,即只在本函数内起效
function fn() { var b = "b"; console.log(b);//b } fn(); console.log(b);//b is not defined
b只能在函数fn内被访问到
-
弊端
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