欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

圖解Javascript——作用域、作用域鏈、閉包

 更新時(shí)間:2017年03月21日 09:14:22   作者:ive  
本文主要介紹了圖解Javascript——作用域、作用域鏈、閉包等知識(shí)。具有很好的參考價(jià)值。下面跟著小編一起來看下吧

什么是作用域?

作用域是一種規(guī)則,在代碼編譯階段就確定了,規(guī)定了變量與函數(shù)的可被訪問的范圍。全局變量擁有全局作用域,局部變量則擁有局部作用域。 js是一種沒有塊級(jí)作用域的語言(包括if、for等語句的花括號(hào)代碼塊或者單獨(dú)的花括號(hào)代碼塊都不能形成一個(gè)局部作用域),所以js的局部作用域的形成有且只有函數(shù)的花括號(hào)內(nèi)定義的代碼塊形成的,既函數(shù)作用域。

什么是作用域鏈?

作用域鏈?zhǔn)亲饔糜蛞?guī)則的實(shí)現(xiàn),通過作用域鏈的實(shí)現(xiàn),變量在它的作用域內(nèi)可被訪問,函數(shù)在它的作用域內(nèi)可被調(diào)用。

作用域鏈?zhǔn)且粋€(gè)只能單向訪問的鏈表,這個(gè)鏈表上的每個(gè)節(jié)點(diǎn)就是執(zhí)行上下文的變量對(duì)象(代碼執(zhí)行時(shí)就是活動(dòng)對(duì)象),單向鏈表的頭部(可被第一個(gè)訪問的節(jié)點(diǎn))始終都是當(dāng)前正在被調(diào)用執(zhí)行的函數(shù)的變量對(duì)象(活動(dòng)對(duì)象),尾部始終是全局活動(dòng)對(duì)象。

作用域鏈的形成?

我們從一段代碼的執(zhí)行來看作用域鏈的形成過程。

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

數(shù)據(jù)訪問流程

如上圖,當(dāng)程序訪問一個(gè)變量時(shí),按照作用域鏈的單向訪問特性,首先在頭節(jié)點(diǎn)的AO中查找,沒有則到下一節(jié)點(diǎn)的AO查找,最多查找到尾節(jié)點(diǎn)(global AO)。在這個(gè)過程中找到了就找到了,沒找到就報(bào)錯(cuò)undefined。

延長(zhǎng)作用域鏈

從上面作用域鏈的形成可以看出鏈上的每個(gè)節(jié)點(diǎn)是在函數(shù)被調(diào)用執(zhí)行是向鏈頭unshift進(jìn)當(dāng)前函數(shù)的AO,而節(jié)點(diǎn)的形成還有一種方式就是“延長(zhǎng)作用域鏈”,既在作用域鏈的頭部插入一個(gè)我們想要的對(duì)象作用域。延長(zhǎng)作用域鏈有兩種方式:

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:個(gè)人感覺with語句使用需求不多,try-catch的使用也是看需求的。個(gè)人對(duì)這兩種使用不多,但是在進(jìn)行這部分整理過程中萌發(fā)了一點(diǎn)點(diǎn)在作用域鏈層面的不成熟的性能優(yōu)化小建議。

由作用域鏈引發(fā)的關(guān)于性能優(yōu)化的一點(diǎn)不成熟的小建議

1.減少變量的作用域鏈的訪問節(jié)點(diǎn)

這里我們自定義一個(gè)名次叫做“查找距離”,表示程序訪問到一個(gè)非undefined變量在作用域鏈中經(jīng)過的節(jié)點(diǎn)數(shù)。因?yàn)槿绻诋?dāng)前節(jié)點(diǎn)沒有找到變量,就跳到下一個(gè)節(jié)點(diǎn)查找,還要進(jìn)行判斷下一個(gè)節(jié)點(diǎn)中是否存在被查找變量?!安檎揖嚯x”越長(zhǎng),要做的“跳”動(dòng)作和“判斷”動(dòng)作也就越多,資源開銷就越大,從而影響性能。這種性能帶來的差距可能少數(shù)的幾次變量查找操作不會(huì)帶來太多性能問題,但如果是多次進(jìn)行變量查找,性能對(duì)比則比較明顯了。

(function(){
 console.time()
 var find = 1      //這個(gè)find變量需要在4個(gè)作用域鏈節(jié)點(diǎn)進(jìn)行查找
 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      //這個(gè)find變量只在當(dāng)前節(jié)點(diǎn)進(jìn)行查找
    while(i <= 100000000){
     if(find){
      i++
     }
    }
   }
   funnn()
  }
  funn()
 }
 fun()
 console.timeEnd()
})()

在mac pro的chrome瀏覽器下做實(shí)驗(yàn),進(jìn)行1億次查找運(yùn)算。

實(shí)驗(yàn)結(jié)果:前者運(yùn)行5次平均耗時(shí)85.599ms,后者運(yùn)行5次平均耗時(shí)63.127ms。

