前端面試整理—Javascipt問題(一)

image

本章節(jié)是前端開發(fā)者面試問題 - JS 部分的參考答案。 歡迎提出建議和指正!

  • 請(qǐng)解釋事件委托(event delegation)。

  • 請(qǐng)簡(jiǎn)述JavaScript中的this。

  • 請(qǐng)解釋原型繼承(prototypal inheritance)的工作原理。

  • 說說你對(duì) AMD 和 CommonJS 的了解。

  • 請(qǐng)解釋下面代碼為什么不能用作 IIFE:function foo(){ }();,需要作出哪些修改才能使其成為 IIFE?

  • null、undefined和未聲明變量之間有什么區(qū)別?如何檢查判斷這些狀態(tài)值?

  • 什么是閉包(closure),為什么使用閉包?

  • 請(qǐng)說明.forEach循環(huán)和.map()循環(huán)的主要區(qū)別,它們分別在什么情況下使用?

  • 匿名函數(shù)的典型應(yīng)用場(chǎng)景是什么?

  • 你如何組織自己的代碼?(使用模塊模式(module pattern)還是經(jīng)典繼承(classical inheritance)?)

  • 宿主對(duì)象(host objects)和原生對(duì)象(native objects)的區(qū)別是什么?

  • 下列語句有什么區(qū)別:function Person(){}、var person = Person()var person = newPerson()?

  • .call.apply有什么區(qū)別?

  • 請(qǐng)說明Function.prototype.bind的用法。

  • 什么時(shí)候會(huì)用到document.write()?

  • 功能檢測(cè)(feature detection)、功能推斷(feature inference)和使用 UA 字符串之間有什么區(qū)別?

  • 請(qǐng)盡可能詳細(xì)地解釋 Ajax。

  • 使用Ajax的優(yōu)缺點(diǎn)分別是什么?

  • 請(qǐng)說明 JSONP 的工作原理,它為什么不是真正的 Ajax?

  • 你使用過 JavaScript 模板嗎?用過什么相關(guān)的庫(kù)?

  • 請(qǐng)解釋變量提升(hosting)。

  • 請(qǐng)描述事件冒泡。

  • “attribute” 和 “property” 之間有什么區(qū)別?

  • 為什么擴(kuò)展 JavaScript 內(nèi)置對(duì)象是不好的做法?

  • document 中的load事件和DOMContentLoaded事件之間的區(qū)別是什么?

  • =====的區(qū)別是什么?

  • 請(qǐng)解釋關(guān)于 JavaScript 的同源策略。

  • 請(qǐng)使下面的語句生效:

  • 請(qǐng)說明三元表達(dá)式中“三元”這個(gè)詞代表什么?

  • 什么是"use strict";?使用它有什么優(yōu)缺點(diǎn)?

  • 創(chuàng)建一個(gè)循環(huán),從1迭代到100,3的倍數(shù)時(shí)輸出 "fizz",5的倍數(shù)時(shí)輸出 "buzz",同時(shí)為35的倍數(shù)時(shí)輸出"fizzbuzz"。

  • 為什么不要使用全局作用域?

  • 為什么要使用load事件?這個(gè)事件有什么缺點(diǎn)嗎?你知道一些代替方案嗎,為什么使用它們?

  • 請(qǐng)解釋單頁應(yīng)用是什么,如何使其對(duì)SEO友好。

  • 你對(duì) Promises 及其 polyfill 的掌握程度如何?

  • Promise代替回調(diào)函數(shù)有什么優(yōu)缺點(diǎn)?

  • 用轉(zhuǎn)譯成 JavaScript 的語言寫 JavaScript 有什么優(yōu)缺點(diǎn)?

  • 你使用什么工具和技巧調(diào)試 JavaScript 代碼?

  • 你使用什么語句遍歷對(duì)象的屬性和數(shù)組的元素?

  • 請(qǐng)解釋可變對(duì)象和不可變對(duì)象之間的區(qū)別。

  • 請(qǐng)解釋同步和異步函數(shù)之間的區(qū)別。

  • 什么是事件循環(huán)?調(diào)用堆棧和任務(wù)隊(duì)列之間有什么區(qū)別?

  • 請(qǐng)解釋function foo() {}var foo = function() {}之間foo的用法上的區(qū)別。

  • 使用let、varconst創(chuàng)建變量有什么區(qū)別?

  • ES6 的類和 ES5 的構(gòu)造函數(shù)有什么區(qū)別?

  • 你能給出一個(gè)使用箭頭函數(shù)的例子嗎,箭頭函數(shù)與其他函數(shù)有什么不同?

  • 在構(gòu)造函數(shù)中使用箭頭函數(shù)有什么好處?

  • 高階函數(shù)(higher-order)的定義是什么?

  • 請(qǐng)給出一個(gè)解構(gòu)(destructuring)對(duì)象或數(shù)組的例子。

  • ES6 的模板字符串為生成字符串提供了很大的靈活性,你可以舉個(gè)例子嗎?

  • 你能舉出一個(gè)柯里化函數(shù)(curry function)的例子嗎?它有哪些好處?

  • 使用擴(kuò)展運(yùn)算符(spread)的好處是什么,它與使用剩余參數(shù)語句(rest)有什么區(qū)別?

  • 如何在文件之間共用代碼?

  • 什么情況下會(huì)用到靜態(tài)類成員?

