JavaScript面試題看這一篇就夠了,簡單全面一發入魂(持續更新 step1)
目錄

1、BOM、DOM有什么區別?
(1)DOM
(2)BOM
2、javaScript中代碼后加不加分號;有什么區別?
3、var與let、const有什么區別?
4、原始值和引用值有什么區別?
5、什么是執行上下文?
6、解釋一下javaScript垃圾回收?
7、如何判斷一個對象是不是數組?
8、Object和Map到底有什么區別?
(1)內存占用
(2)插入性能
(3)查找速度
(4)刪除性能
9、簡述一下什么是Set?
(1)基礎API
(2)順序與迭代
10、什么是迭代器?
11、什么是生成器?
12、JavaScript的繼承是通過什么方式實現的?(問法2:什么是原型鏈?問法3:構造函數、原型、實例三者的關系?)
13、以下表達式,對嗎?為什么?
14、apply()和call()有什么區別?
15、函數聲明和函數表達式有什么區別?
1、BOM、DOM有什么區別?
(1)DOM
文檔對象模型(DOM,Document Object Model)是一個應用編程接口(API),用于在HTML中使用擴展的HTML。DOM將整個頁面抽象為一組分層節點。
DOM通過創建表示文檔的樹,讓開發者可以隨心所欲的控制網頁的內容和結構。使用DOM API可以輕松地刪除、添加、替換、修改節點。
對瀏覽器而言,DOM就是使用ECMAScript實現的,如今已經成為JavaScript語言的一大組成部分。
言而言之,DOM提供與網頁內容交互的方法和接口。
(2)BOM
IE3和Netscape Navigator3提供了瀏覽器對象模型(BOM)API,用于支持訪問和操作瀏覽器的窗口。使用BOM,開發者可以操控瀏覽器顯示頁面之外的部分。
BOM的能力展示:
彈出新瀏覽器窗口的能力;
移動、縮放和關閉瀏覽器窗口的能力;
navigator對象,提供關于瀏覽器的詳盡信息;
location對象,提供瀏覽器加載頁面的詳盡信息;
screen對象,提供關于用戶屏幕分辨率的詳盡信息;
performance對象,提供瀏覽器內存占用、導航行為和時間統計的詳盡信息;
對cookie的支持;
其它自定義對象,如XMLHttpRequest和IE的ActiveXObject。
簡而言之,BOM提供與瀏覽器交互的方法和接口。
2、JavaScript中代碼后加不加分號;有什么區別?
加分號有助于防止省略造成的問題
避免輸入內容不完整
便于開發者通過刪除空行來壓縮代碼(如果沒有結尾的分號,只刪除空行,則會導致語法錯誤)
加分號有助于提升性性能,因為解析器會嘗試在合適的位置補上分號以糾正語法錯誤。
3、var與let、const有什么區別?
let不具備聲明提升,var具備聲明提升
let聲明的范圍是塊作用域,而var聲明的范圍是函數作用域。
let是ES6才引入的聲明關鍵字
for循環中的let聲明
const與let很相似,最大的區別是const必須初始化,且不能再次賦值。
結語:
不使用var,有了let和const,大多數開發者會發現自己不再需要var了,限制自己只使用let和const,有助于提升代碼質量,因為變量有了明確的作用域、聲明位置、以及不變的值。
const優先,let次之。使用const聲明可以讓瀏覽器運行時強制保持變量不變,也可以讓靜態代碼分析工具提前發現不合法的賦值操作。
4、原始值和引用值有什么區別?
原始值大小固定,保存在棧內存中
從一個變量到另一個變量復制原始值,會創建該值得第二個副本
引用值是對象,存儲在堆內存中
包含引用值的變量實際上只包含指向相應對象的一個指針,而不是對象本身
從一個變量到另一個變量復制引用值,只會復制指針,因此結果是兩個變量都指向同一個對象
typeof操作費可以確定值的原始類型,instanceof操作符用于確保值得引用類型
5、什么是執行上下文?
任何變量都存在于某個執行上下文中(也稱為作用域)。這個上下文(作用域)決定了變量的生命周期,以及它們可以訪問代碼的哪些部分。
執行上下文可以總結如下:
執行上下文分為 ① 全局上下文、② 函數上下文、③ 塊級上下文
代碼執行流每進入一個新上下文,都會創建一個作用域鏈,用于搜索變量和函數
函數或塊的局部上下文不僅可以訪問自己作用域內的變量,也可以訪問任何包含上下文乃至全局上下文的變量
全局上下文只能訪問全局上下文中的變量和函數,不能直接訪問局部上下文中的任何數據
變量的執行上下文用于確定什么時候釋放內存
6、解釋一下JavaScript垃圾回收?
JavaScript是使用垃圾回收的編程語言,開發者不需要操心內存分配和回收。
離開作用域的值會被自動標記為可回收,然后在垃圾回收期間被刪除
主流的垃圾回收算法是標記算法,即先給當前不使用的值加上標記,再回來回收它們的內存
引用計數是另一種垃圾回收策略,需要記錄值被引用了多少次。JavaScript引擎不再使用這種算法,但某些舊版本的IE仍然會受這種算法的影響,原因是JavaScript會訪問非原生JavaScript對象(如DOM對象)。
引用計數在代碼中循環引用時會出現問題
解除變量的引用不僅可以消除循環引用,而且對垃圾回收也有幫助。為促進內存回收,全局對象、全局對象的屬性和循環引用都應該在不需要時解除引用。
7、如何判斷一個對象是不是數組?
在只有一個全局作用域的時候,使用instanceof操作符就足矣:
if(value instanceof Array){ //操作數組 }
1
2
3
使用instanceof的前提是只有一個全局執行上下文,如果網頁里有多個框架,則可能涉及兩個不同的全局上下文,因此就會有兩個不同版本的Array構造函數。如果要把數組從一個框架傳到另一個框架,則這個數組的構造函數將有別于第二個框架內本地創建的數組。
為了解決這個問題,ECMAScript提供了 Array.isArray()方法。這個方法的目的就是確定一個值是否為數組,而不用管它是在哪個全局執行上下文中創建的。
if(Array.isArray(value)){ //操作數組 }
1
2
3
8、Object和Map到底有什么區別?
(1)內存占用
給定固定大小內存的情況下,Map一般會比Object多存儲50%的鍵值對。
(2)插入性能
插入Map一般會稍微快一點。
(3)查找速度
相差無幾。
(4)刪除性能
Map的刪除性能完勝Object。
綜上四點,選擇Map顯然是更好地選擇。
9、簡述一下什么是Set?
(1)基礎API
添加add()
查詢has()
獲取數量size
刪除delete()
清空clear()
(2)順序與迭代
Set會維護值插入時的順序,因此支持按順序迭代。
集合實例可以提供一個迭代器Iterator,能以插入順序生成集合內容??梢酝ㄟ^values()方法及其別名方法keys(),或者Symbol.iterator屬性,他引用values(),取得這個迭代器。
const s = new Set("哪吒","云韻","比比東"); alert(s.keys === s[Symbol.iterator]);//true alert(s.values === s[Symbol.iterator]);//true for(let value of s.values()){ } for(let value of s[Symbol.iterator]){ }
1
2
3
4
5
6
7
8
9
10
11
12
因為values()是默認迭代器,所以可以直接對集合實例使用擴展操作,把集合轉為數組:
const s = new Set("哪吒","云韻","比比東"); console.log([...s]);//["哪吒","云韻","比比東"]
1
2
10、什么是迭代器?
迭代器是一個可以由任意對象實現的接口,支持連續獲取對象產出的每一個值。任何實現Iterable接口的對象都有一個Symbol.iterator屬性,這個屬性引用默認迭代器。默認迭代器就像一個迭代器工廠,也就是一個函數,調用之后會產生一個實現Iterator接口的對象。
迭代器必須通過連續調用next()方法才能連續獲取值,這個方法返回一個IteratorObject。這個對象包含一個done屬性和一個value屬性。前者時刻一個布爾值,表示十分還有更多值可以訪問;后者包含迭代器返回的當前值。這個接口可以通過手動反復調用next()方法來消費,也可以通過原生消費者,比如for循環來自動消費。
11、什么是生成器?
生成器是ECMAScript6新增的一個極為靈活的結構,擁有在一個函數塊內暫停和恢復代碼執行的能力。這種新能力具有較深遠的影響,比如,使用生成器可以自定義迭代器和實現協程。
生成器的形式是一個函數,函數名稱前面加一個星號*,表示它是一個生成器。只要是可以定義函數的地方,就可以定義生成器。
調用生成器函數會產生一個生成器對象,生成器對象一開始處于暫停執行(suspended)狀態。與迭代器相似,生成器對象也實現了Iterator接口,因此具有next()方法。調用這個方法會讓生成器開始或恢復執行。
next()方法的返回值類似于迭代器,有一個done屬性和一個value屬性。函數體為空的生成器函數中間不會停留,調用一次next()就會讓生成器到達done:true狀態。
12、JavaScript的繼承是通過什么方式實現的?(問法2:什么是原型鏈?問法3:構造函數、原型、實例三者的關系?)
ECMA-262把原型鏈定義為ECMAScript的主要繼承方式。其基本思想就是通過原型繼承多個引用類型的屬性和方法。
每個構造函數都有一個原型對象,原型有一個屬性指回構造函數,而實例有一個內部指針指向原型。如果原型是另一個類型的實例呢?那就意味著這個原型本身有一個內部指針指向另一個原型,相應地另一個原型也有一個指針指向另一個構造函數,這樣就在實例和原型之間構造了一條原型鏈。
13、以下表達式,對嗎?為什么?
console.log(sum(1,2)); function sum(num1,num2){ return num1+num2; }
1
2
3
4
JavaScript引擎在任何代碼執行之前,會先讀取函數聲明,并在執行上下文中生成函數定義。而函數表達式必須等到代碼執行到它那一行,才會在執行上下文中生成函數定義。
所以,以上代碼是可以正常運行的,因為函數聲明會在任何代碼執行之前先被讀取并添加到執行上下文,這個過程叫做函數聲明提升。
在執行代碼時,JavaScript引擎會先執行一遍掃描,把發現的函數聲明提升到源代碼樹的頂部。因此即使函數定義出現在它們的代碼之后,引擎也會把函數聲明提升到頂部。
如果把前面代碼中的函數聲明改為等價的函數表達式,那么執行的時候就會出錯。
console.log(sum(1,2)); let sum = function sum(num1,num2){ return num1+num2; }
1
2
3
4
14、apply()和call()有什么區別?
這兩個方法都會以制定的this值來調用函數,即會設置調用函數時函數體內的this對象的值。
apply()接收兩個參數,函數體this的值和一個參數數組。第二個對象是Array的實例,也可以是arguments對象。
call()和apply()的作用是一樣的,只是傳入參數的形式不一樣。call()傳入的不是數組或arguments對象,而是將參數一個一個的傳入。
apply()和call()真正的用處不在于傳遞參數,而在于控制this。
window.color = 'red'; let o = { color:'blue'; } function getColor(){ console.log(this.color); } getColor(); //red getColor().call(this);//red getColor().call(window);//red getColor().call(o);//blue
1
2
3
4
5
6
7
8
9
10
11
12
13
15、函數聲明和函數表達式有什么區別?
函數聲明是這樣的:
function functionNam(arg0,arg1,arg2){ //函數體 }
1
2
3
函數式聲明的關鍵特點是函數聲明提升,即函數聲明會在代碼執行前獲得定義。
函數表達式是什么?
let functionName = function(arg0.arg1,arg2){ //函數體 }
1
2
3
函數表達式看起來就像一個普通的變量定義和賦值,即創建一個函數再把它賦值給一個變量functionName。這樣創建的函數叫做匿名函數,因為function關鍵字后面沒有標識符。匿名函數有時也被成為蘭姆達函數。
函數表達式和其他表達式一樣,需要先賦值再使用,所以如下代碼就是錯誤的。
sayHello(); let sayHello = function(){ console.log("hello world"); }
1
2
3
4
函數聲明和函數表達式的根本區別在于理解提升。
JavaScript 數據結構
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。