10道典型的JavaScript面試題

seekmiki 7年前發布 | 12K 次閱讀 JavaScript開發 JavaScript

在IT界中,JavaScript開發人員的需求量一直居高不下。如果你的能力能夠勝任這一角色,那么你有很多機會換一家公司,并提高薪水。但在你被一家公司聘用之前,你必須展示你的技能,以通過面試環節。在本文中,我將向您展示10個 JavaScript 技能典型問題以及其相關解決方案,來面試前端工程師。它們很有意思!

問題1: 作用域(Scope)

考慮以下代碼:

(function() {
   var a = b = 5;
})();

console.log(b);

控制臺(console)會打印出什么?

答案

上述代碼會打印出 5 。

這個問題的陷阱就是,在立即執行函數表達式(IIFE)中,有兩個賦值,但是其中變量 a 使用關鍵詞 var 來聲明。這就意味著 a 是這個函數的局部變量。與此相反, b 被分配給了全局作用域(譯注:也就是全局變量)。

這個問題另一個陷阱就是,在函數中沒有使用”嚴格模式” ( 'use strict'; )。如果 嚴格模式 開啟,那么代碼就會報錯 ” Uncaught ReferenceError: b is not defined” 。請記住,如果這是預期的行為,嚴格模式要求你顯式地引用全局作用域。所以,你需要像下面這么寫:

(function() {
   'use strict';
   var a = window.b = 5;
})();

console.log(b);

問題2: 創建 “原生(native)” 方法

在 String 對象上定義一個 repeatify 函數。這個函數接受一個整數參數,來明確字符串需要重復幾次。這個函數要求字符串重復指定的次數。舉個例子:

console.log('hello'.repeatify(3));

應該打印出 hellohellohello .

答案

一個可行的做法如下:

String.prototype.repeatify = String.prototype.repeatify || function(times) {
   var str = '';

   for (var i = 0; i < times; i++) {
      str += this;
   }

   return str;
};

這個問題測試了開發人員對 javascript 中繼承及原型( prototype )屬性的知識。這也驗證了開發人員是否有能力擴展原生數據類型功能(雖然不應該這么做)。

在這里,另一個關鍵點是,看你怎樣避免重寫可能已經定義了的方法。這可以通過在定義自己的方法之前,檢測方法是否已經存在。

String.prototype.repeatify = String.prototype.repeatify || function(times) {/* code here */};

當你被問起去擴展一個Javascript方法時,這個技術非常有用。

愚人碼頭譯注:重復輸出一個給定的字符串的解決方案可以看看這篇文章。也許面試官考你的是知識的廣度和對新知識的掌握情況。

問題3: 變量提升(Hoisting)

執行以下代碼的結果是什么?為什么?

function test() {
   console.log(a);
   console.log(foo());

   var a = 1;
   function foo() {
      return 2;
   }
}

test();

答案

這段代碼的執行結果是 undefined 和 2 。

這個結果的原因是,變量和函數都被提升(hoisted) 到了函數體的頂部。因此,當打印變量 a 時,它雖存在于函數體(因為 a 已經被聲明),但仍然是 undefined 。換言之,上面的代碼等同于下面的代碼:

function test() {
   var a;
   function foo() {
      return 2;
   }

   console.log(a);
   console.log(foo());

   a = 1;
}

test();

問題4: 在javascript中,`this`是如何工作的

以下代碼的結果是什么?請解釋你的答案。

var fullname = 'John Doe';
var obj = {
   fullname: 'Colin Ihrig',
   prop: {
      fullname: 'Aurelio De Rosa',
      getFullname: function() {
         return this.fullname;
      }
   }
};

console.log(obj.prop.getFullname());

var test = obj.prop.getFullname;

console.log(test());

答案

這段代碼打印結果是: Aurelio De Rosa 和 John Doe 。原因是,JavaScript中關鍵字 this 所引用的是函數上下文,取決于函數是如何調用的,而不是怎么被定義的。

