全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

图解Javascript——作用域、作用域链、闭包

什么是作用域?

作用域是一种规则,在代码编译阶段就确定了,规定了变量与函数的可被访问的范围。全局变量拥有全局作用域,局部变量则拥有局部作用域。 js是一种没有块级作用域的语言(包括if、for等语句的花括号代码块或者单独的花括号代码块都不能形成一个局部作用域),所以js的局部作用域的形成有且只有函数的花括号内定义的代码块形成的,既函数作用域。

什么是作用域链?

作用域链是作用域规则的实现,通过作用域链的实现,变量在它的作用域内可被访问,函数在它的作用域内可被调用。

作用域链是一个只能单向访问的链表,这个链表上的每个节点就是执行上下文的变量对象(代码执行时就是活动对象),单向链表的头部(可被第一个访问的节点)始终都是当前正在被调用执行的函数的变量对象(活动对象),尾部始终是全局活动对象。

作用域链的形成?

我们从一段代码的执行来看作用域链的形成过程。

function fun01 () {
 console.log('i am fun01...');
 fun02();
}
function fun02 () {
 console.log('i am fun02...');
}
fun01();

数据访问流程

如上图,当程序访问一个变量时,按照作用域链的单向访问特性,首先在头节点的AO中查找,没有则到下一节点的AO查找,最多查找到尾节点(global AO)。在这个过程中找到了就找到了,没找到就报错undefined。

延长作用域链

从上面作用域链的形成可以看出链上的每个节点是在函数被调用执行是向链头unshift进当前函数的AO,而节点的形成还有一种方式就是“延长作用域链”,既在作用域链的头部插入一个我们想要的对象作用域。延长作用域链有两种方式:

1.with语句

function fun01 () {
 with (document) {
  console.log('I am fun01 and I am in document scope...')
 }
}
fun01();

2.try-catch语句的catch块

function fun01 () {
 try {
  console.log('Some exceptions will happen...')
 } catch (e) {
  console.log(e)
 }
}
fun01();

ps:个人感觉with语句使用需求不多,try-catch的使用也是看需求的。个人对这两种使用不多,但是在进行这部分整理过程中萌发了一点点在作用域链层面的不成熟的性能优化小建议。

由作用域链引发的关于性能优化的一点不成熟的小建议

1.减少变量的作用域链的访问节点

这里我们自定义一个名次叫做“查找距离”,表示程序访问到一个非undefined变量在作用域链中经过的节点数。因为如果在当前节点没有找到变量,就跳到下一个节点查找,还要进行判断下一个节点中是否存在被查找变量。“查找距离”越长,要做的“跳”动作和“判断”动作也就越多,资源开销就越大,从而影响性能。这种性能带来的差距可能少数的几次变量查找操作不会带来太多性能问题,但如果是多次进行变量查找,性能对比则比较明显了。

(function(){
 console.time()
 var find = 1      //这个find变量需要在4个作用域链节点进行查找
 function fun () {
  function funn () {
   var funnv = 1;
   var funnvv = 2;
   function funnn () {
    var i = 0
    while(i <= 100000000){
     if(find){
      i++
     }
    }
   }
   funnn()
  }
  funn()
 }
 fun()
 console.timeEnd()
})()

(function(){
 console.time()
 function fun () {
  function funn () {
   var funnv = 1;
   var funnvv = 2;
   function funnn () {
    var i = 0
    var find = 1      //这个find变量只在当前节点进行查找
    while(i <= 100000000){
     if(find){
      i++
     }
    }
   }
   funnn()
  }
  funn()
 }
 fun()
 console.timeEnd()
})()

在mac pro的chrome浏览器下做实验,进行1亿次查找运算。

实验结果:前者运行5次平均耗时85.599ms,后者运行5次平均耗时63.127ms。

2.避免作用域链内节点AO上过多的变量定义

过多的变量定义造成性能问题的原因主要是查找变量过程中的“判断”操作开销较大。我们使用with来进行性能对比。

(function(){
 console.time()
 function fun () {
  function funn () {
   var funnv = 1;
   var funnvv = 2;
   function funnn () {
    var i = 0
    var find = 10
    with (document) {
     while(i <= 1000000){
      if(find){
       i++
      }
     }
    }
   }
   funnn()
  }
  funn()
 }
 fun()
 console.timeEnd()
})()

