日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

用9种办法解决 JS 闭包经典面试题之 for 循环取 i

發布時間:2023/11/30 javascript 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 用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

總結

以上是生活随笔為你收集整理的用9种办法解决 JS 闭包经典面试题之 for 循环取 i的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。