在第一個 console.log() , getFullname() 是作為 obj.prop 對象的函數被調用。因此,當前的上下文指代后者,并且函數返回這個對象的 fullname 屬性。相反,當 getFullname() 被賦值給 test 變量時,當前的上下文是全局對象 window ,這是因為 test 被隱式地作為全局對象的屬性。基于這一點,函數返回 window 的 fullname ,在本例中即為第一行代碼設置的。

問題5: call() 和 apply()

修復前一個問題,讓最后一個 console.log() 打印輸出 Aurelio De Rosa .

答案

這個問題可以通過運用 call() 或者 apply() 方法強制轉換上下文環境。 下面的代碼中,我用了 call() ,但 apply() 也能產生同樣的結果:

console.log(test.call(obj.prop));

問題6: 閉包(Closures)

考慮下面的代碼:

var nodes = document.getElementsByTagName('button');
for (var i = 0; i < nodes.length; i++) {
   nodes[i].addEventListener('click', function() {
      console.log('You clicked element #' + i);
   });
}

請問,如果用戶點擊第一個和第四個按鈕的時候,控制臺分別打印的結果是什么?為什么?

答案

上面的代碼考察了一個非常重要的 JavaScript 概念:閉包(Closures)。對于每一個JavaScript開發者來說,如果你想在網頁中編寫5行以上的代碼,那么準確理解和恰當使用閉包是非常重要的。

也就是說,代碼打印兩次 You clicked element #NODES_LENGTH ,其中 NODES_LENGTH 是nodes的結點個數。原因是在for循環完成后,變量 i 的值等于節點列表的長度。此外,因為 i 在代碼添加處理程序的作用域中,該變量屬于處理程序的閉包。你會記得,閉包中的變量的值不是靜態的,因此 i 的值不是添加處理程序時的值(對于列表來說,第一個按鈕為0,對于第二個按鈕為1,依此類推)。在處理程序將被執行的時候,在控制臺上將打印變量 i 的當前值,等于節點列表的長度。

問題7: 閉包(Closures)

修復上題的問題,使得點擊第一個按鈕時輸出0,點擊第二個按鈕時輸出1,依此類推。

答案

有多種辦法可以解決這個問題,下面主要使用兩種方法解決這個問題。

第一個解決方案使用立即執行函數表達式(IIFE)再創建一個閉包,從而得到所期望的i的值。實現此方法的代碼如下:

var nodes = document.getElementsByTagName('button');
for (var i = 0; i < nodes.length; i++) {
   nodes[i].addEventListener('click', (function(i) {
      return function() {
         console.log('You clicked element #' + i);
      }
   })(i));
}

另一個解決方案不使用IIFE,而是將函數移到循環的外面。這種方法由下面的代碼實現:

function handlerWrapper(i) {
   return function() {
      console.log('You clicked element #' + i);
   }
}

var nodes = document.getElementsByTagName('button');
for (var i = 0; i < nodes.length; i++) {
   nodes[i].addEventListener('click', handlerWrapper(i));
}

問題8:數據類型

考慮如下代碼:

console.log(typeof null);
console.log(typeof {});
console.log(typeof []);
console.log(typeof undefined);

答案

前面的問題似乎有點傻,但它考察 typeof 操作符的知識。很多JavaScript開發人員不知道 typeof 的一些特性。在此示例中,控制臺將顯示以下內容:

object
object
object
undefined

最令人驚訝的輸出結果可能是第三個。大多數開發人員認為 typeof [] 會返回 Array 。如果你想測試一個變量是否為數組,您可以執行以下測試:

var myArray = [];
if (myArray instanceof Array) {
   // do something...
}

問題9:事件循環

下面代碼運行結果是什么?請解釋。

function printing() {
   console.log(1);
   setTimeout(function() { console.log(2); }, 1000);
   setTimeout(function() { console.log(3); }, 0);
   console.log(4);
}
printing();

答案

輸出結果:

