javascript經典面試題

kinshan發表於2018-11-30

1、作用域

(function() {
   var a = b = 5;
})();
 
console.log(b);  //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);

複製程式碼

答案:
5

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 */}; 
複製程式碼

3、變數提升

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 RosaJohn Doe 。原因是,JavaScript中關鍵字this所引用的是函式上下文,取決於函式是如何呼叫的,而不是怎麼被定義的

在第一個console.log()getFullname()是作為obj.prop物件的函式被呼叫。因此,當前的上下文指代後者,並且函式返回這個物件的fullname屬性。相反,當getFullname()被賦值給test變數時,當前的上下文是全域性物件window,這是因為test被隱式地作為全域性物件的屬性。基於這一點,函式返回windowfullname,在本例中即為第一行程式碼設定的。

5、call()和apply()

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

答案:
這個問題可以通過運用call()或者apply()方法強制轉換上下文環境。如果你不瞭解這兩個方法及它們的區別,我建議你看看這篇文章 function.callfunction.apply之間有和區別。 下面的程式碼中,我用了call(),但apply()也能產生同樣的結果:


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

複製程式碼

6、閉包

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

請問,如果使用者點選第一個和第四個按鈕的時候,控制檯分別列印的結果是什麼?為什麼?

答案:

概念:閉包(Closures)。對於每一個JavaScript開發者來說,如果你想在網頁中編寫5行以上的程式碼,那麼準確理解和恰當使用閉包是非常重要的。如果你想開始學習或者只是想簡單地溫習一下閉包,那麼我強烈建議你去閱讀 Colin Ihrig 這個教程:JavaScript Closures Demystified

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

7、 閉包

修復上題的問題,使得點選第一個按鈕時輸出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、演算法 質數

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

答案:

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

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

第二點要記住:負數不是質數。同樣的,1和0也不是,因此,首先測試這些數字。此外,2是質數中唯一的偶數。沒有必要用一個迴圈來驗證4,6,8。再則,如果一個數字不能被2整除,那麼它不能被4,6,8等整除。因此,你的迴圈必須跳過這些數字。如果你測試輸入偶數,你的演算法將慢2倍(你測試雙倍數字)。可以採取其他一些更明智的優化手段,我這裡採用的是適用於大多數情況的。例如,如果一個數字不能被5整除,它也不會被5的倍數整除。所以,沒有必要檢測10,15,20等等。如果你深入瞭解這個問題的解決方案,我建議你去看相關的Wikipedia介紹。

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

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

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;
}
複製程式碼
var timer1 = (cb,time) => {
	(function loop(){
		cb()
		setTimeout(loop,time)
	})()
}



var timer2 = (cb,time) =>{
	cb()
	setInterval(cb,time)
}

timer1(function(){
	console.log(2)
},1000)

timer2(function(){
    console.log(3)
},1000)

複製程式碼

金三銀四,看見大家都在為了面試而努力 特開了一個前端模擬面試題,組織了面試的群友每天來群裡分享面試題,講題 急思眾議,共同進步,歡迎最近在面試或者準備面試的群友加入本群,加群格式: 工作年限-面試等級(初、中、高)-工作地點 (不在面試或者不準備面試或者不活躍的勿加本群,加了也會被清理)

javascript經典面試題

相關文章