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

相关推荐
真的很上进4 小时前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
噢,我明白了8 小时前
同源策略:为什么XMLHttpRequest不能跨域请求资源?
javascript·跨域
sanguine__8 小时前
APIs-day2
javascript·css·css3
关你西红柿子8 小时前
小程序app封装公用顶部筛选区uv-drop-down
前端·javascript·vue.js·小程序·uv
济南小草根8 小时前
把一个Vue项目的页面打包后再另一个项目中使用
前端·javascript·vue.js
小木_.9 小时前
【python 逆向分析某有道翻译】分析有道翻译公开的密文内容,webpack类型,全程扣代码,最后实现接口调用翻译,仅供学习参考
javascript·python·学习·webpack·分享·逆向分析
Aphasia3119 小时前
一次搞懂 JS 对象转换,从此告别类型错误!
javascript·面试
m0_748256569 小时前
Vue - axios的使用
前端·javascript·vue.js
m0_748256349 小时前
QWebChannel实现与JS的交互
java·javascript·交互
胡西风_foxww9 小时前
【es6复习笔记】函数参数的默认值(6)
javascript·笔记·es6·参数·函数·默认值