本周總結(jié)請查收(內(nèi)附21道面試題)

目錄
5道筆試題 10道簡答題 6道算法題
筆試題
1.下面代碼輸出什么
// base.js
let count = 0;
setTimeout(() => {
? ?console.log("base.count", ++count);
}, 500)
module.exports.count = count;
// commonjs.js
const { count } = require('./base');
setTimeout(() => {
? ? console.log("count is" + count + 'in commonjs');
}, 1000)
// base1.js
let count = 0;
setTimeout(() => {
? ?console.log("base.count", ++count);
}, 500)
exports const count = count;
// es6.js
import { count } from'./base1';
setTimeout(() => {
? ? console.log("count is" + count + 'in es6');
}, 1000)
答案:1,0,1,1
解析:
CommonJs模塊輸出的是一個(gè)值的拷貝,也就是說,一旦輸出一個(gè)值,模塊內(nèi)部的變化就影響不到這個(gè)值。 ES6 模塊的運(yùn)行機(jī)制與 CommonJS 不一樣。JS 引擎對腳本靜態(tài)分析的時(shí)候,遇到模塊加載命令import,就會生成一個(gè)只讀引用。等到腳本真正執(zhí)行時(shí),再根據(jù)這個(gè)只讀引用,到被加載的那個(gè)模塊里面去取值。換句話說,ES6 的import有點(diǎn)像 Unix 系統(tǒng)的“符號連接”,原始值變了,import加載的值也會跟著變。因此,ES6 模塊是動態(tài)引用,并且不會緩存值,模塊里面的變量綁定其所在的模塊。
詳細(xì)分析請看《require和import的區(qū)別》
2.下面代碼輸出什么
console.log((function() {
? ?console.log(1);
? ?setTimeout(function() {
? ? ? ?console.log(2)
? ?}, 1000)
? ?setTimeout(function() {
? ? ? ?console.log(3)
? ?}, 0);
? ?setTimeout(function() {
? ? ? ?console.log(4)
? ?}, 0);
? ?console.log(5)
})());
答案:1, 5, undefined, 3, 4, 2
詳細(xì)分析請看《setTimeout和requestAnimationFrame》
3.下面代碼輸出什么
console.log((typeofnull ));
console.log((typeof []));
console.log((typeofSymbol()));
console.log((typeof123n) );
function foo() {
? ?console.log(111);
};
console.log((typeof foo));
答案
object object symbol bigint function
4.下面代碼輸出什么
function *foo(x) {
? ?const y = 2 * (yield (x + 1));
? ?const z = yield (y / 3);
? ?return (x + y + z);
}
const ?a = foo(5);
console.log(a.next());
console.log(a.next());
console.log(a.next());
const b = foo(5);
console.log(b.next());
console.log(b.next(12));
console.log(b.next(13));
答案:
{ value: 6, done: false }
{ value: NaN, done: false }
{ value: NaN, done: true }
{ value: 6, done: false }
{ value: 8, done: false }
{ value: 42, done: true }
解析:先看使用Generator函數(shù)生成的迭代器a:
第一次調(diào)用next方法,遇到 yield 停止,返回yield表達(dá)式的值,此時(shí)為
5 + 1 = 6;第二次調(diào)用next方法,遇到 yield 停止,返回yield表達(dá)式的值,由于next方法沒有帶參數(shù),上一個(gè)yield表達(dá)式返回值為
undefined, 導(dǎo)致y的值等于2*undefined即(NaN),除以3以后還是NaN,因此返回對象的value屬性也等于NaN。第三次調(diào)用next方法,執(zhí)行的是
return (x + y + z),此時(shí)x的值為5,y的值為NaN, 由于next方法沒有帶參數(shù),上一個(gè)yield表達(dá)式返回值為undefined,導(dǎo)致z為 undefined,返回對象的 value屬性等于5 + NaN + undefined,即 NaN
再來看看使用Generator函數(shù)生成的迭代器b:
第一次調(diào)用next方法,遇到 yield 停止,返回yield表達(dá)式的值,此時(shí)為
5 + 1 = 6;第二次調(diào)用next方法,遇到 yield 停止,返回yield表達(dá)式的值,由于next方法帶有參數(shù)
12,所以上一個(gè)yield表達(dá)式返回值為12, 因此y的值等于2*12即(24),除以3是8,因此返回對象的value屬性為8。第三次調(diào)用next方法,執(zhí)行的是
return (x + y + z),此時(shí)x的值為5,y的值為24, 由于next方法沒有帶參數(shù)13,因此z為13,返回對象的 value屬性等于5 + 24 + 13,即42
詳細(xì)分析請看《Generator函數(shù)》
5.下面代碼輸出什么
let z = 1;
function *foo() {
? ?const x = yield2;
? ?z++;
? ?const y = yield (x * z);
? ?console.log(x, y, z);
}
const a = foo();
const b = foo();
let val1 = a.next().value;
console.log(val1);
let val2 = b.next().value;
console.log(val2);
val1 = a.next(val2 * 10).value;
console.log(val1);
val2 = b.next(val1 * 5).value;
console.log(val2);
a.next(val2 / 2);
b.next(val1 / 4);
答案:
2 2 40 600 20 300 3 200 10 3
解析
*foo()的兩個(gè)實(shí)例同時(shí)啟用,兩個(gè)next()分別從yield 2語句得到2val2 * 10也就是2 * 10,發(fā)送到第一個(gè)生成器實(shí)例a, 因?yàn)閤得到的值20。z從1增加到2,然后20 * 2通過yield發(fā)出,將val1設(shè)置為40val1 * 5也就是40 * 5,發(fā)送到第二個(gè)生成器實(shí)例b,因此x得到的值200。z再從2遞增到3,然后200*3通過yield發(fā)出,將val2設(shè)置為600val2 / 2也就是600 / 2發(fā)動到第一個(gè)生成器實(shí)例a, 因此 y得到值300, 然后打印出x y z的值分別為20, 300, 3。val1 / 4也就是40 / 4, 發(fā)送到第二個(gè)生成器實(shí)例b, 因此y得到的值10, 然后打印出x y z的值分別為200, 10, 3。
詳細(xì)分析請看《Generator函數(shù)》
簡答題
1. commonjs模塊和ES6模塊的區(qū)別
CommonJs模塊輸出的是一個(gè)值的拷貝,ES6模塊輸出的是值的引用。 CommonJs模塊是運(yùn)行時(shí)加載,ES6模塊是編譯時(shí)輸出接口。
詳細(xì)分析請看《require和import的區(qū)別》
2. JS有哪幾種數(shù)據(jù)類型
JavaScript有八種內(nèi)置類型
空值(null) 未定義(undefined) 布爾值(boolean) 數(shù)字(number) 字符串(string) 對象 (object) 符號(symbol, ES6中新增) 大整數(shù)(BigInt, ES2020 引入)
除對象外,其他統(tǒng)稱為“基本類型”。
注意新增的 ?
symbol和BigInt
3. typeof 原理是什么
typeof原理:不同的對象在底層都表示為二進(jìn)制,在Javascript中二進(jìn)制前(低)三位存儲其類型信息。
000: 對象 010: 浮點(diǎn)數(shù) 100:字符串 110:布爾 1:整數(shù)
typeof null 為"object", 原因是因?yàn)?不同的對象在底層都表示為二進(jìn)制,在Javascript中二進(jìn)制前(低)三位都為0的話會被判斷為Object類型,null的二進(jìn)制表示全為0,自然前三位也是0,所以執(zhí)行typeof時(shí)會返回"object"。
關(guān)鍵詞:JavaScript數(shù)據(jù)類型的相關(guān)底層機(jī)制
4. instanceof 原理是什么,自己可以自定義一個(gè)么
instanceof的語法:
object instanceofconstructor
// 等同于
constructor.prototype.isPrototypeOf(object)
object:要檢測的對象 constructor:某個(gè)構(gòu)造函數(shù)
instanceof原理:檢測constructor.prototype是否存在于參數(shù) object的 原型鏈上。instanceof查找的過程中會遍歷object的原型鏈,直到找到constructor的prototype,如果查找失敗,則會返回false,告訴我們,object并非是constructor的實(shí)例。
對象的Symbol.hasInstance屬性,指向一個(gè)內(nèi)部方法。當(dāng)其他對象使用instanceof運(yùn)算符,判斷是否為該對象的實(shí)例時(shí),會調(diào)用這個(gè)方法。比如,foo instanceof Foo在語言內(nèi)部,實(shí)際調(diào)用的是FooSymbol.hasInstance。
class MyClass {
?[Symbol.hasInstance](foo) {
? ?return foo instanceofArray;
?}
}
[1, 2, 3] instanceofnew MyClass() // true
關(guān)鍵詞:instanceof 用法,原型鏈,
Symbol.hasInstance。詳細(xì)分析請查看《 typeof和instanceof原理》
5. setTimeout和setInterval 區(qū)別是什么,怎么用 setTimeout實(shí)現(xiàn) setInterval
setTimeout: 指定延期后調(diào)用函數(shù),每次setTimeout計(jì)時(shí)到后就會去執(zhí)行,然后執(zhí)行一段時(shí)間后才繼續(xù)setTimeout,中間就多了誤差,(誤差多少與代碼的執(zhí)行時(shí)間有關(guān))。 setInterval:以指定周期調(diào)用函數(shù),而setInterval則是每次都精確的隔一段時(shí)間推入一個(gè)事件(但是,事件的執(zhí)行時(shí)間不一定就不準(zhǔn)確,還有可能是這個(gè)事件還沒執(zhí)行完畢,下一個(gè)事件就來了).
setTimeout(function fn(){
? ?console.log('我被調(diào)用了');
? ?setTimeout(fn, 100);
},100);
詳細(xì)分析請看《setTimeout和requestAnimationFrame》
6. 怎么理解setTimeout(()=> {}, 0)
執(zhí)行該語句時(shí),是立即把當(dāng)前定時(shí)器代碼推入事件隊(duì)列,當(dāng)定時(shí)器在事件列表中滿足設(shè)置的時(shí)間值時(shí)將傳入的函數(shù)加入任務(wù)隊(duì)列,之后的執(zhí)行就交給任務(wù)隊(duì)列負(fù)責(zé)。但是如果此時(shí)任務(wù)隊(duì)列不為空,則需等待,所以執(zhí)行定時(shí)器內(nèi)代碼的時(shí)間可能會大于設(shè)置的時(shí)間。
HTML5標(biāo)準(zhǔn)規(guī)定了setTimeout()的第二個(gè)參數(shù)的最小值(最短間隔)不得低于4毫秒。當(dāng)指定的時(shí)間低于該時(shí)間時(shí),瀏覽器會用最小允許的時(shí)間作為setTimeout的時(shí)間間隔,也就是說即使我們把setTimeout的延遲時(shí)間設(shè)置為0,實(shí)際上可能為 4毫秒后才事件推入任務(wù)隊(duì)列。
詳細(xì)分析請看《setTimeout和requestAnimationFrame》
7. requestAnimationFrame是什么,有什么應(yīng)用場景, requestIdleCallback是什么,有什么應(yīng)用場景
requestAnimationFrame是瀏覽器用于定時(shí)循環(huán)操作的一個(gè)接口,類似于setTimeout,主要用途是按幀對網(wǎng)頁進(jìn)行重繪。requestIdleCallback()常用來切割長任務(wù),利用空閑時(shí)間執(zhí)行,避免主線程長時(shí)間阻塞。
詳細(xì)分析請看《setTimeout和requestAnimationFrame》
8. for...of 原理是什么?
for...of 不只是用來遍歷數(shù)組的,只要有iterator接口的數(shù)據(jù)結(jié)構(gòu)都可以用它來遍歷。一個(gè)數(shù)據(jù)結(jié)構(gòu)只要部署了Symbol.iterator屬性,就被視為具有iterator接口。iterator的實(shí)現(xiàn)思想來源于單向鏈表。
關(guān)鍵詞:
iterator,Symbol.iterator, 單向鏈表。詳細(xì)分析請看《for...of原理解析》
9. 自己實(shí)現(xiàn)一個(gè)迭代器?
function makeIterator(array) {
?var nextIndex = 0;
?return {
? ?next: function() {
? ? ?return nextIndex < array.length ?
? ? ? ?{
? ? ? ? ? ?value: array[nextIndex++],
? ? ? ? ? ?done: false
? ? ? ?}
? ? ? ?:
? ? ? ?{
? ? ? ? ? ?value: undefined,
? ? ? ? ? ?done: true
? ? ? ?};
? ?}
?};
}
const it = makeIterator(['a', 'b']);
it.next()
// { value: "a", done: false }
it.next()
// { value: "b", done: false }
it.next()
// { value: undefined, done: true }
10. Gennrator哪些特性決定它可以解決異步?
可以 暫停執(zhí)行(yield)和恢復(fù)執(zhí)行(next)函數(shù)體內(nèi)外的數(shù)據(jù)交換(next返回值的value,是向外輸出數(shù)據(jù),next方法的參數(shù),是向內(nèi)輸入數(shù)據(jù))和錯(cuò)誤處理機(jī)制(Generator 函數(shù)內(nèi)部還可以部署錯(cuò)誤處理代碼,捕獲函數(shù)體外拋出的錯(cuò)誤)
詳細(xì)分析請看?《Generator函數(shù)》
算法題
1. 買賣股票的最佳時(shí)機(jī)I
給定一個(gè)數(shù)組,它的第 i 個(gè)元素是一支給定股票第 i 天的價(jià)格。
如果你最多只允許完成一筆交易(即買入和賣出一支股票),設(shè)計(jì)一個(gè)算法來計(jì)算你所能獲取的最大利潤。
注意你不能在買入股票前賣出股票。
示例 1:
輸入: [7,1,5,3,6,4]
輸出: 5
解釋: 在第 2 天(股票價(jià)格 = 1)的時(shí)候買入,在第 5 天(股票價(jià)格 = 6)的時(shí)候賣出,最大利潤 = 6-1 = 5 。
? ? 注意利潤不能是 7-1 = 6, 因?yàn)橘u出價(jià)格需要大于買入價(jià)格。
示例 2:
輸入: [7,6,4,3,1]
輸出: 0
解釋: 在這種情況下, 沒有交易完成, 所以最大利潤為 0。
2. 買賣股票的最佳時(shí)機(jī)II
給定一個(gè)數(shù)組,它的第 i 個(gè)元素是一支給定股票第 i 天的價(jià)格。
設(shè)計(jì)一個(gè)算法來計(jì)算你所能獲取的最大利潤。你可以盡可能地完成更多的交易(多次買賣一支股票)。
注意:你不能同時(shí)參與多筆交易(你必須在再次購買前出售掉之前的股票)。
示例 1:
輸入: [7,1,5,3,6,4]
輸出: 7
解釋: 在第 2 天(股票價(jià)格 = 1)的時(shí)候買入,在第 3 天(股票價(jià)格 = 5)的時(shí)候賣出, 這筆交易所能獲得利潤 = 5-1 = 4 。
? ? 隨后,在第 4 天(股票價(jià)格 = 3)的時(shí)候買入,在第 5 天(股票價(jià)格 = 6)的時(shí)候賣出, 這筆交易所能獲得利潤 = 6-3 = 3 。
示例 2:
輸入: [1,2,3,4,5]
輸出: 4
解釋: 在第 1 天(股票價(jià)格 = 1)的時(shí)候買入,在第 5 天 (股票價(jià)格 = 5)的時(shí)候賣出, 這筆交易所能獲得利潤 = 5-1 = 4 。
? ? 注意你不能在第 1 天和第 2 天接連購買股票,之后再將它們賣出。
? ? 因?yàn)檫@樣屬于同時(shí)參與了多筆交易,你必須在再次購買前出售掉之前的股票。
示例 3:
輸入: [7,6,4,3,1]
輸出: 0
解釋: 在這種情況下, 沒有交易完成, 所以最大利潤為 0。
3. 合并兩個(gè)有序鏈表
將兩個(gè)有序鏈表合并為一個(gè)新的有序鏈表并返回。新鏈表是通過拼接給定的兩個(gè)鏈表的所有節(jié)點(diǎn)組成的。示例:
輸入:1->2->4, 1->3->4
輸出:1->1->2->3->4->4
答案:合并兩個(gè)有序鏈表
4. 最大子序和
給定一個(gè)整數(shù)數(shù)組 nums ,找到一個(gè)具有最大和的連續(xù)子數(shù)組(子數(shù)組最少包含一個(gè)元素),返回其最大和。示例:
輸入: [-2,1,-3,4,-1,2,1,-5,4],
輸出: 6
解釋: 連續(xù)子數(shù)組 [4,-1,2,1] 的和最大,為 6。
答案:最大子序和
5.撲克牌中的順子
從撲克牌中隨機(jī)抽5張牌,判斷是不是一個(gè)順子,即這5張牌是不是連續(xù)的。2~10為數(shù)字本身,A為1,J為11,Q為12,K為13,而大、小王為 0 ,可以看成任意數(shù)字。A 不能視為 14。
示例?1:
輸入: [1,2,3,4,5]
輸出: True
?
示例?2:
輸入: [0,0,1,2,5]
輸出: True
限制:1.數(shù)組長度為 5 2.數(shù)組的數(shù)取值為 [0, 13] .
答案:撲克牌中的順子
6.無重復(fù)字符的最長子串
給定一個(gè)字符串,請你找出其中不含有重復(fù)字符的 最長子串 的長度。
示例 1:
輸入: "abcabcbb"
輸出: 3
解釋: 因?yàn)闊o重復(fù)字符的最長子串是 "abc",所以其長度為 3。
示例 2:
輸入: "bbbbb"
輸出: 1
解釋: 因?yàn)闊o重復(fù)字符的最長子串是 "b",所以其長度為 1。
示例 3:
輸入: "pwwkew"
輸出: 3
解釋: 因?yàn)闊o重復(fù)字符的最長子串是 "wke",所以其長度為 3。請注意,你的答案必須是 子串 的長度,"pwke" 是一個(gè)子序列,不是子串。
