js作用域&let的for循环

参考:
let for循环
ES6中let在for循环中的表现
JavaScript小知识:for 循环中的 let 声明
js中的执行上下文
JS入门难点解析8-作用域,作用域链,执行上下文,执行上下文栈等分析
JS入门难点解析7-this
JS中的bind()方法
小火柴的前端小册子

// 在看下面的js代码之前,先回顾一下js函数
// 函数存在的目的:就是为了封装一段代码去完成一项功能
// 那么,定义完函数之后,这个函数需要被调用,才会被执行,那么在执行的过程中很自然的需要访问变量,
// 那么, 能被这个函数访问的变量应当在这个函数能访问到的作用域当中,当然,这些能被这个函数访问的变量,
//		同实也可能被其它函数所访问到,也就是2个函数有能会访问到同一个变量

// 那么,如果我在全局作用域中,定义两个变量a和变量b,
// 然后我需要定义一个函数求a和b,
// 那么我定义的这个函数就是来对a和b求和,并打印结果的
var a = 10;
var b = 20;
function add(){
	var sum = a + b;
	console.log(sum)
}
// 那我为什么要定义这个函数呢?我就是想执行多遍
add()
add()
add()
// 但是,我并没有把a和b定义为add函数的形参,因为我乐意
// 因为函数它并没有规定函数内部所使用到的所有的参数都必须定义在函数内部才能被使用,所以我这样做是合理的
// 并且,js函数就是一个对象(可以被调用),它用来封装一段(可多次调用)的逻辑代码,
// 那我定义这个add函数就是用来多次求a和b的值,也是没有毛病的。
// 那么我这个函数能访问到a和b也就是合理的(从它设计的合理性上考虑,这一段主要是为了说服我自己)
// 所以,从上面,我们就会发现,我们的add函数能访问到函数外面的变量a和变量b了
// --------------------

var a = 10;
function incr(){
	a++
	console.log(a)
}
function showA(){
	console.log(a)
}
show()
incr()
show()
incr()
show()
// 定义2个函数,它们都访问的是变量a, 当一个函数改变这个变量的值时,另一个函数由于访问的是同一个变量,所以得到的是改变后的值
// -------------------

var a = 10;
function incr(){
	var a = 20 // 在函数内部,可能需要定义一些个变量,用于内部使用,
			   // 但它有可能与这个函数能访问到的作用域的其它变量存在冲突
			   // 将会忽略掉其它作用域中的变量(作用域有隔离变量的作用)
	a++
	console.log(a)
}
function showA(){
	console.log(a)
}
showA()
incr()
showA()
incr()
showA()

// ----------------------------------

// 在fn函数中定义了一个变量b,和f1函数、f2函数、f3函数,这3个函数都是访问的变量b。
// 当函数fn每次(下面故意执行了2次)执行时,都会自动创建一个属于当前fn函数的作用域,在这个作用域中定义了b、f1、f2、f3这些变量和函数,
// 并且把这些函数通过fn函数的返回值暴露出去了,这样外界就能凭借暴露出去的函数操作这个变量b了, 
// 并且这个变量b只属于当前的这个作用域
//(当fn又一次执行时,又会创建一个新的作用域,在这个新的作用域里面又有一个变量b,并且暴露出去的函数又是一组新的函数,这些新的函数访问的又是新的作用域中的变量b)
// 并且fn函数中的作用域内部,能访问到外部的这个全局作用域中定义的变量a(上面说了)

// 在fn函数的内部又定义了函数f1、f2、f3,按照上面的说法(其实就是把前面的说法换成了fn内部了),f1、f2、f3能访问到fn中定义的变量b是自然而然的,
//(只不过注意此时这些函数访问的这个变量b是处于这次fn函数执行时所创建的作用域中的。)
// 那么fn函数中定义的f1函数能否访问fn函数能访问的外部这个全局作用域么?或者说,假设多套几层进去,它能不能访问到外部的外部中的变量呢?
// 我觉得必须要能访问,因为对于fn来说,fn函数是外部这个全局作用域中用来定义功能的代码,
// fn函数能访问外部全局作用域中的变量是理所应当的(不然就像你要我这个函数帮你改东西,你又不把东西给我改,我给你改个毛线)
// 然后,在fn函数里面,我为了改这个东西,我可能又需要定义一些函数来封装功能代码,这些函数也有可能需要直接访问到fn函数外部的变量吧,
//      或者说,fn函数能访问呢到外部的变量,那不管fn函数里面怎么玩,它里面都应该能访问到外部的变量,
//      或者说,外面提供的变量就相当于是一个基础的环境,环境中有定义的这些变量供fn函数内部去调用

