JavaScript 中的閉包和作用域鏈(讀書筆記)

  要想理解閉包,應當先理解JavaScript的作用域和作用域鏈。

  JavaScript有一個特性被稱之為“聲明提前(hoisting)”,即JavaScript函數里聲明的所有變量(但不涉及賦值)都被“提前”至函數體的頂部,“聲明提前”這步操作是在JavaScript引擎的“預編譯”時進行的,是在代碼開始運行之前,看一下下面的例子:

var name = "YY";
function getName(){console.log(name);      //輸出undefine,而不是“YY”var name = "Crucify";console.log(name);      //輸出“Crucify”
}

  首先局部變量定義了一個和全局變量相同名字的變量,則在函數體內部局部變量遮蓋了同名的全局變量,然后在函數體內部變量name的聲明被提前至函數體頂部但并沒有賦值,所以此時name是一個只被聲明但并沒有初始化的變量,我們知道變量只進行聲明但并不初始化則它的值為undefine,所以第一行打印時undefine,下一行開始為變量name進行賦值,所以第二行打印的輸出是我們所期望的。

?

  當某個函數被調用時,會創建一個執行環境及相應的作用域鏈。

  執行環境(execution context)定義了變量或函數有權訪問的其他數據,決定了它們各自的行為。每個函數都有自己的執行環境。當執行流進入一個函數時,函數的環境就會被推入一個環境棧中。而在函數執行之后,棧將其環境彈出,把控制權返回給之前的執行環境。當代碼在一個環境中執行時,會創建變量對象的一個作用域鏈(scope chain)。作用域鏈的用途,是保證對執行環境有權訪問的所有變量和函數的有序訪問。

  作用域鏈本質上是一個指向變量對象的指針列表,它只引用但不實際包含變量對象。當JavaScript需要查找變量x的值的時候(這個過程稱作“變量解析”(variable resolution)),他會從列表之中的第一個對象開始查找直到最后一個對象,如果某個對象有一個名為x的屬性,則會直接食用這個屬性的值。如果作用域鏈上沒有任何一個對象含有屬性x,那么就認為這段代碼的作用域上不存在x,并最終拋出一個引用錯誤異常。

  所謂的“變量對象的指針列表”很好理解。在JavaScript的最頂層代碼中(也就是不包含在任何函數定義內的代碼),作用域鏈是由一個全局對象組成。在不包含嵌套的函數體內,作用域鏈上有兩個對象,第一個是定義函數參數和局部變量的對象,第二個是全局對象。在一個嵌套的函數體內,作用域鏈上則至少有三個對象,看下面的例子:

var name = "YY";
function getName(){var name = "Crucify";function f(){return name;          }return f();
}

  函數f()的作用域鏈上有三個對象,第一個是定義函數f()參數和局部變量的對象,第二個是定義函數getName()參數和局部變量的對象,第三個是全局對象。

  每個環境都可以向上搜索作用域鏈,以查詢變量和函數名,但任何環境都不能通過向下搜索作用域鏈而進入另一個執行環境,即函數f()可以向上搜索函數getName()和全局對象中的屬性,但是全局對象不能向下搜索getName()和f()中的值。

?

  而創建閉包的常見方式,就是在一個函數內部創建另一個函數。閉包是指有權訪問另一個函數作用域中的變量的函數。

  一般來講,當函數執行完畢后,局部活動對象就會被銷毀,內存中僅保存全局作用域(全局執行環境的變量對象)。但是,閉包的情況有所不同,因為在另一個函數內部定義的函數會將包含函數(即外部函數)的活動對象添加到它的作用域鏈中,參考下面的代碼:

function createComparisonFunction(propertyName) {return function(object1, object2){var value1 = object1[propertyName];var value2 = object2[propertyName];if (value1 < value2){return -1;} else if (value1 > value2){return 1;} else {return 0;}};
}

當下列代碼執行時,包含函數與內部匿名函數的作用域鏈如圖所示:

var compare = createComparisonFunction("name");
var result = compare({ name: "Nicholas" }, { name: "Greg" });

?

  當createComparisonFunction()函數在執行完畢后,其活動對象也不會被銷毀,因為匿名函數的作用域鏈仍然在引用這個活動對象。直到匿名函數被銷毀后, createComparisonFunction()的活動對象才會被銷毀:

compare = null;  //解除對匿名函數的引用(以便釋放內存)

  由于閉包會攜帶包含它的函數的作用域,因此會比其他函數占用更多的內存。過度使用閉包可能會導致內存占用過多,所以在絕對必要時再考慮使用閉包。

  作用域鏈的這種配置機制引出了一個值得注意的副作用,即閉包只能取得包含函數中任何變量的最后一個值。因為閉包所保存的是整個變量對象,而不是某個特殊的變量:

function createFunctions(){var result = new Array();for (var i=0; i < 10; i++){result[i] = function(){return i;};}return result;
}

  這個函數會返回一個函數數組,且每個函數都返回 10。

  在閉包中使用 this 對象也可能會導致一些問題。我們知道, this 對象是在運行時基于函數的執行環境綁定的,而匿名函數的執行環境具有全局性,因此其 this 對象通常指向 window(在通過 call()或 apply()改變函數執行環境的情況下, this 就會指向其他對象),看下面的例子:

var name = "The Window";
var object = {name : "My Object",getNameFunc : function(){return function(){return this.name;};}
};
alert(object.getNameFunc()()); //"The Window"(在非嚴格模式下)

  把外部作用域中的 this 對象保存在一個閉包能夠訪問到的變量里,就可以讓閉包訪問該對象了,如下所示:

var name = "The Window";
var object = {name : "My Object",getNameFunc : function(){var that = this;return function(){return that.name;};}
};
alert(object.getNameFunc()()); //"My Object"

arguments 也存在同樣的問題。如果想訪問作用域中的 arguments 對象,必須將對該對象的引用保存到另一個閉包能夠訪問的變量中。

轉載于:https://www.cnblogs.com/crucify-lee/p/4521273.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/257264.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/257264.shtml
英文地址,請注明出處:http://en.pswp.cn/news/257264.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

leetcode jump game ii

題目&#xff1a; Given an array of non-negative integers, you are initially positioned at the first index of the array. Each element in the array represents your maximum jump length at that position. Your goal is to reach the last index in the minimum numb…

韓師師范學院計算機科學與技術在哪個學區,2017年韓山師范學院本科插班生考試《數據結構》A卷...

韓山師范學院2017年本科插班生考試試卷計算機科學與技術 專業 數據結構 試卷(A 卷)一、單項選擇題(每題2分&#xff0c;共30分)1. 對線性表&#xff0c;在下列哪種情況下應當采用鏈表表示&#xff1f;( ) A. 經常需要隨機地存取元素 B. 經常需要進行插入和刪除操作 C. 表中元素…

JAVA取隨機數,石頭剪刀布實例

一、取隨機數&#xff1a; import java.util.Random; //導入隨機數 public class Test{public static void main(String[] args){Random xx new Random(); //聲明隨機數int number xx.nextInt(10); //賦值隨機數給numberSystem.out.println("隨機數…

計算機網絡犯罪和一般犯罪的不同,論計算機網絡犯罪題稿.doc

目 錄摘要2第一章、網絡犯罪概念、特點以及構成特征5(一)網絡犯罪的概念認定5(二)網絡犯罪的特點6(三)網絡犯罪的構成7第二章、?網絡犯罪的類型9(一)網絡色情和性騷擾9(二)欺詐9(三)販賣、銷售違禁物品11(四)妨害名譽、侵犯個人隱私12(五)?制造、傳播計算機病毒12第三章、?網…

實例變量和靜態變量(或類變量static)

一個類通過使用運算符new可以創建多個不同的對象&#xff0c;這些對象將被分配不同的內存空間&#xff0c;準確的說法是&#xff1a;不同對象的實例變量將被分配不同的內存空間&#xff0c;如果類中有類變量&#xff0c;那么所有對象的這個類變量都被分配到同一處內存&#xff…

DB2 數據庫清表語句

truncate table DWDM2.tablename IMMEDIATE; alter table DWDM1.tablename activate not logged initially with empty table&#xff1b; but which one is best ? the truncate should be better 轉載于:https://www.cnblogs.com/TendToBigData/p/10501485.html

cnblogs_504 Gateway Time-out

地址&#xff1a;http://zzk.cnblogs.com/s?tb&w%E6%B1%82%E8%81%8C 504 Gateway Time-out 504 Gateway Time-out The gateway did not receive a timely response from the upstream server or application. Sorry for the inconvenience. Please report this message an…

第一階段

初步實現了相機的調用&#xff0c;做了簡單界面&#xff0c;并沒有實現核心功能 Button button (Button) findViewById(R.id.sao);button.setOnClickListener(new OnClickListener(){Overridepublic void onClick(View v) {Intent intent new Intent(MediaStore.ACTION_IMAGE…

JavaScript 詳說事件機制之冒泡、捕獲、傳播、委托

DOM事件流&#xff08;event flow &#xff09;存在三個階段&#xff1a;事件捕獲階段、處于目標階段、事件冒泡階段。 事件捕獲&#xff08;event capturing&#xff09;&#xff1a;通俗的理解就是&#xff0c;當鼠標點擊或者觸發dom事件時&#xff0c;瀏覽器會從根節點開始…

很棒的HTML5效果實例

2019獨角獸企業重金招聘Python工程師標準>>> http://mrdoob.com/141/Internet_Explorer_with_WebGL 轉載于:https://my.oschina.net/u/3647620/blog/1552495

計算機一級網絡操作題沒點回答,計算機等級一級考試操作題1(附答案)

一、選擇題1、在計算機領域中通常用mips來描述______。a、計算機的運算速度 b、計算機的可靠性 c、計算機的可運行性 d、計算機的可擴充性2、微型計算機存儲系統中&#xff0c;prom是______。a、可讀寫存儲器 b、動態隨機存取存儲器 c、只讀存儲器 d、可編程只讀存儲器3、按161…

模擬 Codeforces Round #297 (Div. 2) A. Vitaliy and Pie

題目傳送門 1 /*2 模擬&#xff1a;這就是一道模擬水題&#xff0c;看到標簽是貪心&#xff0c;還以為錯了呢3 題目倒是很長:)4 */5 #include <cstdio>6 #include <algorithm>7 #include <iostream>8 #include <algorithm>9 #include <cstr…

Socket 之 API函數介紹

1、創建套接字──socket() 應用程序在使用套接字前&#xff0c;首先必須擁有一個套接字&#xff0c;系統調用socket()向應用程序提供創建套接字的手段&#xff0c;其調用格式如下&#xff1a; SOCKET PASCAL FAR socket(int af, int type, int protocol); 該調用要接收三個參…

分配的訪問權限的展臺應用:最佳做法

原文: 分配的訪問權限的展臺應用&#xff1a;最佳做法 best practices guidance for developing a kiosk app for assigned access. 在 Windows 10 中&#xff0c;你可以使用鎖屏框架和分配的訪問權限創建展臺應用&#xff0c;該應用允許用戶與設備上的單個應用進行交互。 本文…

計算機工程 目錄 2014年第1期 pdf,2013科技核心期刊目錄有效期至2014年).pdf