請(qǐng)解釋事件委托(event delegation)。

事件委托是將事件監(jiān)聽器添加到父元素,而不是每個(gè)子元素單獨(dú)設(shè)置事件監(jiān)聽器。當(dāng)觸發(fā)子元素時(shí),事件會(huì)冒泡到父元素,監(jiān)聽器就會(huì)觸發(fā)。這種技術(shù)的好處是:

  • 內(nèi)存占用減少,因?yàn)橹恍枰粋€(gè)父元素的事件處理程序,而不必為每個(gè)后代都添加事件處理程序。

  • 無需從已刪除的元素中解綁處理程序,也無需將處理程序綁定到新元素上。

參考

請(qǐng)簡(jiǎn)述JavaScript中的this。

JS 中的this是一個(gè)相對(duì)復(fù)雜的概念,不是簡(jiǎn)單幾句能解釋清楚的。粗略地講,函數(shù)的調(diào)用方式?jīng)Q定了this的值。我閱讀了網(wǎng)上很多關(guān)于this的文章,Arnav Aggrawal 寫的比較清楚。this取值符合以下規(guī)則:

  1. 在調(diào)用函數(shù)時(shí)使用new關(guān)鍵字,函數(shù)內(nèi)的this是一個(gè)全新的對(duì)象。

  2. 如果apply、callbind方法用于調(diào)用、創(chuàng)建一個(gè)函數(shù),函數(shù)內(nèi)的 this 就是作為參數(shù)傳入這些方法的對(duì)象。

  3. 當(dāng)函數(shù)作為對(duì)象里的方法被調(diào)用時(shí),函數(shù)內(nèi)的this是調(diào)用該函數(shù)的對(duì)象。比如當(dāng)obj.method()被調(diào)用時(shí),函數(shù)內(nèi)的this將綁定到obj對(duì)象。

  4. 如果調(diào)用函數(shù)不符合上述規(guī)則,那么this的值指向全局對(duì)象(global object)。瀏覽器環(huán)境下this的值指向window對(duì)象,但是在嚴(yán)格模式下('use strict'),this的值為undefined。

  5. 如果符合上述多個(gè)規(guī)則,則較高的規(guī)則(1號(hào)最高,4號(hào)最低)將決定this的值。

  6. 如果該函數(shù)是 ES2015 中的箭頭函數(shù),將忽略上面的所有規(guī)則,this被設(shè)置為它被創(chuàng)建時(shí)的上下文。

想獲得更深入的解釋,請(qǐng)查看他在 Medium 上的文章。

參考

請(qǐng)解釋原型繼承(prototypal inheritance)的工作原理。

這是一個(gè)非常常見的 JavaScript 問題。所有 JS 對(duì)象都有一個(gè)prototype屬性,指向它的原型對(duì)象。當(dāng)試圖訪問一個(gè)對(duì)象的屬性時(shí),如果沒有在該對(duì)象上找到,它還會(huì)搜尋該對(duì)象的原型,以及該對(duì)象的原型的原型,依次層層向上搜索,直到找到一個(gè)名字匹配的屬性或到達(dá)原型鏈的末尾。這種行為是在模擬經(jīng)典的繼承,但是與其說是繼承,不如說是委托(delegation)。

參考

說說你對(duì) AMD 和 CommonJS 的了解。