2.避免作用域鏈內(nèi)節(jié)點(diǎn)AO上過多的變量定義

過多的變量定義造成性能問題的原因主要是查找變量過程中的“判斷”操作開銷較大。我們使用with來進(jìn)行性能對(duì)比。

(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瀏覽器下做實(shí)驗(yàn),進(jìn)行100萬次查找運(yùn)算,借助with使用document進(jìn)行的延長(zhǎng)作用域鏈,因?yàn)閐ocument下的變量屬性比較多,可以測(cè)試在多變量作用域鏈節(jié)點(diǎn)下進(jìn)行查找的性能差異。

實(shí)驗(yàn)結(jié)果:5次平均耗時(shí)558.802ms,而如果刪掉with和document,5次平均耗時(shí)0.956ms。

當(dāng)然,這兩個(gè)實(shí)驗(yàn)是在我們假設(shè)的極端環(huán)境下進(jìn)行的,結(jié)果僅供參考!

關(guān)于閉包

1.什么是閉包?

函數(shù)對(duì)象可以通過作用域鏈相互關(guān)聯(lián)起來,函數(shù)體內(nèi)的數(shù)據(jù)(變量和函數(shù)聲明)都可以保存在函數(shù)作用域內(nèi),這種特性在計(jì)算機(jī)科學(xué)文獻(xiàn)中被稱為“閉包”。既函數(shù)體內(nèi)的數(shù)據(jù)被隱藏于作用于鏈內(nèi),看起來像是函數(shù)將數(shù)據(jù)“包裹”了起來。從技術(shù)角度來說,js的函數(shù)都是閉包:函數(shù)都是對(duì)象,都關(guān)聯(lián)到作用域鏈,函數(shù)內(nèi)數(shù)據(jù)都被保存在函數(shù)作用域內(nèi)。

2.閉包的幾種實(shí)現(xiàn)方式

實(shí)現(xiàn)方式就是函數(shù)A在函數(shù)B的內(nèi)部進(jìn)行定義了,并且當(dāng)函數(shù)A在執(zhí)行時(shí),訪問了函數(shù)B內(nèi)部的變量對(duì)象,那么B就是一個(gè)閉包。如下:

如上兩圖所示,是在chrome瀏覽器下查看閉包的方法。兩種方式的共同點(diǎn)是都有一個(gè)外部函數(shù)outerFun(),都在外部函數(shù)內(nèi)定義了內(nèi)部函數(shù)innerFun(),內(nèi)部函數(shù)都訪問了外部函數(shù)的數(shù)據(jù)。不同的是,第一種方式的innerFun()是在outerFun()內(nèi)被調(diào)用的,既聲明和被調(diào)用均在同一個(gè)執(zhí)行上下文內(nèi)。而第二種方式的innerFun()則是在outerFun()外被調(diào)用的,既聲明和被調(diào)用不在同一個(gè)執(zhí)行上下文。第二種方式恰好是js使用閉包常用的特性所在:通過閉包的這種特性,可以在其他執(zhí)行上下文內(nèi)訪問函數(shù)內(nèi)部數(shù)據(jù)。

我們更常用的一種方式則是這樣的:

//閉包實(shí)例
function outerFun () {
 var outerV1 = 10
 function outerF1 () {
  console.log('I am outerF1...')
 }
 function innerFun () {
  var innerV1 = outerV1
  outerF1()
 }
 return innerFun //return回innerFun()內(nèi)部函數(shù)
}
var fn = outerFun()  //接到return回的innerFun()函數(shù)
fn()     //執(zhí)行接到的內(nèi)部函數(shù)innerFun()

此時(shí)它的作用域鏈?zhǔn)沁@樣的:

3.閉包的好處及使用場(chǎng)景

js的垃圾回收機(jī)制可以粗略的概括為:如果當(dāng)前執(zhí)行上下文執(zhí)行完畢,且上下文內(nèi)的數(shù)據(jù)沒有其他引用,則執(zhí)行上下文pop出call stack,其內(nèi)數(shù)據(jù)等待被垃圾回收。而當(dāng)我們?cè)谄渌麍?zhí)行上下文通過閉包對(duì)執(zhí)行完的上下文內(nèi)數(shù)據(jù)仍然進(jìn)行引用時(shí),那么被引用的數(shù)據(jù)則不會(huì)被垃圾回收。就像上面代碼中的outerV1,放我們?cè)谌稚舷挛耐ㄟ^調(diào)用innerFun()仍然訪問引用outerV1時(shí),那么outerFun執(zhí)行完畢后,outerV1也不會(huì)被垃圾回收,而是保存在內(nèi)存中。另外,outerV1看起來像不像一個(gè)outerFun的私有內(nèi)部變量呢?除了innerFun()外,我們無法隨意訪問outerV1。所以,綜上所述,這樣閉包的使用情景可以總結(jié)為:

(1)進(jìn)行變量持久化。

