亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

詳解Javascript的作用域、作用域鏈以及閉包

3403771864 / 522人閱讀

  一、我們先說說javascript的作用域

  ①全局變量-函數(shù)體外部進(jìn)行聲明

 ?、诰植孔兞?函數(shù)體內(nèi)部進(jìn)行聲明

  1)函數(shù)級(jí)作用域

  javascript語言中局部變量不同于C#、Java等高級(jí)語言,在這些高級(jí)語言內(nèi)部,采用的塊級(jí)作用域中會(huì)聲明新的變量,這些變量不會(huì)影響到外部作用域。

  而javascript則采用的是函數(shù)級(jí)作用域,也就是說js創(chuàng)建作用域的單位是函數(shù)。

  例如:

  在C#當(dāng)中我們寫如下代碼: 

 static void Main(string[] args)
  {
  for (var x = 1; x < 10; x++)
  {
  Console.WriteLine(x.ToString());
  }
  Console.WriteLine(x.ToString());
  }

  上面代碼會(huì)出現(xiàn)如下的編譯錯(cuò)誤:

  The name 'x' does not exist in the current context

  同樣在javascript當(dāng)中寫如下代碼:

  <script>
  function main() {
  for (var x = 1; x < 10; x++) {
  console.log(x.toString());
  }
  console.log(x.toString());
  }
  main();
  </script>

  輸出結(jié)果如下:

  [Web瀏覽器] "1"

  [Web瀏覽器] "2"

  [Web瀏覽器] "3"

  [Web瀏覽器] "4"

  [Web瀏覽器] "5"

  [Web瀏覽器] "6"

  [Web瀏覽器] "7"

  [Web瀏覽器] "8"

  [Web瀏覽器] "9"

  [Web瀏覽器] "10"

  在知道“塊級(jí)作用域”和“函數(shù)級(jí)作用域”的區(qū)別,塊級(jí)作用域當(dāng)離開作用域后,這樣外部就 用不上,就比如之前C#例子,"x"離開for循環(huán)后這個(gè)就沒效果,但是javascript中不一樣,它還可以進(jìn)行訪問。

  2)變量提升

  再看javascript中作用域的一個(gè)特性,例子如下:

  function func(){
  console.log(x);
  var x = 1;
  console.log(x);
  }
  func();

  輸出結(jié)果:

  [Web瀏覽器] "undefined"

  [Web瀏覽器] "1"

  其實(shí)我們可以想,為什么第一個(gè)輸出是“undefined”呢?我們可以看到j(luò)avascript中的“變量提升”,稱為“聲明提升”更好,因?yàn)檫@個(gè)機(jī)制就是把變量的聲明提前到函數(shù)的前面。也就會(huì)造成提升至,也就是為什么第一次沒有輸出“1”的原因。

  上面的代碼就相當(dāng)于:

  function func(){
  var x;
  console.log(x);
  var x = 1;
  console.log(x);
  }
  func();

  3)函數(shù)內(nèi)部用不用“var”對(duì)程序的影響

  這是個(gè)值得注意的地方:

  var x = 1;
  function addVar(){
  var x = 2;
  console.log(x);
  }
  addVar();
  console.log(x);

  輸出:

  [Web瀏覽器] "2"

  [Web瀏覽器] "1"

  當(dāng)在函數(shù)內(nèi)部去掉var之后,再執(zhí)行:

  var x = 1;
  function delVar(){
  x = 2;
  console.log(x);
  }
  delVar();
  console.log(x);

  [Web瀏覽器] "2"

  [Web瀏覽器] "2"

  上面的例子說明了,在函數(shù)內(nèi)部如果在聲明變量沒有使用var,那么聲明的變量就是全局變量。

  二、javascript的作用域鏈

  先看如下的代碼:

  var name="Global";
  function t(){
  var name="t_Local";
  function s1(){
  var name="s1_Local";
  console.log(name);
  }
  function s2(){
  console.log(name);
  }
  s1();
  s2();
  }
  t();

  輸出結(jié)果:

  [Web瀏覽器] "s1_Local"

  [Web瀏覽器] "t_Local"

  那么就有幾個(gè)問題:

  1.為什么第二次輸出的不是s1_Local?

  2.為什么不是Global?

  解決這個(gè)兩個(gè)問題就在于作用域鏈…

  下面就解析一下這個(gè)過程,