它們都是實(shí)現(xiàn)模塊體系的方式,直到 ES2015 出現(xiàn)之前,JavaScript 一直沒有模塊體系。CommonJS 是同步的,而AMD(Asynchronous Module Definition)從全稱中可以明顯看出是異步的。CommonJS 的設(shè)計(jì)是為服務(wù)器端開發(fā)考慮的,而 AMD 支持異步加載模塊,更適合瀏覽器。

我發(fā)現(xiàn) AMD 的語法非常冗長(zhǎng),CommonJS 更接近其他語言 import 聲明語句的用法習(xí)慣。大多數(shù)情況下,我認(rèn)為AMD 沒有使用的必要,因?yàn)槿绻阉?JavaScript 都捆綁進(jìn)一個(gè)文件中,將無法得到異步加載的好處。此外,CommonJS 語法上更接近 Node 編寫模塊的風(fēng)格,在前后端都使用 JavaScript 開發(fā)之間進(jìn)行切換時(shí),語境的切換開銷較小。

我很高興看到 ES2015 的模塊加載方案同時(shí)支持同步和異步,我們終于可以只使用一種方案了。雖然它尚未在瀏覽器和Node 中完全推出,但是我們可以使用代碼轉(zhuǎn)換工具進(jìn)行轉(zhuǎn)換。

參考

請(qǐng)解釋下面代碼為什么不能用作 IIFE:function foo(){ }();,需要作出哪些修改才能使其成為 IIFE?

IIFE(Immediately Invoked Function Expressions)代表立即執(zhí)行函數(shù)。 JavaScript 解析器將 function foo(){ }();解析成function foo(){ }();。其中,前者是函數(shù)聲明;后者(一對(duì)括號(hào))是試圖調(diào)用一個(gè)函數(shù),卻沒有指定名稱,因此它會(huì)拋出Uncaught SyntaxError: Unexpected token )的錯(cuò)誤。

修改方法是:再添加一對(duì)括號(hào),形式上有兩種:(function foo(){ })()(function foo(){ }())。以上函數(shù)不會(huì)暴露到全局作用域,如果不需要在函數(shù)內(nèi)部引用自身,可以省略函數(shù)的名稱。

你可能會(huì)用到 void 操作符:void function foo(){ }();。但是,這種做法是有問題的。表達(dá)式的值是undefined,所以如果你的 IIFE 有返回值,不要用這種做法。例如:

<pre style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; margin: 0px; padding: 0px; caret-color: rgb(34, 34, 34); color: rgb(34, 34, 34); font-size: 14px; box-sizing: border-box;">const foo = void function bar() { return 'foo'; }();console.log(foo); // undefined</pre>

參考

null、undefined和未聲明變量之間有什么區(qū)別?如何檢查判斷這些狀態(tài)值?

當(dāng)你沒有提前使用var、letconst聲明變量,就為一個(gè)變量賦值時(shí),該變量是未聲明變量(undeclaredvariables)。未聲明變量會(huì)脫離當(dāng)前作用域,成為全局作用域下定義的變量。在嚴(yán)格模式下,給未聲明的變量賦值,會(huì)拋出ReferenceError錯(cuò)誤。和使用全局變量一樣,使用未聲明變量也是非常不好的做法,應(yīng)當(dāng)盡可能避免。要檢查判斷它們,需要將用到它們的代碼放在try/catch語句中。

<pre style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; margin: 0px; padding: 0px; caret-color: rgb(34, 34, 34); color: rgb(34, 34, 34); font-size: 14px; box-sizing: border-box;">function foo() { x = 1; // 在嚴(yán)格模式下,拋出 ReferenceError 錯(cuò)誤}foo();console.log(x); // 1</pre>

當(dāng)一個(gè)變量已經(jīng)聲明,但沒有賦值時(shí),該變量的值是undefined。如果一個(gè)函數(shù)的執(zhí)行結(jié)果被賦值給一個(gè)變量,但是這個(gè)函數(shù)卻沒有返回任何值,那么該變量的值是undefined。要檢查它,需要使用嚴(yán)格相等(===);或者使用typeof,它會(huì)返回'undefined'字符串。請(qǐng)注意,不能使用非嚴(yán)格相等(==)來檢查,因?yàn)槿绻兞恐禐?code>null,使用非嚴(yán)格相等也會(huì)返回true

