1.了解函數提升
使用var關鍵字聲明的變量在JAVAScript中會被提升,并在內存中分配值undefined。 但初始化恰發生在你給變量賦值的地方。 另外,var聲明的變量是函數作用域的,而let和const是塊作用域的。
2.什么是暫時死區TDZ?
let和const聲明可以讓變量在其作用域上受限于它所使用的塊、語句或表達式。與var不同的是,這些變量沒有被提升,并且有一個所謂的暫時死區(TDZ)。試圖訪問TDZ中的這些變量將引發ReferenceError,因為只有在執行到達聲明時才能訪問它們。
下表概述了與JavaScript中使用的不同關鍵字聲明的變量對應的提升行為和使用域:
四個選項:關鍵字,提升,作用域,創建全局屬性。
3.如何保存 i 的指向?
在for循環的頭部聲明帶有var關鍵字的變量會為該變量創建單個綁定(存儲空間)。
如果使用 let 聲明一個具有塊級作用域的變量,則為每個循環迭代創建一個新的綁定。
解決這個問題的另一種方法是使用閉包。
4.是否存在堆棧溢出?
JavaScript并發模型基于“事件循環”。 瀏覽器是執行JS代碼提供運行時環境之一。
瀏覽器的主要組件包括調用堆棧,事件循環**,任務隊列和Web API**。 像setTimeout,setInterval和Promise這樣的全局函數不是JavaScript的一部分,而是 Web API 的一部分。 JavaScript 環境的可視化形式如下所示:
JS調用棧是后進先出(LIFO)的。引擎每次從堆棧stack中取出一個函數,然后從上到下依次運行代碼。每當它遇到一些異步代碼,如setTimeout,就把它交給Web API(箭頭1)。因此,每當 事件 被觸發時,callback 都會被發送到任務隊列(箭頭2)。
事件循環(Event loop)不斷地監視任務隊列(Task Queue),并按它們排隊的順序一次處理一個回調。每當調用 堆棧(call stack) 為空時,Event loop獲取回調并將其放入堆棧(stack )(箭頭3)中進行處理。請記住,如果調用堆棧不是空的,則事件循環不會將任何回調推入堆棧。
現在,有了這些知識,讓我們來回答前面提到的問題:
步驟:
- 調用 foo()會將foo函數放入調用堆棧(call stack)。
- 在處理內部代碼時,JS引擎遇到 setTimeout。
- 然后將foo回調函數傳遞給WebAPIs(箭頭1)并從函數返回,調用堆棧再次為空
- 計時器被設置為0,因此foo將被發送到任務隊列(箭頭2)。
- 由于調用堆棧是空的,事件循環將選擇foo回調并將其推入調用堆棧進行處理。
- 進程再次重復,堆棧不會溢出。
5.執行foo函數,頁面是否卡死(卡頓)?
必須會卡死!
大多數時候,開發人員假設在事件循環圖中只有一個任務隊列。但事實并非如此,我們可以有多個任務隊列。由瀏覽器選擇其中的一個隊列并在該隊列中處理回調。
在底層來看,JavaScript中有宏任務和微任務。setTimeout回調是宏任務,而Promise回調是微任務。
主要的區別在于他們的執行方式。宏任務在單個循環周期中一次一個地推入堆棧,但是微任務隊列總是在執行后返回到事件循環之前清空。因此,如果你以處理條目的速度向這個隊列添加條目,那么你就永遠在處理微任務。只有當微任務隊列為空時,事件循環才會重新渲染頁面。每次調用'foo'都會繼續在微任務隊列上添加另一個'foo'回調,因此事件循環無法繼續處理其他事件(滾動,單擊等),直到該隊列完全清空為止。 因此,它會阻止渲染。
6.如何使 obj 展開運算而不導致類型錯誤?
var obj = { x: 1, y: 2, z: 3 }; [...obj]; // TypeError
展開語法 和 for-of 語句遍歷 iterable對象 定義要遍歷的數據。Array 或 Map 是具有默認迭代行為的內置迭代器。對象不是可迭代的,但是可以通過使用iterable和iterator協議使它們可迭代。
在Mozilla文檔中,如果一個對象實現了@@iterator方法,那么它就是可迭代的,這意味著這個對象(或者它原型鏈上的一個對象)必須有一個帶有@@iterator鍵的屬性,這個鍵可以通過常量Symbol.iterator獲得。
上述語句可能看起來有點冗長,但是下面的示例將更有意義:
還可以使用 generator 函數來定制對象的迭代行為:
7.打印的結果是什么
知識點:
for-in循環遍歷對象本身的可枚舉屬性以及對象從其原型繼承的屬性。 可枚舉屬性是可以在for-in循環期間包含和訪問的屬性。
現在你已經掌握了這些知識,應該很容易理解為什么我們的代碼要打印這些特定的屬性:
8.this 始終指向調用方法的對象
在全局范圍內初始化x時,它成為window對象的屬性(不是嚴格的模式)。看看下面的代碼:
可以斷言:
window.x === 10; // true
this 始終指向調用方法的對象。因此,在foo.getx()的例子中,它指向foo對象,返回90的值。而在xGetter()的情況下,this指向 window對象, 返回 window 中的x的值,即10。
要獲取 foo.x的值,可以通過使用 Function.prototype.bind 將this的值綁定到foo對象來創建新函數。
let getFooX = foo.getX.bind(foo); getFooX(); // 90