在mac pro的chrome浏览器下做实验,进行100万次查找运算,借助with使用document进行的延长作用域链,因为document下的变量属性比较多,可以测试在多变量作用域链节点下进行查找的性能差异。

实验结果:5次平均耗时558.802ms,而如果删掉with和document,5次平均耗时0.956ms。

当然,这两个实验是在我们假设的极端环境下进行的,结果仅供参考!

关于闭包

1.什么是闭包?

函数对象可以通过作用域链相互关联起来,函数体内的数据(变量和函数声明)都可以保存在函数作用域内,这种特性在计算机科学文献中被称为“闭包”。既函数体内的数据被隐藏于作用于链内,看起来像是函数将数据“包裹”了起来。从技术角度来说,js的函数都是闭包:函数都是对象,都关联到作用域链,函数内数据都被保存在函数作用域内。

2.闭包的几种实现方式

实现方式就是函数A在函数B的内部进行定义了,并且当函数A在执行时,访问了函数B内部的变量对象,那么B就是一个闭包。如下:

如上两图所示,是在chrome浏览器下查看闭包的方法。两种方式的共同点是都有一个外部函数outerFun(),都在外部函数内定义了内部函数innerFun(),内部函数都访问了外部函数的数据。不同的是,第一种方式的innerFun()是在outerFun()内被调用的,既声明和被调用均在同一个执行上下文内。而第二种方式的innerFun()则是在outerFun()外被调用的,既声明和被调用不在同一个执行上下文。第二种方式恰好是js使用闭包常用的特性所在:通过闭包的这种特性,可以在其他执行上下文内访问函数内部数据。

我们更常用的一种方式则是这样的:

//闭包实例
function outerFun () {
 var outerV1 = 10
 function outerF1 () {
  console.log('I am outerF1...')
 }
 function innerFun () {
  var innerV1 = outerV1
  outerF1()
 }
 return innerFun //return回innerFun()内部函数
}
var fn = outerFun()  //接到return回的innerFun()函数
fn()     //执行接到的内部函数innerFun()

此时它的作用域链是这样的:

3.闭包的好处及使用场景

js的垃圾回收机制可以粗略的概括为:如果当前执行上下文执行完毕,且上下文内的数据没有其他引用,则执行上下文pop出call stack,其内数据等待被垃圾回收。而当我们在其他执行上下文通过闭包对执行完的上下文内数据仍然进行引用时,那么被引用的数据则不会被垃圾回收。就像上面代码中的outerV1,放我们在全局上下文通过调用innerFun()仍然访问引用outerV1时,那么outerFun执行完毕后,outerV1也不会被垃圾回收,而是保存在内存中。另外,outerV1看起来像不像一个outerFun的私有内部变量呢?除了innerFun()外,我们无法随意访问outerV1。所以,综上所述,这样闭包的使用情景可以总结为:

(1)进行变量持久化。

(2)使函数对象内有更好的封装性,内部数据私有化。

进行变量持久化方面举个栗子:

我们假设一个需求时写一个函数进行类似id自增或者计算函数被调用的功能,普通青年这样写:

 var count = 0
 function countFun () {
  return count++
 }

这样写固然实现了功能,但是count被暴露在外,可能被其他代码篡改。这个时候闭包青年就会这样写:

function countFun () {
 var count = 0
 return function(){
  return count++
 }
}
var a = countFun()
a()

这样count就不会被不小心篡改了,函数调用一次就count加一次1。而如果结合“函数每次被调用都会创建一个新的执行上下文”,这种count的安全性还有如下体现:

function countFun () {
 var count = 0
 return {
  count: function () {
   count++
  },
  reset: function () {
   count = 0
  },
  printCount: function () {
   console.log(count)
  }
 }
}
var a = countFun()
var b = countFun()
a.count()
a.count()
b.count()
b.reset()
a.printCount()  //打印:2 因为a.count()被调用了两次
b.printCount()  //打印出:0 因为调用了b.reset()

以上便是闭包提供的变量持久化和封装性的体现。

4.闭包的注意事项

由于闭包中的变量不会像其他正常变量那种被垃圾回收,而是一直存在内存中,所以大量使用闭包可能会造成性能问题。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持!