<pre style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; margin: 0px; padding: 0px; caret-color: rgb(34, 34, 34); color: rgb(34, 34, 34); font-size: 14px; box-sizing: border-box;">var foo;console.log(foo); // undefinedconsole.log(foo === undefined); // trueconsole.log(typeof foo === 'undefined'); // trueconsole.log(foo == null); // true. 錯(cuò)誤,不要使用非嚴(yán)格相等!function bar() {}var baz = bar();console.log(baz); // undefined</pre>

null只能被顯式賦值給變量。它表示空值,與被顯式賦值 undefined 的意義不同。要檢查判斷null值,需要使用嚴(yán)格相等運(yùn)算符。請(qǐng)注意,和前面一樣,不能使用非嚴(yán)格相等(==)來檢查,因?yàn)槿绻兞恐禐?code>undefined,使用非嚴(yán)格相等也會(huì)返回true。

<pre style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; margin: 0px; padding: 0px; caret-color: rgb(34, 34, 34); color: rgb(34, 34, 34); font-size: 14px; box-sizing: border-box;">var foo = null;console.log(foo === null); // trueconsole.log(foo == undefined); // true. 錯(cuò)誤,不要使用非嚴(yán)格相等!</pre>

作為一種個(gè)人習(xí)慣,我從不使用未聲明變量。如果定義了暫時(shí)沒有用到的變量,我會(huì)在聲明后明確地給它們賦值為null

參考

什么是閉包(closure),為什么使用閉包?

閉包是函數(shù)和聲明該函數(shù)的詞法環(huán)境的組合。詞法作用域中使用的域,是變量在代碼中聲明的位置所決定的。閉包是即使被外部函數(shù)返回,依然可以訪問到外部(封閉)函數(shù)作用域的函數(shù)。

為什么使用閉包?

  • 利用閉包實(shí)現(xiàn)數(shù)據(jù)私有化或模擬私有方法。這個(gè)方式也稱為模塊模式(module pattern)。

  • 部分參數(shù)函數(shù)(partial applications)柯里化(currying).

參考

請(qǐng)說明.forEach循環(huán)和.map()循環(huán)的主要區(qū)別,它們分別在什么情況下使用?

為了理解兩者的區(qū)別,我們看看它們分別是做什么的。

forEach

  • 遍歷數(shù)組中的元素。

  • 為每個(gè)元素執(zhí)行回調(diào)。

  • 無返回值。

<pre style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; margin: 0px; padding: 0px; caret-color: rgb(34, 34, 34); color: rgb(34, 34, 34); font-size: 14px; box-sizing: border-box;">const a = [1, 2, 3];const doubled = a.forEach((num, index) => { // 執(zhí)行與 num、index 相關(guān)的代碼});// doubled = undefined</pre>

map

  • 遍歷數(shù)組中的元素

  • 通過對(duì)每個(gè)元素調(diào)用函數(shù),將每個(gè)元素“映射(map)”到一個(gè)新元素,從而創(chuàng)建一個(gè)新數(shù)組。

<pre style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; margin: 0px; padding: 0px; caret-color: rgb(34, 34, 34); color: rgb(34, 34, 34); font-size: 14px; box-sizing: border-box;">const a = [1, 2, 3];const doubled = a.map(num => { return num * 2; });// doubled = [2, 4, 6]</pre>

.forEach.map()的主要區(qū)別在于.map()返回一個(gè)新的數(shù)組。如果你想得到一個(gè)結(jié)果,但不想改變?cè)紨?shù)組,用.map()。如果你只需要在數(shù)組上做迭代修改,用forEach。

參考

匿名函數(shù)的典型應(yīng)用場(chǎng)景是什么?

匿名函數(shù)可以在IIFE中使用,來封裝局部作用域內(nèi)的代碼,以便其聲明的變量不會(huì)暴露到全局作用域。

<pre style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; margin: 0px; padding: 0px; caret-color: rgb(34, 34, 34); color: rgb(34, 34, 34); font-size: 14px; box-sizing: border-box;">(function() { // 一些代碼。})();</pre>

匿名函數(shù)可以作為只用一次,不需要在其他地方使用的回調(diào)函數(shù)。當(dāng)處理函數(shù)在調(diào)用它們的程序內(nèi)部被定義時(shí),代碼具有更好地自閉性和可讀性,可以省去尋找該處理函數(shù)的函數(shù)體位置的麻煩。

<pre style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; margin: 0px; padding: 0px; caret-color: rgb(34, 34, 34); color: rgb(34, 34, 34); font-size: 14px; box-sizing: border-box;">setTimeout(function() { console.log('Hello world!'); }, 1000);</pre>

匿名函數(shù)可以用于函數(shù)式編程或 Lodash(類似于回調(diào)函數(shù))。

<pre style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; margin: 0px; padding: 0px; caret-color: rgb(34, 34, 34); color: rgb(34, 34, 34); font-size: 14px; box-sizing: border-box;">const arr = [1, 2, 3];const double = arr.map(function(el) { return el * 2; });console.log(double); // [2, 4, 6]</pre>

參考

你如何組織自己的代碼?(使用模塊模式(module pattern)還是經(jīng)典繼承(classicalinheritance)?)

我以前使用 Backbone 組織我的模型(model),Backbone 鼓勵(lì)采用面向?qū)ο蟮姆椒ā獎(jiǎng)?chuàng)建 Backbone 模型,并為其添加方法。

模塊模式仍然是很好的方式,但是現(xiàn)在我使用基于 React/Redux 的 Flux 體系結(jié)構(gòu),它鼓勵(lì)使用單向函數(shù)編程的方法。我用普通對(duì)象(plain object)表示我的 app 模型,編寫實(shí)用純函數(shù)去操作這些對(duì)象。使用動(dòng)作(actions)和化簡(jiǎn)器(reducers)來處理狀態(tài),就像其他 Redux 應(yīng)用一樣。

我盡可能避免使用經(jīng)典繼承。如果非要這么做,我會(huì)堅(jiān)持這些原則。

宿主對(duì)象(host objects)和原生對(duì)象(native objects)的區(qū)別是什么?

原生對(duì)象是由 ECMAScript 規(guī)范定義的 JavaScript 內(nèi)置對(duì)象,比如String、MathRegExp、Object、Function等等。

宿主對(duì)象是由運(yùn)行時(shí)環(huán)境(瀏覽器或 Node)提供,比如window、XMLHTTPRequest等等。

參考

下列語句有什么區(qū)別:function Person(){}var person = Person()varperson = new Person()?

這個(gè)問題問得很含糊。我猜這是在考察 JavaScript 中的構(gòu)造函數(shù)(constructor)。從技術(shù)上講,functionPerson(){}只是一個(gè)普通的函數(shù)聲明。使用 PascalCase 方式命名函數(shù)作為構(gòu)造函數(shù),是一個(gè)慣例。

var person = Person()Person以普通函數(shù)調(diào)用,而不是構(gòu)造函數(shù)。如果該函數(shù)是用作構(gòu)造函數(shù)的,那么這種調(diào)用方式是一種常見錯(cuò)誤。通常情況下,構(gòu)造函數(shù)不會(huì)返回任何東西,因此,像普通函數(shù)一樣調(diào)用構(gòu)造函數(shù),只會(huì)返回undefined賦給用作實(shí)例的變量。

