用9種辦法解決 JS 閉包經典面試題之 for 循環取 i

?

2017-01-06?Tomson?JavaScript

轉自?https://segmentfault.com/a/1190000003818163

?

閉包

1.正確的說,應該是指一個閉包域,每當聲明了一個函數,它就產生了一個閉包域(可以解釋為每個函數都有自己的函數棧),每個閉包域(Function 對象)都有一個 function scope(不是屬性),function scope內默認有個名為Global的全局引用(有了這個引用,就可以直接調用 Global 的屬性或方法)

2.凡是在閉包域內聲明的變量或方法,外部無法直接訪問

3.閉包域可以訪問外部的變量或方法?
(上圖為 chrome 下 debug 環境)

當在一個閉包域內包含另一個閉包域時(簡單的說就是在一個函數內有另一個函數,當然這個內部函數的生命周期是依附于外部函數的), 此時,若子閉包域(內部的閉包域,內部函數)使用了父閉包域(外部閉包域,外部函數)的私有變量(在父閉包域中聲明的變量,父閉包域的外部空間無法直接訪問,但子閉包域可以訪問),子閉包域即當前的子函數的 function scope 會產生一個 closure 對象屬性,這個對象屬性內包含的是子閉包域對父閉包域的所有引用(只要子閉包域(內部函數)還存活,其父閉包域(外部函數)就依舊存活),倘若在父閉包域存活期間對其私有變量內容進行修改,則對這些父閉包域的私有變量進行引用的子閉包域中 function scope 的 closure 對象屬性的內容也會發生變化,因為這只是引用.

舉例:

<!DOCTYPE html>

<html lang="en">

<head>

? ?<meta charset="UTF-8">

? ?<title></title>

</head>

<body>

? ?<script type="text/javascript" charset="utf-8">

? ? ? ?//函數 a 有一個私有變量 p 和一個內部函數 innerA

? ? ? ?function a() { ? ? ? ? ? ? ? ? ? ? ?//外部閉包域 ,一個名為 a 的 Function 對象

? ? ? ? ? ?var p = 0; ? ? ? ? ? ? ? ? ? ? ?//私有變量 p

? ? ? ? ? ?var innerA = function () { ? ? ?//內部閉包域 ,一個名為 innerA 的 Function 對象

? ? ? ? ? ? ? ?console.log(p); ? ? ? ? ? ? //對外部閉包域的私有變量進行了引用,故 innerA 對象的 function scope 會產生一個名為 closure 的對象屬性,closure 對象內含有一個名為 p 的引用

? ? ? ? ? ?}

?

? ? ? ? ? ?innerA();//輸出0

? ? ? ? ? ?p++;

? ? ? ? ? ?innerA();//輸出1

? ? ? ?}

? ? ? ?a();

? ?</script>

</body>

</html>

結果如下:

第一次調用innerA

第二次調用 innerA

控制臺輸出

回到主題 面試經典問題

<!DOCTYPE html>

<html lang="en">

<head>

? ?<meta charset="UTF-8">

? ?<title></title>

? ?<script type="text/javascript">

? ? ? ?//面試經典問題:

?

? ? ? ?function onMyLoad(){

? ? ? ? ? ?/*

? ? ? ? ? ?拋出問題:

? ? ? ? ? ? ? ?此題的目的是想每次點擊對應目標時彈出對應的數字下標 0~4,但實際是無論點擊哪個目標都會彈出數字5

? ? ? ? ? ?問題所在:

? ? ? ? ? ? ? ?arr 中的每一項的 onclick 均為一個函數實例(Function 對象),這個函數實例也產生了一個閉包域,

? ? ? ? ? ? ? ?這個閉包域引用了外部閉包域的變量,其 function scope 的 closure 對象有個名為 i 的引用,

? ? ? ? ? ? ? ?外部閉包域的私有變量內容發生變化,內部閉包域得到的值自然會發生改變

? ? ? ? ? ?*/

? ? ? ? ? ?var arr = document.getElementsByTagName("p");

? ? ? ? ? ?for(var i = 0; i < arr.length;i++){

? ? ? ? ? ? ? ?arr[i].onclick = function(){

? ? ? ? ? ? ? ? ? ?alert(i);

? ? ? ? ? ? ? ?}

? ? ? ? ? ?}

? ? ? ?}

? ?</script>

</head>

<body onload="onMyLoad()">

? ?<p>產品一</p>

? ?<p>產品二</p>

? ?<p>產品三</p>

? ?<p>產品四</p>

? ?<p>產品五</p>

</body>

</html>

解決辦法:

解決辦法一

/*

解決思路:

? ?增加若干個對應的閉包域空間(這里采用的是匿名函數),專門用來存儲原先需要引用的內容(下標),不過只限于基本類型(基本類型值傳遞,對象類型引用傳遞)

*/

