续聊浏览器中js的执行机制(二)

书接上回,今天我们继续细聊js的执行机制

var&let

按照惯例,我们先来看段代码:

ini 复制代码
var arr = []
for (var i = 0; i < 10; i++) {
  arr[i] = function() {
    console.log(i);
  }
}
for (var j = 0; j < arr.length; j++) {
  arr[j]()
}

问:这段代码的输出是啥?

我们直接看结果:

震惊,为啥是十个10?

我们来进行分析

for循环语句中,第一次循环是i=0;i<10成立,执行

scss 复制代码
  arr[i] = function() {
    console.log(i);
  }

然后i++,一直到i为9,i<10成立,执行

scss 复制代码
  arr[i] = function() {
    console.log(i);
  }

i++,此时i为10,i<10不成立,结束循环,开始调用arr[j]()

这个时候其实arr相当于

lua 复制代码
 arr = [
    function () {
        console.log(i);
    },
    function () {
        console.log(i);
    },
    function () {
        console.log(i);
    },//...从0-9有10次function()
]

当我们去看这个arr里的i时,function()里并没有定义它,只能去作用域链的下一级找i,也就是全局。这时候for循环已经在全局声明了10个函数体,且此时i为10,这样就输出了十个10.

那我们怎么把这段代码改成输出的是0-9呢?

我们思考一下,打印出十个10的根本原因就是function()作用域链的下一级是全局,如果我们让for循环形成作用域,function()就不算做声明在全局了,i就不会去全局里找。

让for形成作用域只用改一个小点,咱们把for里的var i = 0改为let i = 0即可, 这里们了解varlet的概念就很好理解 :

  • var:声明的变量具有函数作用域或全局作用域,也就是说同一个函数或全局范围内,var 声明的变量可以在任何地方被访问和修改。

  • let :能够提供块级作用域,即 let 声明的变量只能在包含它的代码块(例如 for 循环)内被访问和修改。

OK,知道了这个知识点之后我们运行验证一下:

结果符合

闭包

概念

在JavaScript中,根据词法作用域的规则,内部函数可以访问定义它的作用域中的变量,当内部函数被拿到外部函数之外调用时,即使外部函数已经执行完毕,但是内部函数对外部函数中的变量依然存在引用,那么这些被引用的变量会以一个集合的方式保存下来,这个集合就叫做闭包。当然在这个闭包中outer指针也依旧会被保留下来。

我个人会把闭包理解为飞机上的黑匣子,机毁盒存

作用

实现变量私有化

优缺点

优点

  1. 记忆功能:闭包可以记住它被创建时的信息,这在编程中非常有用,因为它可以帮助我们保存数据,而不需要每次都重新设置。
  2. 数据保护:闭包可以保护数据不被外部直接访问。

缺点

  1. 占用空间:闭包如果保存了太多数据,会占用计算机的内存。
  2. 意外行为:在编程中,如果不正确地使用闭包,可能会导致一些意外的行为。
  3. 速度变慢:如果程序中使用了很多闭包,可能会让程序运行得慢一些。

例子说明

看下面这段代码:

php 复制代码
function foo() {
  function bar() {
    var age = 18
    console.log(myName);
  }
  var myName = 'Tom'
  return bar
}
var myName = 'Jerry'
var fn = foo()
fn()

分析

第一步 创建全局的执行上下文:

第二步 运行到给fn赋值,发现调用了foo(),导致foo的执行上下文入栈,并调用foo(),foo()中的return导致fn变为一个函数体

js 复制代码

第三步 此时foo()执行完毕,出栈销毁。fn()调用,fn()此时为bar(),即bar的执行上下文入栈

bar()中有myName的输出,但是bar()里找不到myName的声明,只能去下一级作用域链里找,但是此时的foo()执行上下文对象已被销毁,按理来说应该会报错,但是结果却是正常运行:

结合概念

为什么,我们的分析哪里出问题了?foo函数的执行上下文不是被销毁了吗?

确实被销毁了,但是闭包 的存在,bar函数仍然可以访问到foo函数作用域中的变量。

foo函数执行时,它返回了bar函数的一个引用。这个返回的bar函数形成了一个闭包,它能够访问创建时的作用域链,即使foo函数的执行上下文已经结束。这是因为闭包保留了对创建时作用域的引用。

结语

以上就是本文全部内容,通过今天的学习,我们了解了JavaScript中varlet的区别,以及闭包的概念和应用。希望能对读者理解js的执行机制有所帮助,感谢阅读!

相关推荐
blzlh16 分钟前
手把手教你做网易云H5页面,进大厂后干的第一件事
前端·javascript·css
梅子酱~1 小时前
Vue 学习随笔系列十七 -- 表格样式修改
javascript·vue.js·学习
刺客-Andy1 小时前
React第四节 组件的三大属性之state
前端·javascript·react.js
黄毛火烧雪下1 小时前
React 表单Form 中的 useWatch
前端·javascript·react.js
爱健身的小刘同学2 小时前
钉钉免登录接口
前端·javascript·钉钉
命运之光2 小时前
生日主题的烟花特效HTML,CSS,JS
前端·javascript·css
Cshaosun2 小时前
js版本之ES5特性简述【String、Function、JSON、其他】(二)
前端·javascript·es
豆包MarsCode2 小时前
我用豆包MarsCode IDE 做了一个 CSS 权重小组件
开发语言·前端·javascript·css·ide·html
X 西安3 小时前
第十章JavaScript的应用
开发语言·javascript·ecmascript
想你的风吹到了瑞士3 小时前
变量提升&函数提升
前端·javascript·vue.js