2013科技核心期刊目錄有效期至2014年).pdf中國科技核心期刊(中國科技論文統計源期刊)2013CODE 期刊名稱2013 年新入選F034 ACTA BIOCHIMICA ET BIOPHYSICA SINICAC096 ACTA MATHEMATICA SCIENTIAB030 ACTA MATHEMATICA SINICA ENGLISH SERIESI051 ACTA MATHEMATICAE APPLICATAE…

SQL Server 阻止了對組件 'Ad Hoc Distributed Queries' 的 STATEMENT'OpenRowset/OpenDatasource' 的訪問的解決方案...

今天寫了一個excel表的導入功能&#xff0c;結果在excel表中的內容導入到頁面時報錯&#xff1a;SQL Server 阻止了對組件 Ad Hoc Distributed Queries 的 STATEMENTOpenRowset/OpenDatasource 的訪問&#xff0c;因為此組件已作為此服務器安全配置的一部分而被關閉。系統管…

Mongo客戶端MongoVUE的基本使用

這里沒有涉及到服務器以及客戶端的安裝&#xff0c;文章主要介紹mongo客戶端mongoVUE的使用 一、數據庫連接 點擊綠色加號添加一個連接&#xff0c;輸入name、server、port&#xff0c;點擊save&#xff0c;點擊connect進行連接 二、添加 1.右鍵添加一個Database 2.輸入名稱&am…

Vim雜記:Sublime的配色方案

一、前言                                     愛美之心人皆有之&#xff0c;sublime的配色實在好看&#xff0c;于是希望Vim也能這樣。 二、配置                                     1.下載monok…

計算機一級考試有三科,全國計算機一級考試是一級WPS?Office?一級MS?Office?一級Photoshop?三個任選一個考試嗎?...

滿意答案nanrrui3j2017.08.24采納率&#xff1a;41% 等級&#xff1a;9已幫助&#xff1a;415人全國計算機一級考試是有考試大綱的&#xff0c;按照大綱要求是三科都考。一級MS Office、一級WPS Office、一級Photoshop&#xff0c;一級共三個科目。完全采取上機考試形式&…

mysql索引結構原理、性能分析與優化

摘要&#xff1a; 第一部分&#xff1a;基礎知識 第二部分&#xff1a;MYISAM和INNODB索引結構 1、簡單介紹B-tree B tree樹 2、MyisAM索引結構 3、Annode索引結構 4、MyisAM索引與InnoDB索引相比較 第三部分&#xff1a;MYSQL優化 1、表數據類型選擇 2、sql語句優化 (1) 最…