for(var i = 0;i<arr.length;i++){

?

? ?//聲明一個匿名函數,若傳進來的是基本類型則為值傳遞,故不會對實參產生影響,

? ?//該函數對象有一個本地私有變量arg(形參) ,該函數的 function scope 的 closure 對象屬性有兩個引用,一個是 arr,一個是 i

? ?//盡管引用 i 的值隨外部改變 ,但本地私有變量(形參) arg 不會受影響,其值在一開始被調用的時候就決定了.

? ?(function (arg) {

? ? ? ?arr[i].onclick = function () { ?//onclick函數實例的 function scope 的 closure 對象屬性有一個引用 arg,

? ? ? ? ? ?alert(arg); ? ? ? ? ? ? ? ? //只要 外部空間的 arg 不變,這里的引用值當然不會改變

? ? ? ?}

? ?})(i); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//立刻執行該匿名函數,傳遞下標 i(實參)

}

解決辦法二

/*

解決思路:

? ?將下標作為對象屬性(name:"i",value:i的值)添加到每個數組項(p對象)中

*/

for(var i = 0;i<arr.length;i++){

? ?//為當前數組項即當前 p 對象添加一個名為 i 的屬性,值為循環體的 i 變量的值,

? ?//此時當前 p 對象的 i 屬性并不是對循環體的 i 變量的引用,而是一個獨立p 對象的屬性,屬性值在聲明的時候就確定了

? ?//(基本類型的值都是存在棧中的,當有一個基本類型變量聲明其等于另一個基本變量時,此時并不是兩個基本類型變量都指向一個值,而是各自有各自的值,但值是相等的)

? ?arr[i].i = i;

? ?arr[i].onclick = function () {

? ? ? ?alert(this.i);

? ?}

}

解決辦法三

/*

解決思路:

? ?與解決辦法一有點相似但卻有點不太相似.

? ?相似點:同樣是增加若干個對應的閉包域空間用來存儲下標

? ?不同點:解決辦法一是在新增的匿名閉包空間內完成事件的綁定,而此例是將事件綁定在新增的匿名函數返回的函數上

?

? ?此時綁定的函數中的 function scope 中的 closure 對象的 引用 arg 是指向將其返回的匿名函數的私有變量 arg

*/

for(var i = 0; i<arr.length;i++){

? ?arr[i].onclick = (function(arg){

? ? ? ?return function () {

? ? ? ? ? ?alert(arg);

? ? ? ?}

? ?})(i);

}

解決辦法四

/*

解決思路與解決辦法一相同

*/

for(var i = 0; i<arr.length;i++){

? ?(function(){

? ? ? var temp = i;

? ? ? ?arr[i].onclick = function () {

? ? ? ? ? ?alert(temp);

? ? ? ?}

? ?})();

}

解決辦法五

/*

解決思路與解決辦法三及四相同

*/

for(var i = 0;i<arr.length;i++){

? ?arr[i].onclick = (function () {

? ? ? ?var temp = i;

? ? ? ?return function () {

? ? ? ? ? ?alert(temp);

? ? ? ?}

? ?})();

}

解決辦法六

/*

解決思路:

? ?將下標添加為綁定函數的屬性

*/

for(var i = 0;i<arr.length;i++){

? ?(arr[i].onclick = function () {

? ? ? ?alert(arguments.callee.i); ? ? ?//arguments 參數對象 ?arguments.callee 參數對象所屬函數

? ?}).i = i;

}

解決辦法七

/*

解決思路:

? ?通過 new 使用 Function 的構造函數 創建 Function 實例實現,由于傳入的函數體的內容是字符串,故 Function 得到的是一個字符串拷貝,而沒有得到 i 的引用(這里是先獲取 i.toString()然后與前后字符串拼接成一個新的字符串,Function 對其進行反向解析成 JS 代碼)

*/

for(var i = 0;i<arr.length;i++){

? ?arr[i].onclick = new Function("alert("+i+");");//每 new 一個 Function 得到一個 Function 對象(一個函數),有自己的閉包域

}

解決辦法八

/*

解決思路:

? ?直接通過 Function 返回一個函數

? ?與解決辦法七的不同之處在于:

? ? ? ?解決辦法七使用 new,使用了 new,此時 Function 函數就被當成構造器可以用來構造一個 Function 實例返回

? ? ? ?當前解決辦法沒有使用 new ,即將 Function 函數當成一個函數,傳入參數返回一個新函數;

? ? ? ?其實此處 new 與不 new 只是的區別在于:

? ? ? ? ? ?使用了 new 即 Function 函數充當構造器,由 JS 解析器生產一個新的對象,構造器內的 this 指向該新對象;

? ? ? ? ? ?不實用 new 即 Function 函數依舊是函數,由函數內部自己生產一個實例返回.

*/

