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這個變量綁定了同樣閉包
let myHello = sayHello('abby');
這段代碼的時候,按理會銷燬掉sayHello這個函數的執行環境,可是在這裏卻沒有,由於,sayHello這個函數返回的是一個函數,這個函數裏面的str引用了外部的變量str,若是銷燬了sayHello的執行環境就會找不到了,因此,sayHello的執行環境會一直在內存中,因此也就會有閉包會增長內存開銷的說法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,所以該函數不會在調用結束後,被垃圾回收機制回收函數
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的私有變量,外層函數每次運行,都會產生一個新的閉包,而這個閉包又會保留外層函數的內部變量,內存也就消耗較多code
一、常見的閉包都是return出來一個函數,但並非說明,閉包必定須要return一個函數,return一個函數也只是爲了能在做用域範圍以外訪問一個變量對象
let say; function sayHello(name){ let str = 'Hello,${name}'; say = function(){ console.log(str); } } let myHello = sayHello('abby'); say();
二、同一個調用函數生成同一個閉包環境,在裏面聲明的全部函數同時具備這個環境裏面的變量的引用ip
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();
三、每個調用函數都會建立不一樣的閉包環境,裏面的變量互不影響內存
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);
四、在循環裏面建立閉包作用域
function newClosure(){ for(var i=0;i<5;i++){ setTimeout(function(){ console.log(i); }); } } newClosure();//5個5
改進方法一:建立一個新的閉包對象,這樣每一個閉包對象裏面的變量就互不影響rem
function log(i){ return function(){ console.log(i); } } function newClosure(){ for(var i=0;i<5;i++){ setTimeout(log(i)); } } newClosure();
每次log(i)都會建立不一樣的閉包對象,全部的回調函數不會指向同一個環境get
改進方法二:使用自執行函數,外部的匿名函數會當即執行,而且把i做爲它的參數,此時函數內變量e就擁有了i的一個拷貝。當傳遞給setTimeout的匿名函數執行時,它就擁有了對e的引用,而這個值是不會被循環改變的回調函數
function newClosure(){ for(var i=0;i<5;i++){ (function(e){ setTimeout(function(){ console.log(e); }); })(i) } } newClosure();