本章節(jié)是前端開發(fā)者面試問題 - JS 部分的參考答案。 歡迎提出建議和指正默责!
請解釋事件委托(event delegation)囱稽。
請簡述
JavaScript
中的this
惜索。請解釋原型繼承(prototypal inheritance)的工作原理派昧。
說說你對 AMD 和 CommonJS 的了解黔姜。
請解釋下面代碼為什么不能用作 IIFE:
function foo(){ }();
拢切,需要作出哪些修改才能使其成為 IIFE蒂萎?null
、undefined
和未聲明變量之間有什么區(qū)別淮椰?如何檢查判斷這些狀態(tài)值五慈?什么是閉包(closure),為什么使用閉包主穗?
請說明
.forEach
循環(huán)和.map()
循環(huán)的主要區(qū)別泻拦,它們分別在什么情況下使用?匿名函數(shù)的典型應(yīng)用場景是什么忽媒?
你如何組織自己的代碼争拐?(使用模塊模式(module pattern)還是經(jīng)典繼承(classical inheritance)?)
宿主對象(host objects)和原生對象(native objects)的區(qū)別是什么晦雨?
下列語句有什么區(qū)別:
function Person(){}
架曹、var person = Person()
和var person = newPerson()
?.call
和.apply
有什么區(qū)別闹瞧?請說明
Function.prototype.bind
的用法绑雄。什么時(shí)候會用到
document.write()
?功能檢測(feature detection)奥邮、功能推斷(feature inference)和使用 UA 字符串之間有什么區(qū)別万牺?
請盡可能詳細(xì)地解釋 Ajax。
使用Ajax的優(yōu)缺點(diǎn)分別是什么洽腺?
請說明 JSONP 的工作原理脚粟,它為什么不是真正的 Ajax?
你使用過 JavaScript 模板嗎蘸朋?用過什么相關(guān)的庫核无?
請解釋變量提升(hosting)。
請描述事件冒泡度液。
“attribute” 和 “property” 之間有什么區(qū)別厕宗?
為什么擴(kuò)展 JavaScript 內(nèi)置對象是不好的做法画舌?
document 中的
load
事件和DOMContentLoaded
事件之間的區(qū)別是什么?==
和===
的區(qū)別是什么已慢?請解釋關(guān)于 JavaScript 的同源策略曲聂。
請使下面的語句生效:
請說明三元表達(dá)式中“三元”這個(gè)詞代表什么?
什么是
"use strict";
佑惠?使用它有什么優(yōu)缺點(diǎn)朋腋?創(chuàng)建一個(gè)循環(huán),從1迭代到100膜楷,
3
的倍數(shù)時(shí)輸出 "fizz"旭咽,5
的倍數(shù)時(shí)輸出 "buzz",同時(shí)為3
和5
的倍數(shù)時(shí)輸出"fizzbuzz"赌厅。為什么不要使用全局作用域穷绵?
為什么要使用
load
事件?這個(gè)事件有什么缺點(diǎn)嗎特愿?你知道一些代替方案嗎仲墨,為什么使用它們?請解釋單頁應(yīng)用是什么揍障,如何使其對SEO友好目养。
你對 Promises 及其 polyfill 的掌握程度如何?
Promise
代替回調(diào)函數(shù)有什么優(yōu)缺點(diǎn)毒嫡?用轉(zhuǎn)譯成 JavaScript 的語言寫 JavaScript 有什么優(yōu)缺點(diǎn)癌蚁?
你使用什么工具和技巧調(diào)試 JavaScript 代碼?
你使用什么語句遍歷對象的屬性和數(shù)組的元素兜畸?
請解釋可變對象和不可變對象之間的區(qū)別努释。
請解釋同步和異步函數(shù)之間的區(qū)別。
什么是事件循環(huán)膳叨?調(diào)用堆棧和任務(wù)隊(duì)列之間有什么區(qū)別洽洁?
請解釋
function foo() {}
和var foo = function() {}
之間foo
的用法上的區(qū)別。使用
let
菲嘴、var
和const
創(chuàng)建變量有什么區(qū)別饿自?ES6 的類和 ES5 的構(gòu)造函數(shù)有什么區(qū)別?
你能給出一個(gè)使用箭頭函數(shù)的例子嗎龄坪,箭頭函數(shù)與其他函數(shù)有什么不同昭雌?
在構(gòu)造函數(shù)中使用箭頭函數(shù)有什么好處?
高階函數(shù)(higher-order)的定義是什么健田?
請給出一個(gè)解構(gòu)(destructuring)對象或數(shù)組的例子烛卧。
ES6 的模板字符串為生成字符串提供了很大的靈活性,你可以舉個(gè)例子嗎?
你能舉出一個(gè)柯里化函數(shù)(curry function)的例子嗎总放?它有哪些好處呈宇?
使用擴(kuò)展運(yùn)算符(spread)的好處是什么,它與使用剩余參數(shù)語句(rest)有什么區(qū)別局雄?
如何在文件之間共用代碼甥啄?
什么情況下會用到靜態(tài)類成員?
請解釋事件委托(event delegation)炬搭。
事件委托是將事件監(jiān)聽器添加到父元素蜈漓,而不是每個(gè)子元素單獨(dú)設(shè)置事件監(jiān)聽器。當(dāng)觸發(fā)子元素時(shí)宫盔,事件會冒泡到父元素融虽,監(jiān)聽器就會觸發(fā)。這種技術(shù)的好處是:
內(nèi)存占用減少灼芭,因?yàn)橹恍枰粋€(gè)父元素的事件處理程序有额,而不必為每個(gè)后代都添加事件處理程序。
無需從已刪除的元素中解綁處理程序姿鸿,也無需將處理程序綁定到新元素上谆吴。
參考
請簡述JavaScript
中的this
。
JS 中的this
是一個(gè)相對復(fù)雜的概念苛预,不是簡單幾句能解釋清楚的。粗略地講笋熬,函數(shù)的調(diào)用方式?jīng)Q定了this
的值热某。我閱讀了網(wǎng)上很多關(guān)于this
的文章,Arnav Aggrawal 寫的比較清楚胳螟。this
取值符合以下規(guī)則:
在調(diào)用函數(shù)時(shí)使用
new
關(guān)鍵字评也,函數(shù)內(nèi)的this
是一個(gè)全新的對象粟焊。如果
apply
、call
或bind
方法用于調(diào)用、創(chuàng)建一個(gè)函數(shù)胯府,函數(shù)內(nèi)的 this 就是作為參數(shù)傳入這些方法的對象。當(dāng)函數(shù)作為對象里的方法被調(diào)用時(shí)渐尿,函數(shù)內(nèi)的
this
是調(diào)用該函數(shù)的對象诽凌。比如當(dāng)obj.method()
被調(diào)用時(shí),函數(shù)內(nèi)的this將綁定到obj
對象舍扰。如果調(diào)用函數(shù)不符合上述規(guī)則倦蚪,那么
this
的值指向全局對象(global object)。瀏覽器環(huán)境下this
的值指向window
對象边苹,但是在嚴(yán)格模式下('use strict'
)陵且,this
的值為undefined
。如果符合上述多個(gè)規(guī)則个束,則較高的規(guī)則(1號最高慕购,4號最低)將決定
this
的值聊疲。如果該函數(shù)是 ES2015 中的箭頭函數(shù),將忽略上面的所有規(guī)則沪悲,
this
被設(shè)置為它被創(chuàng)建時(shí)的上下文售睹。
想獲得更深入的解釋,請查看他在 Medium 上的文章可训。
參考
請解釋原型繼承(prototypal inheritance)的工作原理昌妹。
這是一個(gè)非常常見的 JavaScript 問題。所有 JS 對象都有一個(gè)prototype
屬性握截,指向它的原型對象飞崖。當(dāng)試圖訪問一個(gè)對象的屬性時(shí),如果沒有在該對象上找到谨胞,它還會搜尋該對象的原型固歪,以及該對象的原型的原型,依次層層向上搜索胯努,直到找到一個(gè)名字匹配的屬性或到達(dá)原型鏈的末尾牢裳。這種行為是在模擬經(jīng)典的繼承,但是與其說是繼承叶沛,不如說是委托(delegation)蒲讯。
參考
說說你對 AMD 和 CommonJS 的了解。
它們都是實(shí)現(xiàn)模塊體系的方式灰署,直到 ES2015 出現(xiàn)之前判帮,JavaScript 一直沒有模塊體系。CommonJS 是同步的溉箕,而AMD(Asynchronous Module Definition)從全稱中可以明顯看出是異步的晦墙。CommonJS 的設(shè)計(jì)是為服務(wù)器端開發(fā)考慮的,而 AMD 支持異步加載模塊肴茄,更適合瀏覽器晌畅。
我發(fā)現(xiàn) AMD 的語法非常冗長,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)換。
參考
請解釋下面代碼為什么不能用作 IIFE:function foo(){ }();
郑气,需要作出哪些修改才能使其成為 IIFE幅垮?
IIFE(Immediately Invoked Function Expressions)代表立即執(zhí)行函數(shù)。 JavaScript 解析器將 function foo(){ }();
解析成function foo(){ }
和();
尾组。其中忙芒,前者是函數(shù)聲明;后者(一對括號)是試圖調(diào)用一個(gè)函數(shù)讳侨,卻沒有指定名稱呵萨,因此它會拋出Uncaught SyntaxError: Unexpected token )
的錯(cuò)誤。
修改方法是:再添加一對括號跨跨,形式上有兩種:(function foo(){ })()
和(function foo(){ }())
潮峦。以上函數(shù)不會暴露到全局作用域,如果不需要在函數(shù)內(nèi)部引用自身勇婴,可以省略函數(shù)的名稱忱嘹。
你可能會用到 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>
參考
http://lucybain.com/blog/2014/immediately-invoked-function-expression/
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void
null
、undefined
和未聲明變量之間有什么區(qū)別慰技?如何檢查判斷這些狀態(tài)值?
當(dāng)你沒有提前使用var
组砚、let
或const
聲明變量吻商,就為一個(gè)變量賦值時(shí),該變量是未聲明變量(undeclaredvariables)糟红。未聲明變量會脫離當(dāng)前作用域艾帐,成為全局作用域下定義的變量。在嚴(yán)格模式下盆偿,給未聲明的變量賦值柒爸,會拋出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
旋圆,它會返回'undefined'
字符串。請注意矛纹,不能使用非嚴(yán)格相等(==
)來檢查臂聋,因?yàn)槿绻兞恐禐?code>null,使用非嚴(yán)格相等也會返回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)算符权她。請注意,和前面一樣逝薪,不能使用非嚴(yán)格相等(==
)來檢查隅要,因?yàn)槿绻兞恐禐?code>undefined,使用非嚴(yán)格相等也會返回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í)沒有用到的變量,我會在聲明后明確地給它們賦值為null
封豪。
參考
https://stackoverflow.com/questions/15985875/effect-of-declared-and-undeclared-variables
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/undefined
什么是閉包(closure)谴轮,為什么使用閉包?
閉包是函數(shù)和聲明該函數(shù)的詞法環(huán)境的組合吹埠。詞法作用域中使用的域第步,是變量在代碼中聲明的位置所決定的疮装。閉包是即使被外部函數(shù)返回,依然可以訪問到外部(封閉)函數(shù)作用域的函數(shù)雌续。
為什么使用閉包斩个?
利用閉包實(shí)現(xiàn)數(shù)據(jù)私有化或模擬私有方法。這個(gè)方式也稱為模塊模式(module pattern)驯杜。
部分參數(shù)函數(shù)(partial applications)柯里化(currying).
參考
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures
https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-closure-b2f0d2152b36
請說明.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ù)組中的元素
通過對每個(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é)果最住,但不想改變原始數(shù)組,用.map()
怠惶。如果你只需要在數(shù)組上做迭代修改涨缚,用forEach
。
參考
匿名函數(shù)的典型應(yīng)用場景是什么策治?
匿名函數(shù)可以在IIFE中使用脓魏,來封裝局部作用域內(nèi)的代碼,以便其聲明的變量不會暴露到全局作用域通惫。
<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ū)ο蟮姆椒ā獎?chuàng)建 Backbone 模型载矿,并為其添加方法垄潮。
模塊模式仍然是很好的方式烹卒,但是現(xiàn)在我使用基于 React/Redux 的 Flux 體系結(jié)構(gòu),它鼓勵(lì)使用單向函數(shù)編程的方法弯洗。我用普通對象(plain object)表示我的 app 模型旅急,編寫實(shí)用純函數(shù)去操作這些對象。使用動作(actions)和化簡器(reducers)來處理狀態(tài)牡整,就像其他 Redux 應(yīng)用一樣藐吮。
我盡可能避免使用經(jīng)典繼承。如果非要這么做逃贝,我會堅(jiān)持這些原則谣辞。
宿主對象(host objects)和原生對象(native objects)的區(qū)別是什么?
原生對象是由 ECMAScript 規(guī)范定義的 JavaScript 內(nèi)置對象沐扳,比如String
泥从、Math
、RegExp
沪摄、Object
躯嫉、Function
等等。
宿主對象是由運(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ù)不會返回任何東西胖翰,因此,像普通函數(shù)一樣調(diào)用構(gòu)造函數(shù)切厘,只會返回undefined
賦給用作實(shí)例的變量萨咳。
var person = new Person()
使用new
操作符,創(chuàng)建Person
對象的實(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
接受逗號分隔的參數(shù)作為后面的參數(shù)馋记,而.apply
接受一個(gè)參數(shù)數(shù)組作為后面的參數(shù)。一個(gè)簡單的記憶方法是螃壤,從call
中的 C 聯(lián)想到逗號分隔(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>
請說明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í)候會用到document.write()
?
document.write()
用來將一串文本寫入由document.open()
打開的文檔流中乙帮。當(dāng)頁面加載后執(zhí)行document.write()
時(shí)杜漠,它將調(diào)用document.open
,會清除整個(gè)文檔(<head>
和<body>
會被移除2炀弧)驾茴,并將文檔內(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ò),請糾正我阁吝。
參考
https://www.quirksmode.org/blog/archives/2005/06/three_javascrip_1.html
https://github.com/h5bp/html5-boilerplate/wiki/Script-Loading-Techniques#documentwrite-script-tag
功能檢測(feature detection)睛竣、功能推斷(feature inference)和使用 UA 字符串之間有什么區(qū)別?
功能檢測(feature detection)
功能檢測包括確定瀏覽器是否支持某段代碼求摇,以及是否運(yùn)行不同的代碼(取決于它是否執(zhí)行)射沟,以便瀏覽器始終能夠正常運(yùn)行代碼功能,而不會在某些瀏覽器中出現(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是處理功能檢測的優(yōu)秀工具验夯。
功能推斷(feature inference)
功能推斷與功能檢測一樣,會對功能可用性進(jìn)行檢查摔刁,但是在判斷通過后挥转,還會使用其他功能,因?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>
非常不推薦這種方式绑谣。功能檢測更能保證萬無一失。
UA 字符串
這是一個(gè)瀏覽器報(bào)告的字符串拗引,它允許網(wǎng)絡(luò)協(xié)議對等方(network protocol peers)識別請求用戶代理的應(yīng)用類型借宵、操作系統(tǒng)、應(yīng)用供應(yīng)商和應(yīng)用版本矾削。它可以通過navigator.userAgent
訪問壤玫。 然而,這個(gè)字符串很難解析并且很可能存在欺騙性哼凯。例如欲间,Chrome 會同時(shí)作為 Chrome 和 Safari 進(jìn)行報(bào)告。因此断部,要檢測 Safari猎贴,除了檢查 Safari 字符串,還要檢查是否存在 Chrome 字符串蝴光。不要使用這種方式她渴。
參考
請盡可能詳細(xì)地解釋 Ajax。
Ajax(asynchronous JavaScript and XML)是使用客戶端上的許多Web技術(shù)虱疏,創(chuàng)建異步Web應(yīng)用的一種Web開發(fā)技術(shù)惹骂。借助Ajax,Web應(yīng)用可以異步(在后臺)向服務(wù)器發(fā)送數(shù)據(jù)和從服務(wù)器檢索數(shù)據(jù)做瞪,而不會干擾現(xiàn)有頁面的顯示和行為对粪。通過將數(shù)據(jù)交換層與表示層分離,Ajax允許網(wǎng)頁和擴(kuò)展Web應(yīng)用程序動態(tài)更改內(nèi)容装蓬,而無需重新加載整個(gè)頁面著拭。實(shí)際上,現(xiàn)在通常將JSON替換為XML牍帚,因?yàn)?JavaScript 對 JSON 有原生支持優(yōu)勢儡遮。
XMLHttpRequest
API經(jīng)常用于異步通信。此外還有最近流行的fetch
API暗赶。
參考
使用Ajax的優(yōu)缺點(diǎn)分別是什么鄙币?
優(yōu)點(diǎn)
交互性更好肃叶。來自服務(wù)器的新內(nèi)容可以動態(tài)更改,無需重新加載整個(gè)頁面十嘿。
減少與服務(wù)器的連接因惭,因?yàn)槟_本和樣式只需要被請求一次。
狀態(tài)可以維護(hù)在一個(gè)頁面上绩衷。JavaScript 變量和 DOM 狀態(tài)將得到保持蹦魔,因?yàn)橹魅萜黜撁嫖幢恢匦录虞d。
基本上包括大部分 SPA 的優(yōu)點(diǎn)咳燕。
缺點(diǎn)
動態(tài)網(wǎng)頁很難收藏勿决。
如果 JavaScript 已在瀏覽器中被禁用,則不起作用招盲。
有些網(wǎng)絡(luò)爬蟲不執(zhí)行 JavaScript低缩,也不會看到 JavaScript 加載的內(nèi)容。
基本上包括大部分 SPA 的缺點(diǎn)宪肖。
請說明 JSONP 的工作原理表制,它為什么不是真正的 Ajax?
JSONP(帶填充的JSON)是一種通常用于繞過Web瀏覽器中的跨域限制的方法控乾,因?yàn)?Ajax 不允許跨域請求么介。
JSONP 通過<script>
標(biāo)簽發(fā)送跨域請求,通常使用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)的庫?
使用過减余。Handlebars综苔、Underscore、Lodash、AngularJS和JSX如筛。我不喜歡 AngularJS 中的模板堡牡,因?yàn)樗谥噶钪写罅渴褂昧俗址⑶視鴮戝e(cuò)誤會被忽略妙黍。JSX是我的新寵悴侵,因?yàn)樗咏?JavaScript,幾乎沒有什么學(xué)習(xí)成本∈眉蓿現(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>
但是,請注意上述方法中可能存在的 XSS捉撮,因?yàn)閮?nèi)容不會被轉(zhuǎn)義怕品,與模板庫不同。
請解釋變量提升(hosting)巾遭。
變量提升(hoisting)是用于解釋代碼中變量聲明行為的術(shù)語肉康。使用var
關(guān)鍵字聲明或初始化的變量,會將聲明語句“提升”到當(dāng)前作用域的頂部灼舍。 但是吼和,只有聲明才會觸發(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 聲明不會提升console.log(bar); // ReferenceError: bar is not definedlet bar = 2;console.log(bar); // 2</pre>
函數(shù)聲明會使函數(shù)體提升,但函數(shù)表達(dá)式(以聲明變量的形式書寫)只有變量聲明會被提升献丑。
<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>
請描述事件冒泡末捣。
當(dāng)一個(gè)事件在DOM元素上觸發(fā)時(shí),如果有事件監(jiān)聽器创橄,它將嘗試處理該事件箩做,然后事件冒泡到其父級元素,并發(fā)生同樣的事情妥畏。最后直到事件到達(dá)祖先元素邦邦。事件冒泡是實(shí)現(xiàn)事件委托的原理(event delegation)。
“attribute” 和 “property” 之間有什么區(qū)別咖熟?
“Attribute” 是在 HTML 中定義的圃酵,而 “property” 是在 DOM 上定義的。為了說明區(qū)別馍管,假設(shè)我們在 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)置對象是不好的做法?
擴(kuò)展 JavaScript 內(nèi)置(原生)對象意味著將屬性或方法添加到其prototype
中。雖然聽起來很不錯(cuò)捌锭,但事實(shí)上這樣做很危險(xiǎn)俘陷。想象一下,你的代碼使用了一些庫观谦,它們通過添加相同的 contains 方法來擴(kuò)展Array.prototype
拉盾,如果這兩個(gè)方法的行為不相同,那么這些實(shí)現(xiàn)將會相互覆蓋豁状,你的代碼將不能正常運(yùn)行捉偏。
擴(kuò)展內(nèi)置對象的唯一使用場景是創(chuàng)建 polyfill,本質(zhì)上為老版本瀏覽器缺失的方法提供自己的實(shí)現(xiàn)泻红,該方法是由JavaScript 規(guī)范定義的夭禽。
參考
document 中的load
事件和DOMContentLoaded
事件之間的區(qū)別是什么?
當(dāng)初始的 HTML 文檔被完全加載和解析完成之后谊路,DOMContentLoaded
事件被觸發(fā)讹躯,而無需等待樣式表、圖像和子框架的完成加載缠劝。
window
的load
事件僅在DOM和所有相關(guān)資源全部完成加載后才會觸發(fā)潮梯。
參考
未完,請繼續(xù)閱讀 前端面試整理-Javascipt問題(二)