(2)使函數(shù)對(duì)象內(nèi)有更好的封裝性,內(nèi)部數(shù)據(jù)私有化。

進(jìn)行變量持久化方面舉個(gè)栗子:

我們假設(shè)一個(gè)需求時(shí)寫一個(gè)函數(shù)進(jìn)行類似id自增或者計(jì)算函數(shù)被調(diào)用的功能,普通青年這樣寫:

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

這樣寫固然實(shí)現(xiàn)了功能,但是count被暴露在外,可能被其他代碼篡改。這個(gè)時(shí)候閉包青年就會(huì)這樣寫:

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

這樣count就不會(huì)被不小心篡改了,函數(shù)調(diào)用一次就count加一次1。而如果結(jié)合“函數(shù)每次被調(diào)用都會(huì)創(chuàng)建一個(gè)新的執(zhí)行上下文”,這種count的安全性還有如下體現(xiàn):

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()  //打?。? 因?yàn)閍.count()被調(diào)用了兩次
b.printCount()  //打印出:0 因?yàn)檎{(diào)用了b.reset()

以上便是閉包提供的變量持久化和封裝性的體現(xiàn)。

4.閉包的注意事項(xiàng)

由于閉包中的變量不會(huì)像其他正常變量那種被垃圾回收,而是一直存在內(nèi)存中,所以大量使用閉包可能會(huì)造成性能問題。

以上就是本文的全部?jī)?nèi)容,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來一定的幫助,同時(shí)也希望多多支持腳本之家!

相關(guān)文章

  • js Date()日期函數(shù)瀏覽器兼容問題解決方法

    js Date()日期函數(shù)瀏覽器兼容問題解決方法

    這篇文章主要介紹了js Date()日期函數(shù)瀏覽器兼容問題解決方法,需要的朋友可以參考下
    2017-09-09
  • 淺談?dòng)肳ebpack路徑壓縮圖片上傳尺寸獲取的問題

    淺談?dòng)肳ebpack路徑壓縮圖片上傳尺寸獲取的問題

    下面小編就為大家分享一篇淺談?dòng)肳ebpack路徑壓縮圖片上傳尺寸獲取的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-02-02
  • php 修改密碼實(shí)現(xiàn)代碼

    php 修改密碼實(shí)現(xiàn)代碼

    這篇文章主要介紹了php 修改密碼實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • Lombok實(shí)現(xiàn)方式JSR-269

    Lombok實(shí)現(xiàn)方式JSR-269

    Lombok的出現(xiàn)幫助開發(fā)人員在開發(fā)工程中消除了大部分冗余代碼:繁瑣的get、set方法甚至建造者模式,今天通過本文給大家分享Lombok實(shí)現(xiàn)方式JSR-269的相關(guān)知識(shí),感興趣的朋友一起看看吧
    2021-06-06
  • JavaScript生成指定范圍隨機(jī)數(shù)和隨機(jī)序列的方法

    JavaScript生成指定范圍隨機(jī)數(shù)和隨機(jī)序列的方法

    這篇文章主要介紹了JavaScript生成指定范圍隨機(jī)數(shù)和隨機(jī)序列,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2018-05-05
  • Bootstrap零基礎(chǔ)入門教程(二)

    Bootstrap零基礎(chǔ)入門教程(二)

    Bootstrap 是一個(gè)用于快速開發(fā) Web 應(yīng)用程序和網(wǎng)站的前端框架。Bootstrap 是基于 HTML、CSS、JAVASCRIPT 的。這篇文章主要介紹了Bootstrap零基礎(chǔ)入門教程(二) 的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,感興趣的朋友一起看下吧
    2016-07-07
  • 微信小程序使用npm包的方法步驟

    微信小程序使用npm包的方法步驟

    這篇文章主要介紹了微信小程序使用npm包的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • 解決option標(biāo)簽selected=

    解決option標(biāo)簽selected="selected"屬性失效的問題

    下面小編就為大家?guī)硪黄鉀Qoption標(biāo)簽selected="selected"屬性失效的問題。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧,希望對(duì)大家有所幫助
    2017-11-11
  • [轉(zhuǎn)]去百度面試的javascript 收獲

    [轉(zhuǎn)]去百度面試的javascript 收獲

    前兩天去了百度面試,1面是很順利,2面基本是被問暈問傻問成了個(gè)白癡。沒去之前,自己覺得自己的js還是可以的,但是被考官問的基本變成了白癡。
    2009-04-04
  • echarts使用中關(guān)于y坐標(biāo)軸無法正常顯示的問題解決記錄

    echarts使用中關(guān)于y坐標(biāo)軸無法正常顯示的問題解決記錄

    Echarts是由百度提供的數(shù)據(jù)可視化解決方案,下面這篇文章主要給大家介紹了關(guān)于echarts使用中關(guān)于y坐標(biāo)軸無法正常顯示的問題解決記錄,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2023-12-12

最新評(píng)論