for(var i = 0;i<arr.length;i++){

? ?arr[i].onclick = Function("alert("+i+");");

}

解決辦法九?
使用ES6新語法 let 關鍵字 由于幾新東西 各瀏覽器支持不同?
chrome 及 opera支持以下語法

<script type="application/javascript">

? ?"use strict";//使用嚴格模式,否則報錯 SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode

? ?var arr = document.getElementsByTagName("p");

? ?for(var i = 0;i<arr.length;i++){

? ? ? ?let j = i;//創建一個塊級變量

? ? ? ?arr[i].onclick = function () {

? ? ? ? ? ?alert(j);

? ? ? ?}

? ?}

</script>

在 chrome 查看

可以在控制臺看到 j 變量是一個 block 級的變量

待函數綁定完成后看數組項:

此時的該數組項的<function scope>的 Block 域有個 j 存儲的就是對應的數組下標?
firefox支持一下語法

<script type="application/javascript;version=1.7">

? ?var arr = document.getElementsByTagName("p");

? ?for(var i = 0;i<arr.length;i++){

? ? ? ?let j = i;

? ? ? ?arr[i].onclick = function () {

? ? ? ? ? ?alert(j);

? ? ? ?}

? ?}

</script>

由于新語法各大廠商的支持尚未規范故暫不不推薦使用

解決辦法大同小異,只要理解其中的實質,可以寫出多多的解決辦法

?

點擊閱讀全文,查看更多

轉載于:https://www.cnblogs.com/meinan588/p/6401978.html

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

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

相關文章

bzoj 2296: 【POJ Challenge】隨機種子

Time Limit: 1 Sec Memory Limit: 128 MBSec Special JudgeDescription1tthinking除了隨機算法&#xff0c;其他什么都不會。但是他還是可以ac很多題目&#xff0c;他用的是什么呢&#xff1f;他會選擇一個好的隨機種子&#xff0c;然后輸出答案。往往他選擇的一個好的種子可…

英特爾第十代處理器為什么不支持win7_5GHz動力澎湃 高主頻多核處理器成就巔峰玩家...

頻率之爭永遠是處理器領域無法回避的話題。高主頻在游戲中所帶來的高速運行&#xff0c;穩定幀數等特性永遠是玩家們所追求的目標。隨著英特爾第十代桌面及移動版酷睿處理器的發布&#xff0c;無論是臺式整機或是筆記本平臺&#xff0c;都已全面進入了5GHz時代。選擇英特爾處理…

leetcode46. 全排列(回溯)

給定一個 沒有重復 數字的序列&#xff0c;返回其所有可能的全排列。 示例: 輸入: [1,2,3] 輸出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ] 代碼 class Solution {List<List<Integer>> cListnew ArrayList<>();public List<List<…

初級算法-12.反轉字符串

題目描述: 編寫一個函數&#xff0c;其作用是將輸入的字符串反轉過來。輸入字符串以字符數組 char[] 的形式給出。 不要給另外的數組分配額外的空間&#xff0c;你必須原地修改輸入數組、使用 O(1) 的額外空間解決這一問題。 你可以假設數組中的所有字符都是 ASCII 碼表中的可打…

linux python源碼目錄結構,TensorFlow0.8源碼閱讀 -- 代碼目錄結構講解

TensorFlow0.8發布以來受到了大量機器學習領域愛好者的關注&#xff0c;目前其項目在github上的follow人數在同類項目中排名第一。作為google的第一個開源項目&#xff0c;TensorFlow的源碼結構較為清晰&#xff0c;相關的代碼注釋覆蓋較全。本文首先從代碼結構入手&#xff0c…

在VirtualBox里復制VDI文件[轉]

原文地址:http://blog.sina.com.cn/s/blog_591a2c940100aree.html 在VirtualBox的快速修復界面里&#xff0c;可以隨時生成當前狀態的備份。當生成了備份之后&#xff0c;會在Snapshots目錄下創建一個新的VDI文件&#xff0c;之后對當前狀態所做的一切操作都將針對最新的VDI文件…

軟件開發重要性_在軟件開發中考慮時間的重要性

軟件開發重要性by Crunch Tech通過Crunch Tech 在軟件開發中考慮時間的重要性 (The importance of time to think in Software Development) Modern Technology teams operate in a fast-paced environment. With a Technology team of only 35 people, we average over 50 re…

自動登錄360,百度

方便登錄&#xff0c;寫的小工具 1 import win.ui;2 import web.ui;3 /*DSG{{*/4 var winform ..win.form(text"AAuto Form";right599;bottom399)5 winform.add(6 button{cls"button";text"百度";left41;top25;right163;bottom59;z1};7 button2…

