JavaScript常見面試題彙總(含答案)

智雲程式設計發表於2019-01-17

一、請解釋 JavaScript 中 this 是如何工作的。

首先:this 永遠指向函式執行時所在的物件,而不是函式被建立時所在的物件。匿名函式或不處於任何物件中的函式指向 window 。

1、 方法呼叫模式

當函式被儲存為物件的一個屬性時,成該函式為該物件的方法。函式中this的值為該物件。

var foo = {
    name: 'fooname',
    getName: function (){
        return this.name  
    }
}
foo.getName();  // this => foo

2、 函式呼叫模式

當函式並不是物件的屬性。函式中this的值為全域性物件
note:某個方法中的內部函式中的this的值也是全域性物件,而非外部函式的this

function foo(){
    this.name = 'fooname';  
}
foo();  // this => window

3、構造器呼叫模式

即使用new呼叫的函式,則其中this將會被繫結到那個新構造的物件。

function Foo(){
    this.name = 'fooname';
}
var foo = new Foo();  // this => foo

4、使用apply或call呼叫模式

該模式呼叫時,函式中this被繫結到apply或call方法呼叫時接受的第一個引數。

function getName(name){
    this.name = name;
}
var foo = {};
getName.call(foo, name);  // this =>foo

改變this的值主要方法(目前想到的,歡迎評論新增):
apply或call方法呼叫時強制修改,使this指向第一個引數。
使用Function.bind方法創造新的函式,該新函式的中this指向所提供的第一個引數。

二、請解釋原型繼承 (prototypal inheritance) 的原理。

JavaScript沒有“子類”和“父類”的概念,也沒有“類”(class)和“例項”(instance)的區分,全靠“原型鏈”(prototype chain)模式,來實現繼承。

每個函式Sub都有一個屬性prototype,prototype指向一個原型物件,原型物件中也有一個指向函式的屬性constructor,透過new一個函式Sub可以產生例項instance,呼叫這個instance的某個屬性或方法時,instance會先查詢自身是否有這個方法或者屬性,沒有的話就會去例項的建構函式Sub的原型prototype中查詢,即Sub.prototype,如果給原型物件Sub.prototype賦予另一個型別的例項superInstance,則是在superInstance中查詢的,這個superInstance中也有屬性prototype指向某個原型物件,以此一級級往上最終到Object.prototype,這樣就形成了原型繼承。

利用此原理可以自己實現一個inherits函式:

function inherits(subType, superType){
    var _prototype = Object.create(superType.prototype);
    _prototype.constructor = subType;
    subType.prototype = _prototype;
}

三、解釋為什麼接下來這段程式碼不是 IIFE (立即呼叫的函式表示式):function foo(){ }(); 要做哪些改動使它變成 IIFE?

(function fn(){..})() ,函式被包含在一個括號內,變成為一個表示式,隨後跟著一個(),就立即執行這個函式。

IIFE的一些作用:

  1. 建立作用域,內部儲存一些大量臨時變數的程式碼防止命名衝突。
  2. 一些庫的外層用這種形式包起來防止作用域汙染。
  3. 執行一些只執行一次的程式碼。

四、(function fn(){..})(),函式被包含在一個括號內,變成為一個表示式,隨後跟著一個(),就立即執行這個函式。

IIFE的一些作用:

  1. 建立作用域,內部儲存一些大量臨時變數的程式碼防止命名衝突。
  2. 一些庫的外層用這種形式包起來防止作用域汙染。
  3. 執行一些只執行一次的程式碼。

當某個函式呼叫時會建立一個執行環境以及作用域鏈,然後根據arguments和其它命名引數初始化形成活動物件。在外部函式呼叫結束後,其執行環境與作用域鏈被銷燬,但是其活動物件儲存在了閉包之中,最後在閉包函式呼叫結束後才銷燬。簡單的說,閉包就是能夠讀取其他函式內部變數的函式。在js中,閉包是指有權訪問另一個函式作用域中的變數的函式。

如何使用:將A函式內部的B函式作為A函式的返回值返回。

為什麼要:

1、匿名自執行函式

有的場景下函式只需要執行一次,例如init()之類的函式,其內部變數無需維護,我們可以使用閉包。 我們建立了一個匿名的函式,並立即執行它,由於外部無法引用它內部的變數,因此在函式執行完後會立刻釋放資源,而且不汙染全域性物件。

2、封裝

模擬物件導向的程式碼風格進行封裝,使私有屬性存在成為可能。

五、.call 和 .apply 的區別是什麼?

.call和.apply的共同點是都是用來改變函式體內this物件的值。

區別是第二個引數不一樣。apply()的第二個引數是一個類陣列物件arguments,引數都是以陣列的形式傳入,而call(),傳遞給他的是一系列引數。例如

Math.max.call(null, 1, 2, 3, 4);
//4
Math.max.apply(null, [1, 2, 3, 4]);
//4

六、請解釋 Function.prototype.bind?

Function.prototype.bind方法會建立一個新函式,當這個新函式被呼叫時,它的this值是傳遞給bind()的第一個引數, 它的引數是bind()的其他引數和其原本的引數.

