js筆試題


第一題

    console.log(a)       //       undefined
    var a = 1;
    var getNum = function() { a = 2; } function getNum() { a = 3; } console.log(a) // 1 getNum() console.log(a) // 2 

這道題主要考查聲明提升函數聲明先於變量聲明這兩個知識點。在解析這個js片段的時候是按照這樣的順序:

// 聲明提升
function getNum() { a = 3; } var a; var getNum; // console.log(a); a = 1; getNum = function() { a = 2; } console.log(a) getNum() console.log(a) 

這樣就很清晰了,第一次輸出a的時候僅僅是聲明了還沒有賦值,所以是undefined。第二次的不用說了。第三次輸出2是因為,變量聲明是無法覆蓋函數聲明的,一開始getNum指向一個函數,后來賦值成了一個函數表達式,指向了另一個函數。

第二題

// 每隔一秒輸出一次i值
for(var i = 0; i < 5; i++){ // TODO } 

此題主要考查閉包js執行機制。以下幾種解法:

大概1秒輸出一次的版本:

// 利用立即執行函數形成閉包
(function(i){ setTimeout(function() { console.log(i) }, i * 1000)})(i) 
// 利用setTimeout的第三個參數形成閉包 setTimeout(function(i) { console.log(i) }, i * 1000, i) 
// 如果不是題目中指定了var,利用ES6的let就簡單多了 for(let i = 1; i < 5; i++) { setTimeout(function(){ console.log(i) }, i * 1000) } 
// 再看ES7版本
const sleep = (time) => 
    new Promise((resolve, reject) => 
        setTimeout(resolve, time)); (async function(){ for(var i = 0; i < 5; i++){ await sleep(1000); console.log(i); } })() 

之所以是說是大概,是因為setTimeout並不是延時多少秒后執行函數,而是多少秒后把函數扔進事件隊列中等待執行,如果此時隊列里有其他任務 的話那就不是精確的1秒了。

關於js執行機制,看這里這一次,徹底弄懂 JavaScript 執行機制

再看比較精確的1秒版本:

for(var i =0; i < 5; i++) { var time = new Date(); while(new Date() - time < 1000) { } console.log(i) } 

直接阻塞線程一秒鍾,簡單粗暴有木有~

第三題

var a = {}
var b = {
    key: "a" } var c = { key: "c" } a[b] = "123"; a[c] = "456"; console.log(a[b]) // 456 

這題主要考查對象。其實這里a[b]a[c]中的b、c都會調用object.prototype.toString(),都變成了[object Object],這樣就與對象中的key值無關了。所以a[b]a[c]的指向是相同的。

第四題

var f = function() { var c = "ccc"; return { a: function() { return c; }, b: function(d) { c = d; } } }() console.warn(f.a()) // ccc console.warn(f.c) // undefined console.warn(f.b("www")) // undefined console.warn(f.a()) // www

這題主要考查的是執行上下文中的作用域鏈。我們要注意到函數表達式后的那個函數執行符——(),它是個立即執行函數,也就是說f是個包含a、b屬性的對象。

console.warn(f.a()) 

當a()的執行上下文被激活時,作用域和變量對象被確定,c是個自由變量,需要在作用域鏈中向上查找,然受在父級作用域中找到,所以輸出“ccc”。

console.warn(f.c)

這個就不用說啦,f中沒有c這個屬性,取不到當然返回undefined

console.warn(f.b("www")) 

同第一行一樣,修改的是父級作用域中的c,但由於沒有返回值所以輸出的是undefined。

函數會順着作用域鏈查找當前執行環境不存在的變量,對象則從原型鏈上查找!!!

第五題

數組去重 輸入[1,2,3,1,'1','2',2]返回[1,2,3,'1','2']。 這個嘛方法就很多啦。

(function(arr){ console.log([...(new Set(arr))]) })([1,2,3,1,'1','2',2]) 

利用Map結構的key可以是任意類型這個特性,能很好的區分字符'1'和數字1,而普通對象的key值是字符串類型,無法區分這兩者。

(function(arr) { let hash = new Map(); arr = arr.reduce((item, value) => { hash.has(value) ? '' : hash.set(value, true) && item.push(value) return item; }, []) console.log(arr) })([1,2,3,1,'1','2',2]) 

第六題

有兩個小寫字符串s1、s2,s2是s1經過打亂后增加一個小寫字符得到的, 編程得出s2中增加的字符,算法時間復雜度最好接近O(n)(如s1是'abc',s2是'cbad',那么增加的字符為‘d’)。

解法一

筆者關於這道題的思考,首先是考慮到增加的字符可能是s1中已經存在的,那通過遍歷+indexOf()的方案也就沒用了,所以筆者在寫這道題的時候考慮到s1、s2只有一個字符之差,索性把s1、s2中的字符都填入一個對象中,統計每個字符的個數,個數為奇數的就是那個多出來的字符了。(另外歡迎在評論區中給出更優解)上代碼:

var s1 = "aaabweddccc"; var s2 = "aaaewwbcccdd"; (function(a,b){ let all = a + b; let allLen = all.length; let hash = {}; for(let i = 0; i < allLen; i++) { hash[all[i]] ? hash[all[i]]++ : hash[all[i]] = 1; } console.log(hash) for(let j in hash) { if(hash[j] % 2 !== 0) { console.log(j) } } })(s1,s2) 

運行結果:

 

 

解法二

這個方法是后來想到的。思路是這樣的,如果沒有插入那個額外的字符之前,對兩個字符串進行排序后,兩字符串對應位置的字符肯定是相同的,插入一個字符之后,必定有個位置的字符不匹配。

(function(a,b){ a = a.split("").sort() b = b.split("").sort() for(let i = 0, len = b.length; i < len; i++) { if(a[i] !== b[i]) console.log(b[i]) } })(s1,s2) 

運行結果:

 

 

總結

總共就6道題,考的比較基礎,包括:

  • 聲明提升
  • 閉包
  • 執行上下文
  • js執行機制
  • ......

作者:Gesangs
鏈接:https://juejin.im/post/5abaeecef265da237a4d0e20
來源:掘金
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM