第1題
請說出下列代碼最終的執行結果,並解釋為什么?
var a = [];
for (var i = 0; i < 10; i++) { a[i] = function () { console.log(i); } } a[6]();
最終執行結果為:打印 10。
原因:for 循環時是使用 var 定義循環變量 i (存在變量提升),然后依次為 a 數組元素賦值為一個函數,函數內打印 i;循環結束后調用 a[6]() ,相當於在全局環境下打印 i,而此時 i 已經變為 10。
第2題
請說出下列代碼最終的執行結果,並解釋為什么?
var temp = 123; if (true) { console.log(tmp); let tmp; }
最終執行結果為:報出引用錯誤,在初始化之前不能訪問 'tmp'。
原因是因為,塊級作用域內使用了 let/const 關鍵字聲明變量,就存在暫時性死區,在聲明之前使用這些變量會報錯。
ES6 明確規定,如果區塊中存在let和const命令,這個區塊對這些命令聲明的變量,從一開始就形成了封閉作用域。凡是在聲明之前就使用這些變量,就會報錯。
第3題
結合 ES6 新語法,用最簡單的方式找出數組中的最小值?
var arr = [12, 34, 32, 89, 4];
答案: Math.min(...arr)
第4題
請詳細說明 var,let,const 三種聲明變量的方式之間的具體差別?
1.使用var聲明變量
- 存在變量提升的情況
- 訪問在后續定義的變量會返回 undefined
- var聲明的變量,可以在其他作用域訪問到
2.使用let聲明變量
- 使用let命令,會創建一個塊級作用域
- let聲明的變量只在塊級作用域內有效
- 同個作用域里,使用let不能重復聲明變量
3.使用const聲明常量
- 使用const聲明的常量不能被修改
- 使用const聲明常量時就要賦值
- 使用const聲明一個引用類型數據的常量,可以往這個常量上 增/刪/改 屬性,但是不能直接修改這個常量的值(內存地址不能修改)
第5題
請說出下列代碼最終的執行結果,並解釋為什么?
var a = 10; var obj = { a: 20, fn() { setTimeout(() => { console.log(this.a); }); } }; obj.fn();
最終執行結果為:打印出 20。
原因:setTimeout的回調是一個箭頭函數,箭頭函數不會改變this的指向。this始終指向沿着作用域往上找的第一個 function ,看這個 function 最終是怎樣調用的。題目里是這樣調用的 obj.fn() , 所以此時fn內的this指向obj。
第6題
簡述 Symbol 類型的用途?
Symbol 是 ES2015 提出的一種新的原始數據類型,主要用途有:
- 為對象創建獨一無二的 key 值
- 可以模擬實現對象的私有成員
- 可以用作常量
第7題
說說什么是淺拷貝?什么是深拷貝?
引用類型的數據在賦值的過程中,其實是拷貝了內存地址,比如將b對象賦值給a變量,這兩個變量都指向了同一個內存地址,修改了a變量之后b對象也會跟着改變。而我們希望得到的是,修改a不會影響b,這兩個對象不會相互影響,針對這種場景就可以使用淺拷貝和深拷貝。
淺拷貝是指將對象的所有屬性值拷貝到新的對象,進行一層屬性的拷貝,但是如果屬性值也是對象的話,依舊是拷貝那個對象的地址,上面的問題還是會出現。這種情況就可以使用深拷貝來解決,即使屬性值是對象,深拷貝也可以做到無限層級拷貝,切斷兩個引用類型變量之間的關系,不會相互影響。
淺拷貝的方法:遍歷對象屬性賦值,Object.assign(),展開運算符...
深拷貝的方法:淺拷貝+遞歸,jsON.parse(jsON.stringify(obj))
JSON.parse(JSON.stringify())這個方法有一定的局限性:
序列化時遇到如下情況需要注意:
- 時間對象 => 字符串的形式
- RegExp,Error => {}
- 會丟失 function,undefined
- NaN,Infinity,-Infinity => null
- 如果對象中存在循環引用的情況也無法實現深拷貝
終極深拷貝的方法可以參考 lodash 的深拷貝,不過日常開發如果沒遇到以上的情況,用 JSON.parse(JSON.stringify()) 足以...
第8題
談談你是如何理解 JS 異步編程的,EventLoop 是做什么的,什么是宏任務,什么是微任務?
js是單線程的,同一時間只能做一件事,兩段JS不能同時執行,主要原因是要避免DOM渲染的沖突。解決方案就是異步,而異步編寫的代碼,沒按照書寫方式執行,callback過多,導致可讀性很差,難以理解,所以就出現了 promise / async await。
EventLoop 指事件輪詢,是js實現異步的具體解決方案。同步代碼,在主線程(調用棧 Call stack)中直接執行,壓棧-彈棧。異步任務會依次放入消息隊列(Queue)中,EventLoop 會監聽調用棧和消息隊列,當調用棧中的代碼執行完之后,它會拿消息隊列中的第一個任務放到調用棧執行,以此類推。
異步任務可以分為宏任務和微任務,這兩個任務的執行順序不同。宏任務會依次放入消息隊列等待事件輪詢去執行,而微任務是放在本次調用棧的末尾去執行,也就是說,微任務比宏任務先執行。
- 宏任務:目前大部分的異步調用都是作為宏任務執行
- 微任務:Promise , MutationObserver , node 中 process.nextTick
第9題
將下面異步代碼使用 Promise 改進?
setTimeout(function () { var a = 'hello '; setTimeout(function () { var b = 'lagou '; setTimeout(function () { var c = 'I Love U'; console.log(a + b + c); }, 10); }, 10); }, 10);
答:
function task (msg) { return new Promise(function (resolve, reject) { setTimeout(function () { resolve(msg); }, 10); }); }; task('hello ') .then(msg => task(msg + 'lagou ')) .then(msg => task(msg + 'I Love U')) .then(msg => console.log(msg));
第10題
請簡述 TypeScript 與 JavaScript 之間的關系?
TypeScript 是 JavaScript 的一個超集(擴展集),就是在 JS 的基礎上多出一些擴展特性,這些擴展特性包括:強大的類型系統、對 ES6+ 的良好支持。TS 最終會被編譯為 JS 運行。
品牌vi設計公司http://www.maiqicn.com 辦公資源網站大全https://www.wode007.com
第11題
請談談你所認為的 TypeScript 優缺點?
優點:
- 使用 TS 新特性開發項目,項目代碼的健壯性增強,代碼錯誤更早暴露。
- 編輯器支持情況良好,比如:VSCode,有很多智能提示,編碼更准確,提升開發效率。
- 使用 TS 的強類型特性來開發項目,可以使得項目代碼重構更牢靠。
- 使用 TS 的強類型特性來編寫代碼,可以減少很多不必要的類型判斷。
- TS 最終會被編譯為 JS 運行,可以將 ES6+ 編譯為ES5/ES3,兼容性好。
- TS 是漸進式的,完全可以使用 JS 編寫代碼,可以將新特性慢慢應用到項目中。
缺點:
- 引入了很多概念和新語法,有一定學習成本,對於一些小項目會增加開發成本
- 開發過程中遇到問題,可能相關 TS 的網上資料相比 JS 會少一些,需要花一些精力查找資料去解決問題