摘要:所以經常看到的說閉包就是綁定了上下文環境的函數。我更偏向于閉包是一個函數和聲明該函數的詞法環境的組合。里面的閉包先上一個閉包該例子的解釋上面的代碼,在函數里面定義的函數和這個函數聲明的詞法環境就形成了一個閉包。
閉包是什么
第一種說法:閉包創建一個詞法作用域,這個作用域里面的變量被引用之后可以在這個詞法作用域外面被自由訪問,是一個函數和聲明該函數的詞法環境的組合
第二種說法:閉包就是引用了自由變量的函數,這個自由變量與函數一同存在,即使脫離了創建它的環境。所以經常看到的說閉包就是綁定了上下文環境的函數。
我更偏向于閉包是一個函數和聲明該函數的詞法環境的組合。
JS里面的閉包 先上一個閉包function sayHello(name){ let str = "Hello,${name}"; function say(){ console.log(str); } return say; } let myHello = sayHello("abby"); myHello();該例子的解釋
上面的代碼,在sayHello函數里面定義的say函數和這個函數聲明的詞法環境就形成了一個閉包。say函數引用了sayHello函數里面定義的一個變量str,并且sayHello函數將say這個函數return了出去,這樣,在sayHello函數的外面也能訪問到它詞法作用域里面的變量str,最后就像say這個函數和str這個變量綁定了一樣
為什么在外部還能訪問到變量str呢?在一些語言中,一般認為函數的局部變量只在函數的執行期間可以訪問
當上段代碼在執行到let myHello = sayHello("abby");這段代碼的時候,按理會銷毀掉sayHello這個函數的執行環境,但是在這里卻沒有,因為,sayHello這個函數返回的是一個函數,這個函數里面的str引用了外部的變量str,如果銷毀了sayHello的執行環境就會找不到了,所以,sayHello的執行環境會一直在內存中,所以也就會有閉包會增加內存開銷的說法
體會在JavaScript語言中,只有函數內部的子函數才能讀取內部變量,可以把閉包簡單理解成“定義在一個函數內部的函數”
閉包就是將函數內部和函數外部連接起來的一座橋梁
閉包的用處 1、讀取函數內部的變量 2、讓這些變量始終保持在內存中function createIncrementor(start) { return function () { return start++; }; } var inc = createIncrementor(5); inc() // 5 inc() // 6 inc() // 7
start是函數createIncrementor的內部變量,通過閉包,start的狀態被保留了,每一次調用都是在上一次調用的基礎上進行計算,閉包inc使得函數createIncrementor的內部環境一直存在,因為inc始終存在內存中,而inc的存在依賴于createIncrementor,因此該函數不會在調用結束后,被垃圾回收機制回收
3、封裝對象的私有屬性和私有方法function Person(name) { var _age; function setAge(n) { _age = n; } function getAge() { return _age; } return { name: name, getAge: getAge, setAge: setAge }; } var p1 = Person("xiaoming"); p1.setAge(25); pa.getAge(); //25
函數Person的內部變量_age,通過閉包getAge和setAge,變成了返回對象p1的私有變量,外層函數每次運行,都會產生一個新的閉包,而這個閉包又會保留外層函數的內部變量,內存也就消耗較多
在舉幾個例子1、常見的閉包都是return出來一個函數,但并不是說明,閉包一定需要return一個函數,return一個函數也只是為了能在作用域范圍之外訪問一個變量
let say; function sayHello(name){ let str = "Hello,${name}"; say = function(){ console.log(str); } } let myHello = sayHello("abby"); say();
2、同一個調用函數生成同一個閉包環境,在里面聲明的所有函數同時具有這個環境里面的變量的引用
let get,up,down function setUp(){ let number = 20; get = function(){ console.log(number); } up = function(){ number += 3; } down = function(){ number -= 2; } } setUp(); get(); up(); down(); get();
3、每一個調用函數都會創建不同的閉包環境,里面的變量互不影響
function newClosure(){ let array = [1,2]; return function(num){ array.push(num); console.log("array:${array}"); } } let myClosure = newClosure(); let yourClosure = newClosure(); myClosure(3); yourClosure(4); myClosure(5);
4、在循環里面創建閉包
function newClosure(){ for(var i=0;i<5;i++){ setTimeout(function(){ console.log(i); }); } } newClosure();//5個5
改進方法一:創建一個新的閉包對象,這樣每個閉包對象里面的變量就互不影響
function log(i){ return function(){ console.log(i); } } function newClosure(){ for(var i=0;i<5;i++){ setTimeout(log(i)); } } newClosure();
每次log(i)都會創建不同的閉包對象,所有的回調函數不會指向同一個環境
改進方法二:使用自執行函數,外部的匿名函數會立即執行,并且把i作為它的參數,此時函數內變量e就擁有了i的一個拷貝。當傳遞給setTimeout的匿名函數執行時,它就擁有了對e的引用,而這個值是不會被循環改變的
function newClosure(){ for(var i=0;i<5;i++){ (function(e){ setTimeout(function(){ console.log(e); }); })(i) } } newClosure();
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/108005.html
摘要:一前言本文章將以報表下載為例,給大家介紹三種文件下載的方式。通過二進制數據流的方式下載這種方式是我目前采用的方式,用于處理報表下載。缺點對于數據量不大的文件,這種方式是可行的。 一、前言 本文章將以excel報表下載為例,給大家介紹三種文件下載的方式。 原文地址:簡談文件下載的三種方式 | Rychou 二、正文 1. 通過服務器文件地址下載 這是最常見的文件下載方式,大多數網站的音頻...
摘要:一前言本文章將以報表下載為例,給大家介紹三種文件下載的方式。通過二進制數據流的方式下載這種方式是我目前采用的方式,用于處理報表下載。缺點對于數據量不大的文件,這種方式是可行的。 一、前言 本文章將以excel報表下載為例,給大家介紹三種文件下載的方式。 原文地址:簡談文件下載的三種方式 | Rychou 二、正文 1. 通過服務器文件地址下載 這是最常見的文件下載方式,大多數網站的音頻...
摘要:常量接口是對接口的一種不良使用。如果這些常量最好被看作是枚舉類型成員,那就應該用枚舉類型來導出。因為客戶端既不能創建枚舉類型的實例,也不能對它進行擴展,因此很可能沒有實例,而只有聲明過的枚舉常量。換句話說,枚舉類型是實例受控的。 問題 我們偶爾能在項目中看到如下風格的代碼: public class ResponseCode { public static final int ...
摘要:而用關鍵字調用構造器,總是會創建一個新的對象,無論內容是否相同。中對象的哈希碼被頻繁地使用比如在等容器中。字符串不變性保證了碼的唯一性因此可以放心地進行緩存。對于所有包含方式新建對象包括的連接表達式,它所產生的新對象都不會被加入字符串池中。 前言 前陣子和同事在吃飯時聊起Java的String,覺得自己之前的筆記寫的略顯零散。故此又重新整理了一下。 String在Java中算是一個有意...
閱讀 3371·2021-11-22 09:34
閱讀 2857·2021-10-09 09:43
閱讀 1445·2021-09-24 09:47
閱讀 2199·2019-08-30 12:53
閱讀 997·2019-08-29 14:00
閱讀 3356·2019-08-29 13:17
閱讀 2268·2019-08-28 18:00
閱讀 1284·2019-08-26 12:00