1.png

  例如當(dāng)上面程序創(chuàng)建完成的時(shí)候,會(huì)形成上圖中的鏈狀結(jié)構(gòu)(假想的),所以每次調(diào)用s1()函數(shù)的時(shí)候,console.log(name);先會(huì)在他自己的作用域中尋找name這個(gè)變量,這里它找到name=“s1_Local”,所以就輸出了s1_Local,而每次調(diào)用s2()的時(shí)候,它和s1()函數(shù)過程一樣,只不過在自身的作用域沒有找到,所以向上層查找,在t()函數(shù)中找到了,于是就輸出了"t_Local"。

  我們可以驗(yàn)證一下,如果把t中的name刪除,可以看看輸出是不是“Global”

  結(jié)果如下:

  [Web瀏覽器] "s1_Local"

  [Web瀏覽器] "Global"

  當(dāng)然具體每一個(gè)作用域直接是如何連接的,請(qǐng)參考


  https://www.jb51.net/article/28610.htm

  其中也說明了為什么JS當(dāng)中應(yīng)該盡量減少with關(guān)鍵字的使用。

  三、閉包

  了解了作用域和作用域鏈的概念之后,再去理解閉包就相對(duì)容易了。

  1.閉包第一個(gè)作用

  還是先看例子: 

 function s1() {
  var x = 123;
  return s2();
  }
  function s2() {
  return x;
  }
  alert(s1());

  這里我想彈出x的值,但是卻發(fā)生了錯(cuò)誤 "Uncaught ReferenceError: x is not defined",根據(jù)作用域鏈的知識(shí),了解到因?yàn)閟2中沒有x的定義,并且向上找全局也沒有x定義,所以就報(bào)錯(cuò)了。也就是說s1和s2不能夠共享x的值。

  那么問題來了,我想要訪問s1當(dāng)中的x,怎么弄?

  修改代碼如下:

  function s1() {
  var x = 123;
  return function(){
  return x;
  };
  }
  var test = s1();
  console.log(test());

  結(jié)果為:

  [Web瀏覽器] "123"

