亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

深入理解javascript作用域和閉包

2019-11-20 14:06:59
字體:
來源:轉載
供稿:網友

作用域

作用域是一個變量和函數的作用范圍,javascript中函數內聲明的所有變量在函數體內始終是可見的,在javascript中有全局作用域和局部作用域,但是沒有塊級作用域,局部變量的優先級高于全局變量,通過幾個示例來了解下javascript中作用域的那些“潛規則”(這些也是在前端面試中經常問到的問題)。

1. 變量聲明提前
示例1:

var scope="global";function scopeTest(){  console.log(scope);  var scope="local" }scopeTest(); //undefined

此處的輸出是undefined,并沒有報錯,這是因為在前面我們提到的函數內的聲明在函數體內始終可見,上面的函數等效于:

var scope="global";function scopeTest(){  var scope;  console.log(scope);  scope="local" }scopeTest(); //local

注意,如果忘記var,那么變量就被聲明為全局變量了。

2. 沒有塊級作用域

和其他我們常用的語言不同,在Javascript中沒有塊級作用域:

function scopeTest() {  var scope = {};  if (scope instanceof Object) {    var j = 1;    for (var i = 0; i < 10; i++) {      //console.log(i);    }    console.log(i); //輸出10  }  console.log(j);//輸出1}

在javascript中變量的作用范圍是函數級的,即在函數中所有的變量在整個函數中都有定義,這也帶來了一些我們稍不注意就會碰到的“潛規則”:

var scope = "hello";function scopeTest() {  console.log(scope);//①  var scope = "no";  console.log(scope);//②}

在①處輸出的值竟然是undefined,簡直喪心病狂啊,我們已經定義了全局變量的值啊,這地方不應該為hello嗎?其實,上面的代碼等效于:

var scope = "hello";function scopeTest() {  var scope;  console.log(scope);//①  scope = "no";  console.log(scope);//②}

聲明提前、全局變量優先級低于局部變量,根據這兩條規則就不難理解為什么輸出undefined了。

作用域鏈

在javascript中,每個函數都有自己的執行上下文環境,當代碼在這個環境中執行時,會創建變量對象的作用域鏈,作用域鏈是一個對象列表或對象鏈,它保證了變量對象的有序訪問。
作用域鏈的前端是當前代碼執行環境的變量對象,常被稱之為“活躍對象”,變量的查找會從第一個鏈的對象開始,如果對象中包含變量屬性,那么就停止查找,如果沒有就會繼續向上級作用域鏈查找,直到找到全局對象中:

作用域鏈的逐級查找,也會影響到程序的性能,變量作用域鏈越長對性能影響越大,這也是我們盡量避免使用全局變量的一個主要原因。

閉包

基礎概念
作用域是理解閉包的一個前提,閉包是指在當前作用域內總是能訪問外部作用域中的變量。

function createClosure(){  var name = "jack";  return {    setStr:function(){      name = "rose";    },    getStr:function(){      return name + ":hello";    }  }}var builder = new createClosure();builder.setStr();console.log(builder.getStr()); //rose:hello

上面的示例在函數中返回了兩個閉包,這兩個閉包都維持著對外部作用域的引用,因此不管在哪調用總是能夠訪問外部函數中的變量。在一個函數內部定義的函數,會將外部函數的活躍對象添加到自己的作用域鏈中,因此上面實例中通過內部函數能夠訪問外部函數的屬性,這也是javascript模擬私有變量的一種方式。

注意:由于閉包會額外的附帶函數的作用域(內部匿名函數攜帶外部函數的作用域),因此,閉包會比其它函數多占用些內存空間,過度的使用可能會導致內存占用的增加。

閉包中的變量

在使用閉包時,由于作用域鏈機制的影響,閉包只能取得內部函數的最后一個值,這引起的一個副作用就是如果內部函數在一個循環中,那么變量的值始終為最后一個值。

  //該實例不太合理,有一定延遲因素,此處主要為了說明閉包循環中存在的問題  function timeManage() {    for (var i = 0; i < 5; i++) {      setTimeout(function() {        console.log(i);      },1000)    };  }

上面的程序并沒有按照我們預期的輸入1-5的數字,而是5次全部輸出了5。再來看一個示例:

function createClosure(){  var result = [];  for (var i = 0; i < 5; i++) {    result[i] = function(){      return i;    }  }  return result;}

調用createClosure()[0]()返回的是5,createClosure()[4]()返回值仍然是5。通過以上兩個例子可以看出閉包在帶有循環的內部函數使用時存在的問題:因為每個函數的作用域鏈中都保存著對外部函數(timeManage、createClosure)的活躍對象,因此,他們都引用著同一變量i,當外部函數返回時,此時的i值為5,所以內部的每個函數i的值也為5。
那么如何解決這個問題呢?我們可以通過匿名包裹器(匿名自執行函數表達式)來強制返回預期的結果:

function timeManage() {  for (var i = 0; i < 5; i++) {    (function(num) {      setTimeout(function() {        console.log(num);      }, 1000);    })(i);  }}

或者在閉包匿名函數中再返回一個匿名函數賦值:

function timeManage() {  for (var i = 0; i < 10; i++) {    setTimeout((function(e) {      return function() {        console.log(e);      }    })(i), 1000)  }}//timeManager();輸出1,2,3,4,5function createClosure() {  var result = [];  for (var i = 0; i < 5; i++) {    result[i] = function(num) {      return function() {        console.log(num);      }    }(i);  }  return result;}//createClosure()[1]()輸出1;createClosure()[2]()輸出2

無論是匿名包裹器還是通過嵌套匿名函數的方式,原理上都是由于函數是按值傳遞,因此會將變量i的值復制給實參num,在匿名函數的內部又創建了一個用于返回num的匿名函數,這樣每個函數都有了一個num的副本,互不影響了。

閉包中的this

在閉包中使用this時要特別注意,稍微不慎可能會引起問題。通常我們理解this對象是運行時基于函數綁定的,全局函數中this對象就是window對象,而當函數作為對象中的一個方法調用時,this等于這個對象(TODO 關于this做一次整理)。由于匿名函數的作用域是全局性的,因此閉包的this通常指向全局對象window:

var scope = "global";var object = {  scope:"local",  getScope:function(){    return function(){      return this.scope;    }  }}

調用object.getScope()()返回值為global而不是我們預期的local,前面我們說過閉包中內部匿名函數會攜帶外部函數的作用域,那為什么沒有取得外部函數的this呢?每個函數在被調用時,都會自動創建this和arguments,內部匿名函數在查找時,搜索到活躍對象中存在我們想要的變量,因此停止向外部函數中的查找,也就永遠不可能直接訪問外部函數中的變量了??傊?,在閉包中函數作為某個對象的方法調用時,要特別注意,該方法內部匿名函數的this指向的是全局變量。

幸運的是我們可以很簡單的解決這個問題,只需要把外部函數作用域的this存放到一個閉包能訪問的變量里面即可:

var scope = "global";var object = {  scope:"local",  getScope:function(){    var that = this;    return function(){      return that.scope;    }  }}object.getScope()()返回值為local。

內存與性能

由于閉包中包含與函數運行期上下文相同的作用域鏈引用,因此,會產生一定的負面作用,當函數中活躍對象和運行期上下文銷毀時,由于必要仍存在對活躍對象的引用,導致活躍對象無法銷毀,這意味著閉包比普通函數占用更多的內存空間,在IE瀏覽器下還可能會導致內存泄漏的問題,如下:

 function bindEvent(){  var target = document.getElementById("elem");  target.onclick = function(){    console.log(target.name);  } }

上面例子中匿名函數對外部對象target產生一個引用,只要是匿名函數存在,這個引用就不會消失,外部函數的target對象也不會被銷毀,這就產生了一個循環引用。解決方案是通過創建target.name副本減少對外部變量的循環引用以及手動重置對象:

 function bindEvent(){  var target = document.getElementById("elem");  var name = target.name;  target.onclick = function(){    console.log(name);  }  target = null; }

閉包中如果存在對外部變量的訪問,無疑增加了標識符的查找路徑,在一定的情況下,這也會造成性能方面的損失。解決此類問題的辦法我們前面也曾提到過:盡量將外部變量存入到局部變量中,減少作用域鏈的查找長度。

總結:閉包不是javascript獨有的特性,但是在javascript中有其獨特的表現形式,使用閉包我們可以在javascript中定義一些私有變量,甚至模仿出塊級作用域,但閉包在使用過程中,存在的問題我們也需要了解,這樣才能避免不必要問題的出現。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲第一区第二区| 亚洲精品www| 精品国产乱码久久久久久天美| 欧美日韩国产成人在线观看| 久久电影一区二区| 92国产精品视频| 亚洲男人天堂网| 精品久久久91| 黑人极品videos精品欧美裸| 国内揄拍国内精品少妇国语| 国产欧美欧洲在线观看| 久久久久久999| 久久精品成人一区二区三区| 久久久久久国产免费| 亚洲国产精品一区二区三区| 国产精品久久久久免费a∨| 国产精品久久久久久久久久久久久| 欧美精品午夜视频| 久久99精品视频一区97| 亚洲性夜色噜噜噜7777| 日韩欧美在线视频免费观看| 国产免费一区二区三区在线观看| 欧美在线视频一二三| 国产精品久久久| 亚洲精品v欧美精品v日韩精品| 欧美性生交xxxxxdddd| 欧美电影在线免费观看网站| 久久亚洲精品一区二区| 精品成人国产在线观看男人呻吟| 91在线视频免费| 欧美乱人伦中文字幕在线| 欧美精品久久久久久久久| 中文字幕在线成人| 一区二区欧美激情| 亚洲qvod图片区电影| 精品久久久久久久久久国产| 日韩精品在线电影| 成人午夜在线观看| 国产亚洲一区精品| 国产丝袜精品第一页| 欧美精品少妇videofree| 国产精品第一页在线| 欧美成人在线免费视频| 欧美色视频日本高清在线观看| 国产精品久久久久av| 亚洲黄色av女优在线观看| 琪琪亚洲精品午夜在线| 日本高清视频精品| 国产精品十八以下禁看| 欧美高跟鞋交xxxxhd| 精品久久久久久久久久ntr影视| 亚洲va欧美va国产综合久久| 亚洲一区二区三区毛片| 91久久夜色精品国产网站| 国产剧情日韩欧美| 国内精品久久久久伊人av| 日本精品视频网站| 亚洲伦理中文字幕| 亚洲激情在线观看视频免费| 久久99精品久久久久久琪琪| 欧美成人精品一区二区三区| 国模精品视频一区二区三区| 亚洲国产精品久久91精品| 91在线观看免费网站| 一区二区三区视频免费在线观看| 狠狠色香婷婷久久亚洲精品| 久久影视三级福利片| 日韩精品一区二区视频| 欧美成人国产va精品日本一级| 亚洲国产精品99久久| 午夜精品久久久久久久久久久久| 久久影院中文字幕| 国产激情久久久久| 亚洲人成网站777色婷婷| 国产区亚洲区欧美区| 国产综合久久久久久| 久久精品国产2020观看福利| 北条麻妃一区二区在线观看| 亚洲va欧美va在线观看| 久久久精品一区二区三区| 亚洲精品99久久久久| 国产精品pans私拍| 日韩免费观看网站| 成人免费网站在线观看| 亚洲人成人99网站| 中文字幕日韩高清| 欧美中文字幕在线观看| 日韩美女免费观看| 亚洲黄色有码视频| 亚洲一区av在线播放| 国产三级精品网站| 亚洲精品乱码久久久久久按摩观| 日韩男女性生活视频| 成人免费激情视频| 亚洲最大福利网站| 亚洲男人天堂手机在线| 日韩欧美亚洲综合| 亚洲图片在线综合| 亚洲人成在线观看网站高清| 成人黄色午夜影院| 91中文在线观看| 欧美黄色片视频| 欧美日韩在线看| 亚洲国产一区二区三区在线观看| 国产精品久久久久av| 国产精品成人av在线| 精品一区二区三区电影| 17婷婷久久www| 久久偷看各类女兵18女厕嘘嘘| 亚洲在线www| 国产精品久久激情| 欧美久久精品午夜青青大伊人| 国产女精品视频网站免费| 91精品在线播放| 久久成人在线视频| 国产精品丝袜高跟| 91中文字幕在线| 国产精品美腿一区在线看| 国产成人精品视频在线| 久久久av亚洲男天堂| 欧美亚洲午夜视频在线观看| 欧美激情一区二区三级高清视频| 欧美日产国产成人免费图片| 5252色成人免费视频| 亚洲男人天堂网站| 日产精品久久久一区二区福利| 日本aⅴ大伊香蕉精品视频| 欧美激情视频在线观看| 国产日韩欧美综合| 97在线看福利| 久久av中文字幕| 国产精品高精视频免费| 韩国日本不卡在线| 欧美裸体xxxxx| 欧美日韩在线免费| 91日韩在线视频| 97久久久免费福利网址| 伊人久久大香线蕉av一区二区| 日韩av日韩在线观看| 97在线观看免费| 日韩黄在线观看| 亚洲欧美日韩精品久久奇米色影视| 欧美日韩成人精品| 日韩有码在线播放| 91av在线免费观看视频| 国产亚洲欧洲高清一区| 欧美在线视频a| 日韩av第一页| 国产又爽又黄的激情精品视频| 欧美精品九九久久| 91美女片黄在线观| 91在线免费网站| 成人一区二区电影| 一本一本久久a久久精品牛牛影视| 青草热久免费精品视频| 成人福利视频在线观看| 国产精品精品一区二区三区午夜版| 一区二区三区视频免费在线观看| 国模私拍视频一区| 日韩精品在线播放| 亚洲最新av网址| 国产aⅴ夜夜欢一区二区三区| 亚洲视频在线免费看| 韩剧1988在线观看免费完整版|