填坑-十萬個爲何?(13)

簡介:不少概念不清或忘記,從新構建本身的知識體系。天天問本身1~多個問題。我是菜鳥 成爲大神之路!javascript

1. 經典面試題 for(var i=0;i<=3;i++){ setTimeout(function() { console.log(i) }, 10);}打印結果?分析?

for(var i = 0; i < 10; i++) {
	console.log(new Date(),i);
    setTimeout(() => {
        console.log(new Date(),i)
    }, 1000);//一秒
}
答案:打印1010

這道題涉及了異步、做用域、閉包

😀settimeout是異步執行,1ms後往任務隊列裏面添加一個任務,只有主線上的所有執行完,纔會執行任務隊列裏的任務,
當主線執行完成後,i是10,因此此時再去執行任務隊列裏的任務時,i所有是10了。

😁對於打印10次是:每一次for循環的時候,settimeout都執行一次,可是裏面的函數沒有被執行,而是被放到了任務隊列裏面,
等待執行,for循環了10次,就放了10次,當主線程執行完成後,才進入任務隊列裏面執行。
(注意:for循環從開始到結束的過程,須要維持幾微秒或幾毫秒。)
複製代碼

① 若要輸出從0到9,將 var 改成 let
for(let i = 0; i < 10; i++) {
	console.log(new Date(),i);
    setTimeout(() => {
        console.log(new Date(),i)
    }, 1000);//一秒
}

當我把var 變成let 時
😂打印出的是:0~10當解決變量做用域,由於for循環頭部的let不只將i綁定到for循環快中,
事實上它將其從新綁定到循環體的每一次迭代中,確保上一次迭代結束的值從新被賦值。

😂setTimeout裏面的function()屬於一個新的域,經過 var 定義的變量是沒法傳入到這個函數執行域中的, 經過使用 let 來聲明【塊變量】,這時候變量就能做用於這個塊,因此 function就能使用 i 這個變量了; 😂這個匿名函數的參數做用域 和 for參數的做用域不同,是利用了這一點來完成的。 這個匿名函數的做用域有點相似類的屬性,是能夠被內層方法使用的。 複製代碼
② 若要輸出從0到9,改寫爲閉包

閉包相關解釋下一問。html

// 使用閉包
for(var i = 0; i < 10; i++) {
    console.log(new Date(),i);
    (function (i) {
        setTimeout(() => {
            console.log(new Date(),i);
        }, 1000);
    })(i);
}
複製代碼

2.閉包的做用域?代碼執行過程?

常常遇到閉包的相關問題java

代碼一:
// 以1問中例子爲例
for(var i = 0; i < 10; i++) {
    console.log(new Date(),i);
    (function (i) {
        setTimeout(() => {
            console.log(new Date(),i);
        }, 1000);
    })(i);
}
代碼一是閉包,寫爲代碼二不爲閉包的形式,(function(i){})(i) '理解爲自執行函數' 自執行函數的相關參考在12天和參考文章中,以後我會對着一塊內容進行學習👍。

代碼二:
var fun = function(x){
	setTimeout(() => {
            console.log(new Date(),x);
        }, 1000);
	}

for(var i = 0; i < 10; i++) {
    console.log(new Date(),i);
    fun(i);
}
複製代碼
JavaScript 變量能夠是局部變量或全局變量。
私有變量能夠用到閉包。

什麼是閉包:
> 就是函數的局部變量集合,只是這些局部變量在函數返回後會繼續存在。
> 就是函數的「堆棧」在函數返回後並不釋放,咱們也能夠理解爲這些函數堆棧並不在棧上分配而是在堆上分配
> 當在一個函數內定義另一個函數就會產生閉包
> "注:變量聲明時若是不使用 var 關鍵字,那麼它就是一個全局變量,即使它在函數內定義。"

注意:一般人們對閉包的理解是不徹底的,認爲在 JavaScript 中只有嵌入的函數纔是閉包。但其實任何擁有 free variable(自由變量)
的函數都是以閉包的形式存在的。由於本質上,閉包是 free variable 問題的一種解決方案。http://liximomo.github.io/javascript-closure

閉包的注意點
1)因爲閉包會使得函數中的變量都被保存在內存中,內存消耗很大,因此不能濫用閉包,不然會形成網頁的性能問題,在IE中可能致使內存泄露。
解決方法是,在退出函數以前,將不使用的局部變量所有刪除。
2)閉包會在父函數外部,改變父函數內部變量的值。因此,若是你把父函數看成對象(object)使用,把閉包看成它的公用方法(Public Method),
把內部變量看成它的私有屬性(private value),這時必定要當心,不要隨便改變父函數內部變量的值。
複製代碼