// 那这样的话,就可以理解为: 
//		外部的这个全局作用域就是js代码执行入口函数所创建的一个作用域,
// 		在全局作用域里面定义的函数自然就能访问到外部这个全局作用域中的变量(并且是同一个变量),
//		只不过这个入口函数只会执行一次,所以这个全局作用域只有一个。
//		而我们定义的函数,可以执行多次,每次执行都会创建一个新的作用域,并在这个新的作用域中创建新的变量,
//		并且我们定义的函数在执行时,创建了新的作用域后,然后在这个作用域中创建了变量,又定义了函数,
//		那么此时所定义的这些个函数,所能访问的作用域,在此时就已经确定下来了!!!
//      当函数再一次执行时,又会创建一个新的作用域,然后又在这个新的作用域中创建了变量,又定义了函数,
//		这些又新定义的函数所访问的作用域,又被确定下来了。
//		如果函数内部的函数的内部又定义了函数,那么函数内部的函数内部定义的这个函数是能够访问外部函数这个函数作用域中的变量,以及外部这个函数的外部函数的作用域中的变量,一直到全局作用域
var a
function fn(){
    var b = 1
    console.log(a)
    function f1() {
        b++
        console.log(a)
        console.log('f1',b);
    }
    function f2() {
        b = b+10
        console.log('f2',b);
    }
    function f3() {
        console.log('f3',b);
        return b
    }
    return {f1,f2,f3}
}
let {f1,f2,f3} = fn()
f3()
f1()
f2()

f3()
f1()
f2()

f3()

let ff = fn()
ff.f3()
ff.f1()
ff.f2()

ff.f3()
ff.f1()
ff.f2()

ff.f3()

console.log(f1 == ff.f1)




<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
  
    <button>测试1</button>
    <button>测试2</button>
    <button>测试3</button>
    
    <script type="text/javascript">
    
      var btns = document.getElementsByTagName("button");
      
      for (let i = 0; i < btns.length; i++) {  // 这个括号里面也有一个隐藏的作用域
        btns[i].onclick = function () {
          console.log("第" + (i + 1) + "个");
          (function(){
            i++
          })()
        };
      }

	 console.log(i) // 在let出现之前,我们在for循环中是用var定义的迭代器变量会溢出循环体;
	 				// 当使用let声明时,这个问题就不存在了,因为let声明的迭代器变量作用域仅限于for循环体。
    </script>

  </body>
</html>

for循环表达式中使用let声明表达式变量
因为let有自己的作用域块,所以在for循环表达式中使用let其实就等价于在代码块中使用let,也就是说

for( let i = 0; i< 5; i++) 这句话的圆括号之间,有一个隐藏的作用域
for( let i = 0; i< 5; i++) { 循环体 } 在每次执行循环体之前,JS 引擎会把 i 在循环体的上下文中重新声明及初始化一次。
var liList = document.querySelectorAll('li') // 共5个li
for(let i = 0;i<liList.length;i++){
 liList[i].onclick = function(){
    //这里的i是循环结束的i,因为监听事件是异步
    console.log(i)//0 1 2 3 4
  }
}
//上面代码其实就等价于
for (let i = 0;i<liList.length;i++){
 let i = 隐藏作用域中的i(表达式中的i)
 liList[i].onclick = function(){
    console.log(i)
 }
}
//你也可以在代码块中使用另一个let变量接受每一个i
for(let i = 0;i<liList.length;i++){
 //这里能很直观的看出j有自己的作用域
 let j = i;
 liList[j].onclick = function(){
    console.log(j)
 }
}
//你也可以当成是声明了五个let块级作用域
{let i = 0}
{let i = 1}
{let i = 2}
{let i = 3}
{let i = 4}
因为let在代码块中都有自己的作用域,所以在for循环中的表达式中使用let它的每一个值都会单独存在一个独立的作用域中不会被覆盖掉。

for(let i = 0; i<5;i++){
  setTimeout(()=>console.log(i),0)//0 1 2 3 4
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值