续聊浏览器中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的执行机制有所帮助,感谢阅读!

相关推荐
喝旺仔la3 小时前
使用vue创建项目
前端·javascript·vue.js
心.c3 小时前
植物大战僵尸【源代码分享+核心思路讲解】
前端·javascript·css·数据结构·游戏·html
喝旺仔la3 小时前
Element Plus中button按钮相关大全
前端·javascript·vue.js
柒@宝儿姐4 小时前
Git的下载与安装
前端·javascript·vue.js·git·elementui·visual studio
敲敲敲敲暴你脑袋4 小时前
【cesium】绘制贴地线面和自定义Primitive
javascript·webgl·cesium
曈欣4 小时前
vue 中属性值上变量和字符串怎么拼接
前端·javascript·vue.js
QGC二次开发5 小时前
Vue3:v-model实现组件通信
前端·javascript·vue.js·前端框架·vue·html
小鼠米奇6 小时前
详解Ajax与axios的区别
前端·javascript·ajax
Bunury7 小时前
Vue3新组件transition(动画过渡)
前端·javascript·vue.js
zero.cyx7 小时前
JS函数部分
开发语言·前端·javascript