想知道為什么輸出順序是這樣的,你需要弄了解 setTimeout() 做了什么,以及瀏覽器的事件循環原理。瀏覽器有一個事件循環用于檢查事件隊列,處理延遲的事件。UI事件(例如,點擊,滾動等),Ajax回調,以及提供給 setTimeout() 和 setInterval() 的回調都會依次被事件循環處理。因此,當調用 setTimeout() 函數時,即使延遲的時間被設置為 0 ,提供的回調也會被排隊。回調會呆在隊列中,直到指定的時間用完后,引擎開始執行動作(如果它在當前不執行其他的動作)。因此,即使 setTimeout() 回調被延遲 0 毫秒,它仍然會被排隊,并且直到函數中其他非延遲的語句被執行完了之后,才會執行。

有了這些認識,理解輸出結果為“1”就容易了,因為它是函數的第一句并且沒有使用 setTimeout() 函數來延遲。接著輸出“4”,因為它是沒有被延遲的數字,也沒有進行排隊。然后,剩下了“2”,“3”,兩者都被排隊,但是前者需要等待一秒,后者等待0秒(這意味著引擎完成前兩個輸出之后馬上進行)。這就解釋了為什么“3”在“2”之前。

問題10:算法

寫一個 isPrime() 函數,當其為質數時返回 true ,否則返回 false 。

答案

我認為這是面試中最常見的問題之一。然而,盡管這個問題經常出現并且也很簡單,但是從被面試人提供的答案中能很好地看出被面試人的數學和算法水平。

首先, 因為JavaScript不同于C或者Java,因此你不能信任傳遞來的數據類型。如果面試官沒有明確地告訴你,你應該詢問他是否需要做輸入檢查,還是不進行檢查直接寫函數。嚴格上說,應該對函數的輸入進行檢查。

第二點要記住:負數不是質數。同樣的,1和0也不是,因此,首先測試這些數字。此外,2是質數中唯一的偶數。沒有必要用一個循環來驗證4,6,8。再則,如果一個數字不能被2整除,那么它不能被4,6,8等整除。因此,你的循環必須跳過這些數字。如果你測試輸入偶數,你的算法將慢2倍(你測試雙倍數字)。可以采取其他一些更明智的優化手段,我這里采用的是適用于大多數情況的。例如,如果一個數字不能被5整除,它也不會被5的倍數整除。所以,沒有必要檢測10,15,20等等。

最后一點,你不需要檢查比輸入數字的開方還要大的數字。我感覺人們會遺漏掉這一點,并且也不會因為此而獲得消極的反饋。但是,展示出這一方面的知識會給你額外加分。

現在你具備了這個問題的背景知識,下面是總結以上所有考慮的解決方案:

function isPrime(number) {
   // If your browser doesn't support the method Number.isInteger of ECMAScript 6,
   // you can implement your own pretty easily
   if (typeof number !== 'number' || !Number.isInteger(number)) {
      // Alternatively you can throw an error.
      return false;
   }
   if (number < 2) {
      return false;
   }

   if (number === 2) {
      return true;
   } else if (number % 2 === 0) {
      return false;
   }
   var squareRoot = Math.sqrt(number);
   for(var i = 3; i <= squareRoot; i += 2) {
      if (number % i === 0) {
         return false;
      }
   }
   return true;
}

結論

本文我們討論了5個在對Javascript開發者面試中常問起的典型問題。實際中的問題會因面試的不同而不同,來自面試的真實問題可能會有所不同,但是涵蓋的概念和主題通常都是十分相似的。我希望你愉悅地測試你的能力。萬一你不知道所有的答案,不要擔心:沒有學習和經驗不能解決的問題。 如果你在面試中被問到了其他有趣的問題,不要猶豫馬上來和我們分享吧。這會幫助到很多的開發者。

在這篇文章中,在一些問題和練習的幫助下,我討論了其他 JavaScript 重要概念,這些概念通常是前端開發人員角色面試的一部分。我希望你成功地回答所有這些問題,或者你學到了新的東西,以便你可以在你的下一次面試中表現更好。

原文:

 

來自:http://www.css88.com/archives/7052

 

 本文由用戶 seekmiki 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!