var person = new Person()使用new操作符,創(chuàng)建Person對(duì)象的實(shí)例,該實(shí)例繼承自Person.prototype。另外一種方式是使用Object.create,例如:Object.create(Person.prototype)`。

<pre style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; margin: 0px; padding: 0px; caret-color: rgb(34, 34, 34); color: rgb(34, 34, 34); font-size: 14px; box-sizing: border-box;">function Person(name) { this.name = name; }var person = Person('John');console.log(person); // undefinedconsole.log(person.name); // Uncaught TypeError: Cannot read property 'name' of undefinedvar person = new Person('John');console.log(person); // Person { name: "John" }console.log(person.name); // "john"</pre>

參考

.call.apply有什么區(qū)別?

.call.apply都用于調(diào)用函數(shù),第一個(gè)參數(shù)將用作函數(shù)內(nèi)this的值。然而,.call接受逗號(hào)分隔的參數(shù)作為后面的參數(shù),而.apply接受一個(gè)參數(shù)數(shù)組作為后面的參數(shù)。一個(gè)簡(jiǎn)單的記憶方法是,從call中的 C 聯(lián)想到逗號(hào)分隔(comma-separated),從apply中的 A 聯(lián)想到數(shù)組(array)。

<pre style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; margin: 0px; padding: 0px; caret-color: rgb(34, 34, 34); color: rgb(34, 34, 34); font-size: 14px; box-sizing: border-box;">function add(a, b) { return a + b; }console.log(add.call(null, 1, 2)); // 3console.log(add.apply(null, [1, 2])); // 3</pre>

請(qǐng)說明Function.prototype.bind的用法。

摘自MDN:

bind()方法創(chuàng)建一個(gè)新的函數(shù), 當(dāng)被調(diào)用時(shí),將其this關(guān)鍵字設(shè)置為提供的值,在調(diào)用新函數(shù)時(shí),在任何提供之前提供一個(gè)給定的參數(shù)序列。

根據(jù)我的經(jīng)驗(yàn),將this的值綁定到想要傳遞給其他函數(shù)的類的方法中是非常有用的。在React組件中經(jīng)常這樣做。

參考

什么時(shí)候會(huì)用到document.write()?

document.write()用來將一串文本寫入由document.open()打開的文檔流中。當(dāng)頁面加載后執(zhí)行document.write()時(shí),它將調(diào)用document.open,會(huì)清除整個(gè)文檔(<head><body>會(huì)被移除!),并將文檔內(nèi)容替換成給定的字符串參數(shù)。因此它通常被認(rèn)為是危險(xiǎn)的并且容易被誤用。

網(wǎng)上有一些答案,解釋了document.write()被用于分析代碼中,或者當(dāng)你想包含只有在啟用了 JavaScript 的情況下才能工作的樣式。它甚至在HTML5樣板代碼中用于并行加載腳本并保持執(zhí)行順序!但是,我懷疑這些使用原因是過時(shí)的,現(xiàn)在可以在不使用document.write()的情況下實(shí)現(xiàn)。如果我的觀點(diǎn)有錯(cuò),請(qǐng)糾正我。

參考

功能檢測(cè)(feature detection)、功能推斷(feature inference)和使用 UA 字符串之間有什么區(qū)別?

功能檢測(cè)(feature detection)

功能檢測(cè)包括確定瀏覽器是否支持某段代碼,以及是否運(yùn)行不同的代碼(取決于它是否執(zhí)行),以便瀏覽器始終能夠正常運(yùn)行代碼功能,而不會(huì)在某些瀏覽器中出現(xiàn)崩潰和錯(cuò)誤。例如:

<pre style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; margin: 0px; padding: 0px; caret-color: rgb(34, 34, 34); color: rgb(34, 34, 34); font-size: 14px; box-sizing: border-box;">if ('geolocation' in navigator) { // 可以使用 navigator.geolocation} else { // 處理 navigator.geolocation 功能缺失}</pre>

Modernizr是處理功能檢測(cè)的優(yōu)秀工具。

功能推斷(feature inference)

功能推斷與功能檢測(cè)一樣,會(huì)對(duì)功能可用性進(jìn)行檢查,但是在判斷通過后,還會(huì)使用其他功能,因?yàn)樗僭O(shè)其他功能也可用,例如:

<pre style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; margin: 0px; padding: 0px; caret-color: rgb(34, 34, 34); color: rgb(34, 34, 34); font-size: 14px; box-sizing: border-box;">if (document.getElementsByTagName) { element = document.getElementById(id); }</pre>

非常不推薦這種方式。功能檢測(cè)更能保證萬無一失。

UA 字符串

這是一個(gè)瀏覽器報(bào)告的字符串,它允許網(wǎng)絡(luò)協(xié)議對(duì)等方(network protocol peers)識(shí)別請(qǐng)求用戶代理的應(yīng)用類型、操作系統(tǒng)、應(yīng)用供應(yīng)商和應(yīng)用版本。它可以通過navigator.userAgent訪問。 然而,這個(gè)字符串很難解析并且很可能存在欺騙性。例如,Chrome 會(huì)同時(shí)作為 Chrome 和 Safari 進(jìn)行報(bào)告。因此,要檢測(cè) Safari,除了檢查 Safari 字符串,還要檢查是否存在 Chrome 字符串。不要使用這種方式。

參考

請(qǐng)盡可能詳細(xì)地解釋 Ajax。

Ajax(asynchronous JavaScript and XML)是使用客戶端上的許多Web技術(shù),創(chuàng)建異步Web應(yīng)用的一種Web開發(fā)技術(shù)。借助Ajax,Web應(yīng)用可以異步(在后臺(tái))向服務(wù)器發(fā)送數(shù)據(jù)和從服務(wù)器檢索數(shù)據(jù),而不會(huì)干擾現(xiàn)有頁面的顯示和行為。通過將數(shù)據(jù)交換層與表示層分離,Ajax允許網(wǎng)頁和擴(kuò)展Web應(yīng)用程序動(dòng)態(tài)更改內(nèi)容,而無需重新加載整個(gè)頁面。實(shí)際上,現(xiàn)在通常將JSON替換為XML,因?yàn)?JavaScript 對(duì) JSON 有原生支持優(yōu)勢(shì)。

XMLHttpRequest API經(jīng)常用于異步通信。此外還有最近流行的fetch API。

參考

使用Ajax的優(yōu)缺點(diǎn)分別是什么?

優(yōu)點(diǎn)

  • 交互性更好。來自服務(wù)器的新內(nèi)容可以動(dòng)態(tài)更改,無需重新加載整個(gè)頁面。

  • 減少與服務(wù)器的連接,因?yàn)槟_本和樣式只需要被請(qǐng)求一次。

  • 狀態(tài)可以維護(hù)在一個(gè)頁面上。JavaScript 變量和 DOM 狀態(tài)將得到保持,因?yàn)橹魅萜黜撁嫖幢恢匦录虞d。

  • 基本上包括大部分 SPA 的優(yōu)點(diǎn)。

缺點(diǎn)

  • 動(dòng)態(tài)網(wǎng)頁很難收藏。

  • 如果 JavaScript 已在瀏覽器中被禁用,則不起作用。

  • 有些網(wǎng)絡(luò)爬蟲不執(zhí)行 JavaScript,也不會(huì)看到 JavaScript 加載的內(nèi)容。

  • 基本上包括大部分 SPA 的缺點(diǎn)。

請(qǐng)說明 JSONP 的工作原理,它為什么不是真正的 Ajax?

JSONP(帶填充的JSON)是一種通常用于繞過Web瀏覽器中的跨域限制的方法,因?yàn)?Ajax 不允許跨域請(qǐng)求。

JSONP 通過<script>標(biāo)簽發(fā)送跨域請(qǐng)求,通常使用callback查詢參數(shù),例如:https://example.com?callback=printData。 然后服務(wù)器將數(shù)據(jù)包裝在一個(gè)名為printData的函數(shù)中并將其返回給客戶端。

<pre style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; margin: 0px; padding: 0px; caret-color: rgb(34, 34, 34); color: rgb(34, 34, 34); font-size: 14px; box-sizing: border-box;"><script>function printData(data) { console.log(My name is ${data.name}!);}</script> <script src="https://example.com?callback=printData"></script></pre>

<pre style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; margin: 0px; padding: 0px; caret-color: rgb(34, 34, 34); color: rgb(34, 34, 34); font-size: 14px; box-sizing: border-box;">// 文件加載自 https://example.com?callback=printDataprintData({ name: 'Yang Shun' });</pre>

客戶端必須在其全局范圍內(nèi)具有printData函數(shù),并且在收到來自跨域的響應(yīng)時(shí),該函數(shù)將由客戶端執(zhí)行。

JSONP 可能具有一些安全隱患。由于 JSONP 是純 JavaScript 實(shí)現(xiàn),它可以完成 JavaScript 所能做的一切,因此需要信任 JSONP 數(shù)據(jù)的提供者。

現(xiàn)如今,跨來源資源共享(CORS) 是推薦的主流方式,JSONP 已被視為一種比較 hack 的方式。

參考

你使用過 JavaScript 模板嗎?用過什么相關(guān)的庫(kù)?

使用過。Handlebars、Underscore、Lodash、AngularJS和JSX。我不喜歡 AngularJS 中的模板,因?yàn)樗谥噶钪写罅渴褂昧俗址?,并且書寫錯(cuò)誤會(huì)被忽略。JSX是我的新寵,因?yàn)樗咏?JavaScript,幾乎沒有什么學(xué)習(xí)成本?,F(xiàn)在,可以使用 ES2015 模板字符串快速創(chuàng)建模板,而不需依賴第三方代碼。

<pre style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; margin: 0px; padding: 0px; caret-color: rgb(34, 34, 34); color: rgb(34, 34, 34); font-size: 14px; box-sizing: border-box;">const template = <div>My name is: ${name}</div>;</pre>

但是,請(qǐng)注意上述方法中可能存在的 XSS,因?yàn)閮?nèi)容不會(huì)被轉(zhuǎn)義,與模板庫(kù)不同。

請(qǐng)解釋變量提升(hosting)。

變量提升(hoisting)是用于解釋代碼中變量聲明行為的術(shù)語。使用var關(guān)鍵字聲明或初始化的變量,會(huì)將聲明語句“提升”到當(dāng)前作用域的頂部。 但是,只有聲明才會(huì)觸發(fā)提升,賦值語句(如果有的話)將保持原樣。我們用幾個(gè)例子來解釋一下。

<pre style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; margin: 0px; padding: 0px; caret-color: rgb(34, 34, 34); color: rgb(34, 34, 34); font-size: 14px; box-sizing: border-box;">// 用 var 聲明得到提升console.log(foo); // undefinedvar foo = 1;console.log(foo); // 1// 用 let/const 聲明不會(huì)提升console.log(bar); // ReferenceError: bar is not definedlet bar = 2;console.log(bar); // 2</pre>

函數(shù)聲明會(huì)使函數(shù)體提升,但函數(shù)表達(dá)式(以聲明變量的形式書寫)只有變量聲明會(huì)被提升。

<pre style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; margin: 0px; padding: 0px; caret-color: rgb(34, 34, 34); color: rgb(34, 34, 34); font-size: 14px; box-sizing: border-box;">// 函數(shù)聲明console.log(foo); // [Function: foo]foo(); // 'FOOOOO'function foo() { console.log('FOOOOO'); }console.log(foo); // [Function: foo]// 函數(shù)表達(dá)式console.log(bar); // undefinedbar(); // Uncaught TypeError: bar is not a functionvar bar = function() { console.log('BARRRR'); };console.log(bar); // [Function: bar]</pre>

請(qǐng)描述事件冒泡。

當(dāng)一個(gè)事件在DOM元素上觸發(fā)時(shí),如果有事件監(jiān)聽器,它將嘗試處理該事件,然后事件冒泡到其父級(jí)元素,并發(fā)生同樣的事情。最后直到事件到達(dá)祖先元素。事件冒泡是實(shí)現(xiàn)事件委托的原理(event delegation)。

“attribute” 和 “property” 之間有什么區(qū)別?

“Attribute” 是在 HTML 中定義的,而 “property” 是在 DOM 上定義的。為了說明區(qū)別,假設(shè)我們?cè)?HTML 中有一個(gè)文本框:<input type="text" value="Hello">。

<pre style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; margin: 0px; padding: 0px; caret-color: rgb(34, 34, 34); color: rgb(34, 34, 34); font-size: 14px; box-sizing: border-box;">const input = document.querySelector('input');console.log(input.getAttribute('value')); // Helloconsole.log(input.value); // Hello</pre>

但是在文本框中鍵入“ World!”后:

<pre style="font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; text-decoration: none; margin: 0px; padding: 0px; caret-color: rgb(34, 34, 34); color: rgb(34, 34, 34); font-size: 14px; box-sizing: border-box;">console.log(input.getAttribute('value')); // Helloconsole.log(input.value); // Hello World!</pre>

參考

為什么擴(kuò)展 JavaScript 內(nèi)置對(duì)象是不好的做法?

擴(kuò)展 JavaScript 內(nèi)置(原生)對(duì)象意味著將屬性或方法添加到其prototype中。雖然聽起來很不錯(cuò),但事實(shí)上這樣做很危險(xiǎn)。想象一下,你的代碼使用了一些庫(kù),它們通過添加相同的 contains 方法來擴(kuò)展Array.prototype,如果這兩個(gè)方法的行為不相同,那么這些實(shí)現(xiàn)將會(huì)相互覆蓋,你的代碼將不能正常運(yùn)行。

擴(kuò)展內(nèi)置對(duì)象的唯一使用場(chǎng)景是創(chuàng)建 polyfill,本質(zhì)上為老版本瀏覽器缺失的方法提供自己的實(shí)現(xiàn),該方法是由JavaScript 規(guī)范定義的。

參考

document 中的load事件和DOMContentLoaded事件之間的區(qū)別是什么?

當(dāng)初始的 HTML 文檔被完全加載和解析完成之后,DOMContentLoaded事件被觸發(fā),而無需等待樣式表、圖像和子框架的完成加載。

windowload事件僅在DOM和所有相關(guān)資源全部完成加載后才會(huì)觸發(fā)。

參考

未完,請(qǐng)繼續(xù)閱讀 前端面試整理-Javascipt問題(二)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容