2.png

  解釋:因?yàn)閒unction本質(zhì)也是數(shù)據(jù),所以它和x的作用域相同,可以訪問x。這樣就相當(dāng)于對(duì)外部開放了一個(gè)可以訪問內(nèi)部變量的接口。

  還可以怎么玩,稍微修改一下代碼

  var func;
  function f(){
  var x='123';
  func=function(){
  return x;
  };
  }
  f();
  alert(func());

  定義一個(gè)全局的變量,然后在函數(shù)內(nèi)部讓其等于一個(gè)函數(shù),這樣就可以通過這個(gè)全局變量來進(jìn)行訪問x了。

  綜上:閉包是啥?閉包就相當(dāng)于函數(shù)外部和內(nèi)部的橋梁,通過閉包可以在外部訪問函數(shù)內(nèi)部的變量。這也是閉包的第一個(gè)作用。

  2.閉包的第二個(gè)作用

  先看代碼:

  function f1(){
  var n=1;
  add=function(){
  n+=1;
  };
  function f2(){
  console.log(n);
  return '輸出完成';
  }
  return f2;
  }
  var res=f1();
  console.log(res());
  add();
  console.log(res());

  輸出結(jié)果:

  [Web瀏覽器] "1"

  [Web瀏覽器] "輸出完成"

  [Web瀏覽器] "2"

  [Web瀏覽器] "輸出完成"

  問題為什么第二次輸出的結(jié)果n變成了2,沒有被清除?

 這很好解釋,在res是一個(gè)全局變量,一直駐留在內(nèi)存當(dāng)中,它就相當(dāng)于f2函數(shù),f2函數(shù)又要依賴于f1函數(shù),所以f1也駐留在內(nèi)存當(dāng)中,保證不被GC所回收,每一次調(diào)用add函數(shù)的時(shí)候,就相當(dāng)于把f1中的n重新賦值了,這樣n的值就變化了。這也是閉包的第二個(gè)作用,可以讓變量的值始終保存在內(nèi)存當(dāng)中。

  3.閉包的應(yīng)用

 ?、倏梢宰鲈L問控制(相當(dāng)于C#當(dāng)中的屬性)

  //定義兩個(gè)變量,用于存儲(chǔ)取值和存值
  var get,set;
  //定義一個(gè)自調(diào)用函數(shù),設(shè)定set和get方法
  (function(){
  //設(shè)定x的默認(rèn)值
  var x = 0;
  set = function(n){
  x = n;
  }
  get = function(){
  return x;
  }
  })();
  console.log(get());
  set(5);
  console.log(get());

  輸出結(jié)果:

  [Web瀏覽器] "0"

  [Web瀏覽器] "5"

 ?、诳梢杂米龅?/p>

 

 //定義一個(gè)函數(shù),里面使用了閉包
  function foo(myArray){
  var i=0;
  //閉包迭代器
  next=function(){
  //每次返回?cái)?shù)組的當(dāng)前值,下標(biāo)+1
  return myArray[i++];
  }
  }
  //調(diào)用foo,參數(shù)為一個(gè)數(shù)組
  foo(['a','b','c','d']);
  //每次調(diào)用next都會(huì)打印數(shù)組的一個(gè)值
  console.log(next());
  console.log(next());
  console.log(next());
  console.log(next());

  輸出結(jié)果:

  [Web瀏覽器] "a"

  [Web瀏覽器] "b"

  [Web瀏覽器] "c"

  [Web瀏覽器] "d"

 ?、坶]包在循環(huán)中的使用

  例1

 

 function f(){
  var a=[];
  var i;
  for(i=0;i<3;i++){
  a[i]=function(){
  return i;
  };
  }
  return a;
  }
  var test=f();
  console.log(test[0]());
  console.log(test[1]());
  console.log(test[2]());

  輸出結(jié)果:

  [Web瀏覽器] "3"

  [Web瀏覽器] "3"

  [Web瀏覽器] "3"

  為什么結(jié)果不是0、1、2?

  這里我們使用了閉包,每次循環(huán)都指向了同一個(gè)局部變量i,但是閉包不會(huì)記錄每一次循環(huán)的值,只保存了變量的引用地址,所以當(dāng)我們?cè)谡{(diào)用test[0]()、test[1]()、test[2]()的時(shí)候都返回的是for循環(huán)最后的值,也就是3的時(shí)候跳出了循環(huán)。

  例2:我想輸出0,1,2怎么搞?

  function f(){
  var a=[];
  var i;
  for(i=0;i<3;i++){
  a[i]=(function(x){
  return function(){
  return x;
  }
  })(i);
  }
  return a;
  }
  var test=f();
  console.log(test[0]());
  console.log(test[1]());
  console.log(test[2]());

  結(jié)果:

  [Web瀏覽器] "0"

  [Web瀏覽器] "1"

  [Web瀏覽器] "2"

  歡迎大家關(guān)注更多后續(xù)精彩文章,學(xué)習(xí)更要實(shí)踐。


文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/128213.html

相關(guān)文章

  • 閉包詳解

    摘要:函數(shù)中的閉包閉包是指有權(quán)訪問另一個(gè)函數(shù)作用域中的變量的函數(shù)。理解閉包預(yù)與變量此時(shí)返回注意觀察下面的輸出內(nèi)容,理解函數(shù)的調(diào)用時(shí)刻和把的賦值給變量時(shí)刻這個(gè)函數(shù)會(huì)返回長(zhǎng)度為的函數(shù)數(shù)組。 Javascript函數(shù)中的閉包 閉包是指有權(quán)訪問另一個(gè)函數(shù)作用域中的變量的函數(shù)。創(chuàng)建閉包的常見方式就是,在一個(gè)函數(shù)的內(nèi)部創(chuàng)建另一個(gè)函數(shù)。 有關(guān)創(chuàng)建作用域鏈以及作用域鏈有什么作用的細(xì)節(jié)對(duì)于徹底理解閉包至關(guān)重...

    lunaticf 評(píng)論0 收藏0
  • 閉包全面詳解

    摘要:環(huán)境由閉包創(chuàng)建時(shí)在作用域中的任何局部變量組成。嚴(yán)格來說,閉包需要滿足三個(gè)條件訪問所在作用域函數(shù)嵌套在所在作用域外被調(diào)用閉包的形成原理先了解的垃圾回收機(jī)制會(huì)找出不再使用的變量,不再使用意味著這個(gè)變量生命周期的結(jié)束。 什么是閉包 最原始定義 閉包(closure),是指函數(shù)變量可以保存在函數(shù)作用域內(nèi),因此看起來是函數(shù)將變量包裹了起來。 //根據(jù)定義,包含變量的函數(shù)就是閉包 function...

    qylost 評(píng)論0 收藏0
  • 詳解js中閉包

    摘要:定義函數(shù)的時(shí)候,為什么的值重新從開始了因?yàn)橛忠淮芜\(yùn)行了函數(shù),生成一個(gè)新的的活動(dòng)對(duì)象,所以的作用域鏈引用的是一個(gè)新的值。 前言 在js中,閉包是一個(gè)很重要又相當(dāng)不容易完全理解的要點(diǎn),網(wǎng)上關(guān)于講解閉包的文章非常多,但是并不是非常容易讀懂,在這里以《javascript高級(jí)程序設(shè)計(jì)》里面的理論為基礎(chǔ)。用拆分的方式,深入講解一下對(duì)于閉包的理解,如果有不對(duì)請(qǐng)指正。 寫在閉包之前 閉包的內(nèi)部細(xì)節(jié),...

    chaosx110 評(píng)論0 收藏0
  • 詳解js變量、作用及內(nèi)存

    摘要:不是引用類型,無法輸出簡(jiǎn)而言之,堆內(nèi)存存放引用值,棧內(nèi)存存放固定類型值。變量的查詢?cè)谧兞康牟樵冎?,訪問局部變量要比全局變量來得快,因此不需要向上搜索作用域鏈。 贊助我以寫出更好的文章,give me a cup of coffee? 2017最新最全前端面試題 基本類型值有:undefined,NUll,Boolean,Number和String,這些類型分別在內(nèi)存中占有固定的大小空...

    waltr 評(píng)論0 收藏0
  • 【進(jìn)階2-1期】深入淺出圖解作用鏈和閉包

    摘要:本期推薦文章從作用域鏈談閉包,由于微信不能訪問外鏈,點(diǎn)擊閱讀原文就可以啦。推薦理由這是一篇譯文,深入淺出圖解作用域鏈,一步步深入介紹閉包。作用域鏈的頂端是全局對(duì)象,在全局環(huán)境中定義的變量就會(huì)綁定到全局對(duì)象中。 (關(guān)注福利,關(guān)注本公眾號(hào)回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實(shí)戰(zhàn)、面試指導(dǎo)) 本周開始前端進(jìn)階的第二期,本周的主題是作用域閉包,今天是第6天。 本...

    levius 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<