arm linux 開機電路_【技術角度看問題之一】ARM到底是個啥?

【小宅按】近期公司推出來基于ARM芯片的服務器&#xff0c;本文就一些基本概念&#xff0c;比如ARM&#xff0c; ARM64, ARMv8, ARM7&#xff0c;ARMv7, 64位等讓人費解的概念進行了粗淺地分析&#xff0c;涉及的關鍵字已用粗體標出。文中觀點僅僅是一家之言&#xff0c;拙劣之…

leetcode77. 組合(回溯)

給定兩個整數 n 和 k&#xff0c;返回 1 … n 中所有可能的 k 個數的組合。 示例: 輸入: n 4, k 2 輸出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ] 代碼 class Solution {List<List<Integer>> cListnew ArrayList<>();public List<List<I…

linux系統配置腳本,Linux系統配置腳本開機自啟

在日常使用過程中&#xff0c;當有時候需要運行開機自啟腳本&#xff0c;同時想看到輸出結果的時候&#xff0c;不免就需要將系統配置為免密登錄模式&#xff0c;本文主要介紹再init3模式(多用戶命令行界面)&#xff0c;運行開機自啟腳本及輸出腳本指令運行數據結果。本文使用的…

WPF多線程UI更新

前言 在WPF中&#xff0c;在使用多線程在后臺進行計算限制的異步操作的時候&#xff0c;如果在后臺線程中對UI進行了修改&#xff0c;則會出現一個錯誤&#xff1a;&#xff08;調用線程無法訪問此對象&#xff0c;因為另一個線程擁有該對象。&#xff09;這是很常見的一個錯誤…

iOS------App之間傳遞數據的幾種方式

UIDocumentInteractionController UIActivityViewController Shared Keychain Access Custom URL Scheme Web Service iCloud API UIPasteboard 參考 http://enharmonichq.com/sharing-data-locally-between-ios-apps/http://stackoverflow.com/questions/9425706/share-data-b…

了解如何使用Vue.js CLI

Interested in learning Vue.js? Get my ebook at vuehandbook.com有興趣學習Vue.js嗎&#xff1f; 在vuehandbook.com上獲取我的電子書 One of them is the Vue Command Line Interface (CLI).其中之一是Vue命令行界面(CLI)。 Note: There is a huge rework of the CLI goin…

rabbitmq學習——隊列

public class Send { public static final String routingKey "wuqidi_task_durable"; /*工作隊列 也叫任務隊列 目的是將任務發送到隊列中 由工作者進行處理 在后臺的多個工作者中 任務是共享的*/ public static void main(String[] args) throws Exception{ Conne…

python 戰艦_簡單Python戰艦

我最近開始學習python&#xff0c;并決定嘗試制作我的第一個項目。我正在嘗試做一個戰艦游戲&#xff0c;隨機放置兩個3塊長的船在一塊板上。但效果不太好。我為2號飛船做了一個臨時的循環&#xff0c;它應該檢查一下旁邊的兩個空間是否空閑&#xff0c;然后在那里建立自己的空…

leetcode面試題 08.12. 八皇后(回溯)

設計一種算法&#xff0c;打印 N 皇后在 N N 棋盤上的各種擺法&#xff0c;其中每個皇后都不同行、不同列&#xff0c;也不在對角線上。這里的“對角線”指的是所有的對角線&#xff0c;不只是平分整個棋盤的那兩條對角線。 注意&#xff1a;本題相對原題做了擴展 示例: 輸…

linux 進入redis 數據庫,Linux下Redis數據庫的安裝方法與自動啟動腳本分享

安裝Redis(1) 下載Rediswget http://redis.googlecode.com/files/redis-2.2.11.tar.gztar xzvf redis-2.2.11.tar.gz(2) 編譯并安裝Redismake && make install(3) 復制并修改配置文件cp redis.conf /etc/redis.confvi /etc/redis.conf注意修改以下幾項&#xff1a;daem…

Flutter 36: 圖解自定義 View 之 Canvas (三)

小菜繼續學習 Canvas 的相關方法&#xff1a; drawVertices 繪制頂點 小菜上次沒有整理 drawVertices 的繪制方法&#xff0c;這次補上&#xff1b;Vertice 即頂點&#xff0c;通過繪制多個頂點&#xff0c;在進行連線&#xff0c;多用于 3D 模型中&#xff1b; drawVertices 包…

sphinx 項目根目錄_如何使用Sphinx工具記錄Django項目

sphinx 項目根目錄I recently visited a company where I had a nice talk with one of its employees. We talked about technology and programming. Then we touched the subject of project documentation. Specifically how React does it automatically but Django doesn…