七、請指出 JavaScript 宿主物件 (host objects) 和原生物件 (native objects) 的區別?

宿主物件是指DOM和BOM。
原生物件是Object、Function、Array、String、Boolean、Number、Date、RegExp、Error、Math等物件。

八、請指出以下程式碼的區別:function Person(){}、var person = Person()、var person = new Person()?

function Person(){}

宣告一個函式Person()。

var person = Person()

將函式Person()的結果返回給變數person,如果沒有返回值則person為undefined。

var person = new Person()

new一個Person的例項物件。

九、請儘可能詳盡的解釋 Ajax 的工作原理。以及使用 Ajax 都有哪些優劣?

Ajax是無需重新整理頁面就能從伺服器取得資料的一種方法。

Ajax透過XmlHttpRequest物件來向伺服器發非同步請求,從伺服器獲得資料,然後用javascript來操作DOM更新頁面。

過程

  1. 建立XMLHttpRequest物件。
  2. 設定響應HTTP請求的回撥函式。
  3. 建立一個HTTP請求,指定相應的請求方法、url等。
  4. 傳送HTTP請求。
  5. 獲取伺服器端返回的資料。
  6. 使用JavaScript操作DOM更新頁面。

缺點

  1. 對搜尋引擎不友好
  2. 要實現Ajax下的前後退功能成本較大
  3. 跨域問題限制

十、請解釋變數宣告提升 (hoisting)。

變數的宣告前置就是把變數的宣告提升到當前作用域的最前面。
函式的宣告前置就是把整個函式提升到當前作用域的最前面(位於前置的變數宣告後面)。

//變數的宣告前置
console.log(num);//undefined
var num = 1;
等價於
//變數的宣告前置
var num;
console.log(num);//undefined
num = 1;

十一、請描述事件冒泡機制 (event bubbling)。

事件冒泡(event bubbling),事件最開始時由觸發的那個元素身上發生,然後沿著DOM樹向上傳播,直到document物件。如果想阻止事件起泡,可以使用e.stopPropagation()。

十二、什麼是 “use strict”; ? 使用它的好處和壞處分別是什麼?

優點

消除Javascript語法的一些不嚴謹之處,減少一些怪異行為;
消除程式碼執行的一些不安全之處,保證程式碼執行的安全;
提高編譯器效率,增加執行速度;
為未來新版本的Javascript做好鋪墊。

缺點

嚴格模式改變了語義。依賴這些改變可能會導致沒有實現嚴格模式的瀏覽器中出現問題或者錯誤。

十三、請解釋 JavaScript 的同源策略 (same-origin policy)。

同源策略限制了一個源(origin)中載入文字或指令碼與來自其它源(origin)中資源的互動方式。同源指的是協議、域名、埠相同,同源策略是一種安全協議。

十四、請解釋 JSONP 的工作原理,以及它為什麼不是真正的 Ajax。

JSONP(JSON with Padding)是一種非官方跨域資料互動協議,它允許在伺服器端整合< script >標籤返回至客戶端,透過javascript回撥的形式實現跨域訪問。

因為同源策略的原因,我們不能使用XMLHttpRequest與外部伺服器進行通訊,但是< script >可以訪問外部資源,所以透過JSON與< script >相結合的辦法,可以繞過同源策略從外部伺服器直接取得可執行的JavaScript函式。

原理

客戶端定義一個函式,比如jsonpCallback,然後建立< script >,src為url + ?jsonp=jsonpCallback這樣的形式,之後伺服器會生成一個和傳遞過來jsonpCallback一樣名字的引數,並把需要傳遞的資料當做引數傳入,比如jsonpCallback(json),然後返回給客戶端,此時客戶端就執行了這個伺服器端返回的jsonpCallback(json)回撥。

通俗的說,就是客戶端定義一個函式然後請求,伺服器端返回的javascript內容就是呼叫這個函式,需要的資料都當做引數傳入這個函式了。

優點 - 相容性好,簡單易用,支援瀏覽器與伺服器雙向通訊
缺點 - 只支援GET請求;存在指令碼注入以及跨站請求偽造等安全問題

補充一點,JSONP不使用XMLHttpRequest物件載入資源,不屬於真正意義上的AJAX。

十五、== 和 === 有什麼不同?

通俗的說就是===比==要更為嚴格,===比較過程中沒有任何的型別轉換。

十六、什麼是三元表示式 (Ternary expression)?“三元 (Ternary)” 表示什麼意思?

如名字表示的三元運算子需要三個運算元。
語法是
條件 ? 結果1 : 結果2;
這裡你把條件寫在問號(?)的前面後面跟著用冒號(:)分隔的結果1和結果2。滿足條件時結果1否則結果2。

十七、你怎麼看 AMD vs. CommonJS?

瀏覽器端非同步和伺服器端同步的模組化程式設計規範

十八、請舉出一個匿名函式的典型用例?

定義回撥函式,立即執行函式,作為返回值的函式,使用方法var foo = function() {}定義的函式。