# js作用域和闭包  # js  # 闭包  # 作用域  # 作用域链  # Javascript的作用域、作用域链以及闭包详解  # 详解JavaScript作用域、作用域链和闭包的用法  # JavaScript闭包与作用域链实例分析  # 深入理解Javascript中的作用域链和闭包  # JS闭包、作用域链、垃圾回收、内存泄露相关知识小结  # 浅析JavaScript作用域链、执行上下文与闭包  # javascript从作用域链谈闭包  # 深入Javascript函数、递归与闭包(执行环境、变量对象与作用域链)使用详解  # JavaScript中的作用域链和闭包  # JavaScript深入理解作用域链与闭包详情  # 是在  # 都是  # 的花  # 是一种  # 过程中  # 则是  # 不多  # 链表  # 第二种  # 不成熟  # 的是  # 体内  # 是一个  # 就会  # 都有  # 太多  # 就像  # 在这个  # 都在  # 第一个 


相关文章: ,巨量百应是干嘛的?  详解ASP.NET 生成二维码实例(采用ThoughtWorks.QRCode和QrCode.Net两种方式)  代刷网站制作软件,别人代刷火车票靠谱吗?  无锡营销型网站制作公司,无锡网选车牌流程?  微课制作网站有哪些,微课网怎么进?  如何彻底删除建站之星生成的Banner?  南京做网站制作公司,南京哈发网络有限公司,公司怎么样,做网页美工DIV+CSS待遇怎么样?  在线制作视频的网站有哪些,电脑如何制作视频短片?  C++如何使用std::optional?(处理可选值)  建站上传速度慢?如何优化加速网站加载效率?  如何高效利用200m空间完成建站?  建站之星备案是否影响网站上线时间?  山东云建站价格为何差异显著?  魔毅自助建站系统:模板定制与SEO优化一键生成指南  一键制作网站软件下载安装,一键自动采集网页文档制作步骤?  济南网站建设制作公司,室内设计网站一般都有哪些功能?  如何配置WinSCP新建站点的密钥验证步骤?  javascript中对象的定义、使用以及对象和原型链操作小结  黑客如何利用漏洞与弱口令入侵网站服务器?  官网自助建站平台指南:在线制作、快速建站与模板选择全解析  视频网站app制作软件,有什么好的视频聊天网站或者软件?  如何快速上传建站程序避免常见错误?  网站制作大概要多少钱一个,做一个平台网站大概多少钱?  网站制作公司广州有几家,广州尚艺美发学校网站是多少?  微网站制作教程,我微信里的网站怎么才能复制到浏览器里?  建站主机选择指南:服务器配置与SEO优化实战技巧  建站ABC备案流程中有哪些关键注意事项?  大连企业网站制作公司,大连2025企业社保缴费网上缴费流程?  如何在景安云服务器上绑定域名并配置虚拟主机?  重庆市网站制作公司,重庆招聘网站哪个好?  西安制作网站公司有哪些,西安货运司机用的最多的app或者网站是什么?  如何在Windows服务器上快速搭建网站?  安云自助建站系统如何快速提升SEO排名?  高端网站建设与定制开发一站式解决方案 中企动力  ,网页ppt怎么弄成自己的ppt?  网站制作价目表怎么做,珍爱网婚介费用多少?  建站主机如何选?高性价比方案全解析  建站VPS能否同时实现高效与安全翻墙?  免费制作海报的网站,哪位做平面的朋友告诉我用什么软件做海报比较好?ps还是cd还是ai这几个软件我都会些我是做网页的?  c++如何打印函数堆栈信息_c++ backtrace函数与符号名解析【方法】  制作宣传网站的软件,小红书可以宣传网站吗?  免费ppt制作网站,有没有值得推荐的免费PPT网站?  网站建设设计制作营销公司南阳,如何策划设计和建设网站?  建站之星安全性能如何?防护体系能否抵御黑客入侵?  seo网站制作优化,网站SEO优化步骤有哪些?  高配服务器限时抢购:企业级配置与回收服务一站式优惠方案  如何确保FTP站点访问权限与数据传输安全?  广州网站制作的公司,现在专门做网站的公司有没有哪几家是比较好的,性价比高,模板也多的?  盘锦网站制作公司,盘锦大洼有多少5G网站?  怎么制作网站设计模板图片,有电商商品详情页面的免费模板素材网站推荐吗? 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。