閉包須要注意的兩種狀況----函數做爲返回值,函數做爲參數傳遞。git

例子:函數做爲返回值github

1. function greeting(name) {
2. 		var text = 'Hello '; // local variable
 		// 每次調用時,產生閉包,並返回內部函數對象給調用者
3. 		return function() {return text += name; }
 	}
 	
4. 	var sayHello=greeting("Closure");
 	
5. 	document.write(sayHello());
6. 	document.write("<br>");
7. 	document.write(sayHello());
8. 	document.write("<br>");
9. 	document.write(sayHello());
10. document.write("<br>");
11. document.write(sayHello());
12. document.write("<br>");
13. document.write(sayHello());// 經過閉包訪問到了局部變量text
	
代碼解析:
> 執行第4行時第一行的函數運行一次,返回內嵌函數引用做爲第4行sayHello變量的值。
> 以後的第5到13行執行的函數操做其實只是執行sayHello指向的函數(即返回的內嵌函數)。
> 解釋text爲何能自增,閉包的概念。

複製代碼

例子:函數做爲參數傳遞面試

1. var num = 10;

2. var fun = function(var num){
3.    console.log(num);
   }
4. !function(f){
5.     var num = 100;
6.     f(num);
7. }(fun)

代碼解析:
> 第四行'!'表示高優先級,被'!'標註的先執行。
> 第7行將fun指向的引用2行函數傳給參數f在函數中執行f(?)函數,此時log出來的值是10。
> 解釋看js做用域
複製代碼

3.var let const的區別?

① 什麼是var的變量提高補充2019年1月5日23:31:41 來源網易課堂
'代碼1'
var a = 10;
function foo(){
    console.log(a);//打印:10
}
'代碼2'
var a = 10;
function foo(){
    console.log(a);//打印:undefined
    var a = 5;
}
'問代碼2爲何輸出的是undefined這裏變量提早了,實際代碼至關於以下'
'代碼3'
var a = 10;
function foo(){
    var a;
    console.log(a);//打印:undefined
    a = 5;
}
複製代碼
② let 聲明的變量只在它所在的代碼塊有效
function demo(){
  {
    var a = 12;
    let c = 10;
    console.log(a);//這裏會打印出12;
    console.log(c);//這裏會打印出10;
  }
  console.log(a);//這裏會打印出12
  console.log(c);//這裏打印出的內容爲 "c is not defined";
  //說明聲明的c變量只在{}代碼塊中可以訪問,其餘地方都訪問不到;
}
demo();
複製代碼
③ let不存在變量提高

let 不像var 那樣會發生 '變量提高' 現象,所以,變量須要先聲明而後再使用,不然報錯;bash

// var 的狀況
console.log(vardata);  // undefined
var vardata = 2;

// let的狀況;
console.log(letdata);  // letdata is not defined 報錯
let letdata = 2;
複製代碼
④ let暫時性死區

快級做用域內存在let命令,它所聲明的變量就綁定在這個區域,再也不受外部影響;閉包

var tmp = 123;
if (true) {
  tmp = 'abc';
  let tmp;
  console.log(tmp); // tmp is not defined
}
複製代碼
⑤ let不容許重複聲明

let 不容許在相同做用域內,重複聲明同一個變量。less

function foo() {
  let v = 10;
  var v = 1;//Identifier 'v' has already been declared
  console.log(v);
}
foo();

function foo1() {
  let v1 = 10;
  let v1 = 1;//Identifier 'v1' has already been declared
  console.log(v1);
}
foo1();
複製代碼
⑥ const 聲明一個只讀的常量,一旦聲明,常量的值就不容許改變
⑦ const 一旦聲明瞭變量,就必須初始化,不能留到之後賦值。若是使用const聲明一個變量,可是不賦值,也會報錯
⑧ const 的做用域與let命令相同;只在聲明所在的塊級做用域內有效。
⑨ const 不可重複聲明 (和let同樣)

參考文章:
let的含義及let與var的區別
閉包文檔
博客-深刻理解javascript原型和閉包
JS關於閉包
函數 + 函數建立時的環境 = 閉包異步

相關文章
相關標籤/搜索