十九、描述以下變數的區別:null,undefined 或 undeclared?該如何檢測它們?

未定義的屬性、定義未賦值的為undefined,JavaScript訪問不會報錯;null是一種特殊的object;NaN是一種特殊的number;undeclared 是未宣告也未賦值的變數,JavaScript訪問會報錯。

二十、在什麼時候你會使用 document.write()?

DOM方法,可向文件寫入 HTML 表示式或 JavaScript 程式碼。

二十一、如何實現下列程式碼:[1,2,3,4,5].duplicator(); // [1,2,3,4,5,1,2,3,4,5]

Array.prototype.duplicator = function(){
  var l = this.length,i;
  for(i=0;i<l;i++){
   this.push(this[i]) 
   }
}

二十二、解釋 function foo() {} 與 var foo = function() {} 用法的區別

函式宣告的兩種方法:

  1. var foo = function () {}
    這種方式是宣告瞭個變數,而這個變數是個方法,變數在js中是可以改變的。
    也:將一個匿名函式賦值給了變數。

  2. function foo() {}
    這種方式是宣告瞭個方法,foo這個名字無法改變

二十三、請解釋可變 (mutable) 和不變 (immutable) 物件的區別。

在 JavaScript 中,物件是引用型別的資料,其優點在於頻繁的修改物件時都是在原物件的基礎上修改,並不需要重新建立,這樣可以有效的利用記憶體,不會造成記憶體空間的浪費,物件的這種特性可以稱之為 Mutable,中文的字面意思是「可變」。

Immutable 從字面上翻譯成中文是「不可變」。每次修改一個 Immutable 物件時都會建立一個新的不可變的物件,在新物件上操作並不會影響到原物件的資料。

二十四、使用 Promises 而非回撥 (callbacks) 優缺點是什麼?

Promise是非同步程式設計的一種解決方案,比傳統的解決方案——回撥函式和事件——更合理和更強大。它由社群最早提出和實現,ES6將其寫進了語言標準,統一了用法,原生提供了Promise物件。

所謂Promise,簡單說就是一個容器,裡面儲存著某個未來才會結束的事件(通常是一個非同步操作)的結果。從語法上說,Promise是一個物件,從它可以獲取非同步操作的訊息。Promise提供統一的API,各種非同步操作都可以用同樣的方法進行處理。

有了Promise物件,就可以將非同步操作以同步操作的流程表達出來,避免了層層巢狀的回撥函式。此外,Promise物件提供統一的介面,使得控制非同步操作更加容易。

Promise也有一些缺點。

首先,無法取消Promise,一旦新建它就會立即執行,無法中途取消。
其次,如果不設定回撥函式,Promise內部丟擲的錯誤,不會反應到外部。
第三,當處於Pending狀態時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)。

二十五、請解釋同步 (synchronous) 和非同步 (asynchronous) 函式的區別。

同步呼叫,在發起一個函式或方法呼叫時,沒有得到結果之前,該呼叫就不返回,直到返回結果;

非同步呼叫的概念和同步相對,在一個非同步呼叫發起後,被呼叫者立即返回給呼叫者,但呼叫者不能立刻得到結果,被呼叫者在實際處理這個呼叫的請求完成後,透過狀態、通知或回撥等方式來通知呼叫者請求處理的結果。

簡單地說,同步就是發出一個請求後什麼事都不做,一直等待請求返回後才會繼續做事;非同步就是發出請求後繼續去做其他事,這個請求處理完成後會通知你,這時候就可以處理這個回應了。

二十六、你使用哪些工具和技術來除錯 JavaScript 程式碼?

1.javascript的debugger語句
需要除錯js的時候,我們可以給需要除錯的地方透過debugger打斷點,程式碼執行到斷點就會暫定,這時候透過單步除錯等方式就可以除錯js程式碼

if (waldo) {
    debugger;
}

這時候開啟console皮膚,就可以除錯了

2.DOM斷點
DOM斷點是一個Firebug和chrome DevTools提供的功能,當js需要操作打了斷點的DOM時,會自動暫停,類似debugger除錯。
使用DOM斷點步驟:
選擇你要打斷點的DOM節點
右鍵選擇Break on..
選擇斷點型別

另外的除錯方法例如alert, console.log,檢視元素等就不再贅述了。

二十七、你會使用怎樣的語言結構來遍歷物件屬性 (object properties) 和陣列內容?

for in 語句
一般for迴圈
陣列forEach方法

這裡推薦一下我的學習交流群:731771211,裡面都是學習前端的,如果你想製作酷炫的網頁,想學習程式設計。從最基礎的HTML+CSS+JS【炫酷特效,遊戲,外掛封裝,設計模式】到移動端HTML5的專案實戰的學習資料都有整理,送給每一位前端小夥伴,有想學習web前端的,或是轉行,或是大學生,還有工作中想提升自己能力的,正在學習的小夥伴歡迎加入學習。

點選:

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69901074/viewspace-2564029/,如需轉載,請註明出處,否則將追究法律責任。

相關文章