摘要:即為被污染的命名,訪問沒有被聲明的變量,會(huì)拋出異常,終止執(zhí)行。請(qǐng)舉出一個(gè)匿名函數(shù)的典型用例匿名函數(shù)可以用作回調(diào)函數(shù)執(zhí)行,可以防止全局變量污染。在框架中常使用匿名函數(shù)來避免全局變量的污染。
請(qǐng)解釋事件代理 (event delegation)
事件委托技術(shù)能讓你避免對(duì)特定的每個(gè)節(jié)點(diǎn)添加事件監(jiān)聽器;相反,事件監(jiān)聽器是被添加到它們的父元素上。事件監(jiān)聽器會(huì)分析從子元素冒泡上來的事件,找到是哪個(gè)子元素的事件。
優(yōu)點(diǎn):
性能得到了優(yōu)化(需要?jiǎng)?chuàng)建的以及駐留在內(nèi)存中的事件處理器少了)
動(dòng)態(tài)添加的元素也能綁定事件了
請(qǐng)解釋 JavaScript 中 this 是如何工作的this 永遠(yuǎn)指向函數(shù)運(yùn)行時(shí)所在的對(duì)象,而不是函數(shù)被創(chuàng)建時(shí)所在的對(duì)象。
函數(shù)調(diào)用es3和非嚴(yán)格es5為全局對(duì)象,嚴(yán)格es5為undefined
方法調(diào)用this指向調(diào)用該方法的對(duì)象(調(diào)用上下文)
構(gòu)造函數(shù)時(shí),this指向新創(chuàng)建的對(duì)象
call() apply() 調(diào)用方法時(shí),this指向調(diào)用方法的對(duì)象,而不是該方法擁有者對(duì)象
請(qǐng)解釋原型繼承 (prototypal inheritance) 的原理?原型繼承的基礎(chǔ)是原型鏈查找。
原型鏈查找基本概念:
每一個(gè)函數(shù) F 都有一個(gè)原型對(duì)象(prototype)F.prototype
每一個(gè)函數(shù)都可以通過 new 關(guān)鍵字化身成為一個(gè)類構(gòu)造函數(shù),new F 會(huì)產(chǎn)生一個(gè)對(duì)象 O
在調(diào)用對(duì)象的某個(gè)屬性或者方法,比如 http://O.xxx 的時(shí)候,會(huì)首先查找對(duì)象自身是否有這個(gè)方法或者屬性,如果沒找到就會(huì)去對(duì)象的構(gòu)造函數(shù)的原型對(duì)象中查找(注意有兩個(gè)定語),也就是查找 O 的構(gòu)造函數(shù) F 的原型對(duì)象 http://F.prototype.xxx
F.prototype 也是一個(gè)對(duì)象,查找 http://F.prototype.xxx 的時(shí)候會(huì)重復(fù)第 3 步的過程
這里只是聲明一個(gè)叫foo的function,直接用()執(zhí)行這樣是不成功的,想要變成IIFE就要把聲明變成表達(dá)式,就可以立即執(zhí)行了,可以這樣(function foo(){})()或者(function foo(){}()),這就是用括號(hào)把定義強(qiáng)轉(zhuǎn)成表達(dá)式,當(dāng)然還有其他方法,關(guān)鍵就是聲明不可以執(zhí)行,表達(dá)式才可以執(zhí)行。
描述以下變量的區(qū)別:null,undefined 或 undeclared? 該如何檢測(cè)它們?undefined:未定義,在變量沒有賦值的時(shí)候的值即為undefined。“缺少值”,就是此處應(yīng)該有一個(gè)值,但是還沒有定義。
underclared:即為被污染的命名,訪問沒有被聲明的變量,會(huì)拋出異常,終止執(zhí)行。嘗試訪問一個(gè)undeclared的變量,瀏覽器會(huì)報(bào)錯(cuò),JS執(zhí)行會(huì)中斷。
null:是一個(gè)空的對(duì)象引用。“沒有對(duì)象”,即該處不應(yīng)該有值
區(qū)別:
undefined和null在if語句中,都會(huì)被自動(dòng)轉(zhuǎn)為false,相等運(yùn)算符甚至直接報(bào)告兩者相等。typeof undefined會(huì)返回undefined ,而typeof null 總返回 object(typeof有六種可能:“number”、“string”、“boolean”、“object”、“function”、“undefined”)
false == undefined;//false false == null;//false null == undefined;//true
該如何檢測(cè)它們?
var obj; obj ===undefined; //檢測(cè)undfined 方法一 typeof obj === ‘undefined’;//檢測(cè)undefined方法2 obj = null; obj === null;//來檢測(cè)null typeof null;//‘object’什么是閉包 (closure),如何使用它,為什么要使用它?
定義:閉包就是可以讀取到其他函數(shù)內(nèi)部變量的函數(shù)。
閉包的用途:
可以讀取函數(shù)內(nèi)部的變量。(外界無法訪問函數(shù)的內(nèi)部的私有方法和變量,只能通過提供的接口訪問)
讓變量的值始終保持在內(nèi)存中。
可以避免污染全局變量,實(shí)現(xiàn)私有方法或者變量等
注意:
由于閉包會(huì)使得函數(shù)中的變量都被保存在內(nèi)存中,內(nèi)存消耗很大,所以不能濫用閉包,否則會(huì)造成網(wǎng)頁的性能問題,在IE中可能導(dǎo)致內(nèi)存泄露。解決方法是,在退出函數(shù)之前,將不使用的局部變量全部刪除。
閉包會(huì)在父函數(shù)外部,改變父函數(shù)內(nèi)部變量的值。所以,如果你把父函數(shù)當(dāng)作對(duì)象(object)使用,把閉包當(dāng)作它的公用方法(Public Method),把內(nèi)部變量當(dāng)作它的私有屬性(private value),這時(shí)一定要小心,不要隨便改變父函數(shù)內(nèi)部變量的值。
請(qǐng)舉出一個(gè)匿名函數(shù)的典型用例?匿名函數(shù)可以用作回調(diào)函數(shù)執(zhí)行,可以防止全局變量污染。
在 JS 框架中常使用匿名函數(shù)來避免全局變量的污染。
$.(“input”).each(function(e){this.val(‘OK’)});
(function(){})();
$(document).ready(function(){ });
$(function() {})
原生對(duì)象:獨(dú)立于宿主環(huán)境的 ECMAScript 實(shí)現(xiàn)提供的對(duì)象。為array obj regexp date function等可以new實(shí)例化的對(duì)象。
內(nèi)置對(duì)象:為gload Math 等,開發(fā)者不必明確實(shí)例化內(nèi)置對(duì)象,它已被實(shí)例化了。類似于isNaN()、parseInt()和parseFloat()方法等,看起來都是函數(shù),而實(shí)際上,它們都是Global對(duì)象的方法。具體可以參考 JavaScript 全局對(duì)象
宿主對(duì)象:即由 ECMAScript 實(shí)現(xiàn)的宿主環(huán)境(操作系統(tǒng)和瀏覽器)提供的對(duì)象。所有的BOM和DOM對(duì)象都是宿主對(duì)象。因?yàn)槠鋵?duì)于不同的“宿主”環(huán)境所展示的內(nèi)容不同(這就是兼容性和特性檢測(cè)的緣由)。ECMAScript官方未定義的對(duì)象都屬于宿主對(duì)象。
請(qǐng)指出以下代碼的區(qū)別:function Person(){}、var person = Person()、var person = new Person()?第一個(gè)為函數(shù)聲明,第二個(gè)將函數(shù)person()返回值賦值給person,第三個(gè)通過Person()的構(gòu)造器創(chuàng)建了一個(gè)對(duì)象讓person變量引用該對(duì)象;
.call 和 .apply 的區(qū)別是什么?call和apply都是調(diào)用一個(gè)對(duì)象的一個(gè)方法,以另一個(gè)對(duì)象替換當(dāng)前對(duì)象。它們都屬于Function.prototype的一個(gè)方法,所以每個(gè)function實(shí)例都有call和apply屬性。這兩個(gè)方法可以用來代替另一個(gè)對(duì)象調(diào)用一個(gè)方法,可將一個(gè)函數(shù)的對(duì)象上下文從初始的上下文改變?yōu)橛?thisObj 指定的新對(duì)象。
區(qū)別:
兩者傳遞的參數(shù)不同,雖然函數(shù)第一個(gè)參數(shù)都是要傳入給當(dāng)前對(duì)象的對(duì)象,但是,apply的第二個(gè)參數(shù)是一個(gè)參數(shù)數(shù)組,將多個(gè)參數(shù)組合成為一個(gè)數(shù)組傳入;而call第二個(gè)參數(shù)則是直接的參數(shù)列表。
Function.prototype.bind()其實(shí)就是函數(shù)綁定。函數(shù)的接收者取決于他是如何被調(diào)用,可以通過調(diào)用.bind()給函數(shù)綁定作用域上下文(this的值),即函數(shù)的接收者。
var foo = { x: 3} var bar = function(){console.log( this.x);} bar(); // undefinedvar boundFunc = bar.bind(foo);//隱式看作是在foo作用域里調(diào)用bar方法 boundFunc(); // 3
.bind()創(chuàng)建了一個(gè)函數(shù),當(dāng)這個(gè)函數(shù)在被調(diào)用的時(shí)候,它的 this 關(guān)鍵詞會(huì)被設(shè)置成被傳入的值(這里指調(diào)用bind()時(shí)傳入的參數(shù))也就是我們傳入想要的上下文。 簡(jiǎn)單的用法: 關(guān)于 Function.prototype.bind() 內(nèi)部,這里有個(gè)非常簡(jiǎn)單的例子:
Function.prototype.bind = function (scope) { var fn = this; return function () { return fn.apply(scope);//使用call效果一樣 }; }在什么時(shí)候你會(huì)使用 document.write()?
document.write()方法可以用在兩個(gè)方面:
頁面載入過程中用實(shí)時(shí)腳本創(chuàng)建頁面內(nèi)容,該方法需要一個(gè)字符串參數(shù),它是寫到窗口或框架中的HTML內(nèi)容。
以及用延時(shí)腳本創(chuàng)建本窗口或新窗口的內(nèi)容。該方法需要一個(gè)字符串參數(shù),它是寫到窗口或框架中的HTML內(nèi)容。
記住,在載入頁面后,瀏覽器輸出流自動(dòng)關(guān)閉。在此之后,任何一個(gè)對(duì)當(dāng)前頁面進(jìn)行操作的document.write()方法將打開—個(gè)新的輸出流,它將清除當(dāng)前頁面內(nèi)容(包括源文檔的任何變量或值)。因此,假如希望用腳本生成的HTML替換當(dāng)前頁面,就必須把HTML內(nèi)容連接起來賦給一個(gè)變量,使用一個(gè)document.write()方法完成寫操作。不必清除文檔并打開一個(gè)新數(shù)據(jù)流,一個(gè)document.write()調(diào)用就可完成所有的操作。
關(guān)于document.write()方法還有一點(diǎn)要說明的是它的相關(guān)方法document.close()。腳本向窗口(不管是本窗口或其他窗口)寫完內(nèi)容后,必須關(guān)閉輸出流。在延時(shí)腳本的最后一個(gè)document.write()方法后面,必須確保含有document.close()方法,不這樣做就不能顯示圖像和表單。并且,任何后面調(diào)用的document.write()方法只會(huì)把內(nèi)容追加到頁面后,而不會(huì)清除現(xiàn)有內(nèi)容來寫入新值。為了演示document.write()方法,我們提供了同一個(gè)應(yīng)用程序的兩個(gè)版本。
大多數(shù)生成的廣告代碼依舊使用 document.write(),雖然這種用法會(huì)讓人很不爽。
請(qǐng)指出瀏覽器特性檢測(cè),特性推斷和瀏覽器 UA 字符串嗅探的區(qū)別?檢測(cè)瀏覽器的特殊名稱和版本(用戶代理檢測(cè))即瀏覽器UA字符串嗅探。瀏覽器嗅探技術(shù)可以快捷的將代碼進(jìn)行分支,以便針對(duì)不同的瀏覽器應(yīng)用不同的指令;針對(duì)特定瀏覽器的特定版本,超出范圍之外都是不可靠的
請(qǐng)盡可能詳盡的解釋 Ajax 的工作原理? 使用 Ajax 都有哪些優(yōu)劣?優(yōu)勢(shì):可以刷新局部頁面,而不用整體頁面都刷新
缺點(diǎn):用戶禁用javascript的情況
工作原理:JSONP動(dòng)態(tài)創(chuàng)建script標(biāo)簽,回調(diào)函數(shù)。Ajax是頁面無刷新請(qǐng)求數(shù)據(jù)操作,動(dòng)態(tài)添加一個(gè)<script>標(biāo)簽,而script標(biāo)簽的src屬性是沒有跨域的限制的。這樣說來,這種跨域方式其實(shí)與ajax XmlHttpRequest協(xié)議無關(guān)了。
當(dāng)GET請(qǐng)求從被調(diào)用頁面返回時(shí),可以返回一段JavaScript代碼,這段代碼會(huì)自動(dòng)調(diào)用主頁面中的一個(gè)callback函數(shù)。
優(yōu)點(diǎn):不受同源策略的影響,它的兼容性更好,在更加古老的瀏覽器中都可以運(yùn)行,不需要XMLHttpRequest或ActiveX的支持;并且在請(qǐng)求完畢后可以通過調(diào)用callback的方式回傳結(jié)果
缺點(diǎn):只支持GET請(qǐng)求而不支持POST等其它類型的HTTP請(qǐng)求;它只支持跨域HTTP請(qǐng)求這種情況,不能解決不同域的兩個(gè)頁面之間如何進(jìn)行JavaScript調(diào)用的問題。
在JavaScript代碼運(yùn)行之前其實(shí)是有一個(gè)編譯階段的。編譯之后才是從上到下,一行一行解釋執(zhí)行。變量提升就發(fā)生在編譯階段,它把變量和函數(shù)的聲明提升至作用域的頂端。(編譯階段的工作之一就是將變量與其作用域進(jìn)行關(guān)聯(lián))。
變量提升需要注意兩點(diǎn):
提升的部分只是變量聲明,賦值語句和可執(zhí)行的代碼邏輯還保持在原地不動(dòng)
提升只是將變量聲明提升到變量所在的變量范圍的頂端,并不是提升到全局范圍
函數(shù)聲明:
變量聲明和函數(shù)聲明都會(huì)得到變量提升,但函數(shù)聲明會(huì)最先得到提升,然后是變量聲明(函數(shù)是一等公民)
對(duì)于函數(shù)聲明來說,如果定義了相同的函數(shù)變量聲明,后定義的聲明會(huì)覆蓋掉先前的聲明
請(qǐng)描述事件冒泡機(jī)制 (event bubbling)從目標(biāo)元素開始,往頂層元素傳播。途中如果有節(jié)點(diǎn)綁定了相應(yīng)的事件處理函數(shù),這些函數(shù)都會(huì)被依次觸發(fā)。如果想阻止事件起泡,可以使用e.stopPropagation()(Firefox)或者e.cancelBubble=true(IE)來組織事件的冒泡傳播
“attribute” 和 “property” 的區(qū)別是什么?DOM元素的attribute和property兩者是不同的東西。attribute翻譯為“特性”,property翻譯為“屬性”。
attribute是一個(gè)特性節(jié)點(diǎn),每個(gè)DOM元素都有一個(gè)對(duì)應(yīng)的attributes屬性來存放所有的attribute節(jié)點(diǎn),attributes是一個(gè)類數(shù)組的容器,說得準(zhǔn)確點(diǎn)就是NameNodeMap,不繼承于Array.prototype,不能直接調(diào)用Array的方法。attributes的每個(gè)數(shù)字索引以名值對(duì)(name=”value”)的形式存放了一個(gè)attribute節(jié)點(diǎn)。
property就是一個(gè)屬性,如果把DOM元素看成是一個(gè)普通的Object對(duì)象,那么property就是一個(gè)以名值對(duì)(name=”value”)的形式存放在Object中的屬性。要添加和刪除property和普通的對(duì)象類似。
很多attribute節(jié)點(diǎn)還有一個(gè)相對(duì)應(yīng)的property屬性,比如上面的div元素的id和class既是attribute,也有對(duì)應(yīng)的property,不管使用哪種方法都可以訪問和修改。
總之,attribute節(jié)點(diǎn)都是在HTML代碼中可見的,而property只是一個(gè)普通的名值對(duì)屬性
為什么擴(kuò)展 JavaScript 內(nèi)置對(duì)象不是好的做法?因?yàn)槟悴恢滥囊惶鞛g覽器或javascript本身就會(huì)實(shí)現(xiàn)這個(gè)方法,而且和你擴(kuò)展的實(shí)現(xiàn)有不一致的表現(xiàn)。到時(shí)候你的javascript代碼可能已經(jīng)在無數(shù)個(gè)頁面中執(zhí)行了數(shù)年,而瀏覽器的實(shí)現(xiàn)導(dǎo)致所有使用擴(kuò)展原型的代碼都崩潰了。
需要給Array原型添加一個(gè)distinct的方法,最好檢查是否存在同名的方法,避免自定義方法覆蓋原生方法:
Arrray.prototype.distinct = Arrray.prototype.distinct || function(){/…../}請(qǐng)指出 document load 和 document DOMContentLoaded 兩個(gè)事件的區(qū)別。
ready 表示文檔的 DOM 已經(jīng)加載完成(不包含圖片、視頻等資源);load 表示整個(gè)網(wǎng)頁加載完成。可以看出,ready 事件發(fā)生在 load 事件之前。
== 和 === 有什么不同?如果兩邊的操作數(shù)具有一致的類型且擁有相同的值時(shí),=== 返回 true,!== 返回 false。
請(qǐng)解釋 JavaScript 的同源策略 (same-origin policy)。同源策略限制了一個(gè)源(origin)中加載文本或腳本與來自其它源(origin)中資源的交互方式。
同源策略出于安全,不允許源 A 的腳本讀取(read)源 B 的資源的內(nèi)容,但卻允許執(zhí)行(execute)源 B 的資源。這個(gè)概念也有些拗口。簡(jiǎn)單說,有一個(gè)頁面調(diào)用了 Google CDN 提供的 jQuery,以及其它 CDN 上的 Bootstrap JS、CSS 代碼,雖然它們與我的博客不同源,但我可以用它們來操作這個(gè)頁面,并應(yīng)用樣式,這是執(zhí)行的概念。
如何實(shí)現(xiàn)下列代碼:[1,2,3,4,5].duplicator(); // [1,2,3,4,5,1,2,3,4,5]將此方法添加至 Array.prototype 實(shí)現(xiàn),代碼如下:
Array.prototype.duplicator = function(){ var l = this.length,i; for(i=0;i什么是三元表達(dá)式 (Ternary expression)?“三元 (Ternary)” 表示什么意思? 一個(gè)運(yùn)算符如果有一個(gè)操作數(shù),為一元運(yùn)算符,兩個(gè)為二元,三個(gè)為三元運(yùn)算符,三元表達(dá)式則為一個(gè)三元運(yùn)算表達(dá)式!
什么是 “use strict”; ? 使用它的好處和壞處分別是什么?ECMAScript5中引入的嚴(yán)格模式,通過讓JavaScript運(yùn)行環(huán)境對(duì)一些開發(fā)過程中最常見和不易發(fā)現(xiàn)的錯(cuò)誤做出和當(dāng)前不同的處理,來讓開發(fā)者擁有一個(gè)”更好”的JavaScript語言。
好處:
消除Javascript語法的一些不合理、不嚴(yán)謹(jǐn)之處,減少一些怪異行為;
消除代碼運(yùn)行的一些不安全之處,保證代碼運(yùn)行的安全;
提高編譯器效率,增加運(yùn)行速度;
為未來新版本的Javascript做好鋪墊。
好處具體體現(xiàn):
去除WITH關(guān)鍵詞
防止意外為全局變量賦值
函數(shù)中的THIS不再默認(rèn)指向全局
防止重名
安全的 EVAL()
對(duì)只讀屬性修改時(shí)拋出異常
壞處:同樣的代碼,在“嚴(yán)格模式”中,可能會(huì)有不一樣的運(yùn)行結(jié)果;一些在“正常模式”下可以運(yùn)行的語句,在“嚴(yán)格模式”下將不能運(yùn)行
總結(jié):啟用JavaScript嚴(yán)格模式,它能幫你發(fā)現(xiàn)代碼中未曾注意到的錯(cuò)誤。不要在全局環(huán)境中啟用,但你能盡量多的使用IIFE(立即執(zhí)行函數(shù)表達(dá)式)來把嚴(yán)格模式作用到多個(gè)函數(shù)范圍內(nèi)。一開始,你會(huì)遇到之前未曾碰到過的錯(cuò)誤提示,這是正常的。當(dāng)啟用嚴(yán)格模式后,請(qǐng)確保在支持的瀏覽器中做了測(cè)試,以發(fā)現(xiàn)新的潛在問題。一定不要僅僅在代碼中添加一行”use strict”就假定余下的代碼能正常工作。
請(qǐng)實(shí)現(xiàn)一個(gè)遍歷至 100 的 for loop 循環(huán),在能被 3 整除時(shí)輸出 “fizz”,在能被 5 整除時(shí)輸出 “buzz”,在能同時(shí)被 3 和 5 整除時(shí)輸出 “fizzbuzz”。for (var i = 1; i <= 30; i++) { if (i % 3 === 0) { if (i % 5 === 0) { alert("fizzbuzz" + i); continue; } alert("fizz" + i); continue; } else if (i % 5 === 0) { if (i % 3 === 0) { alert("fizzbuzz" + i); continue; } alert("buzz" + i); continue; } }為何通常會(huì)認(rèn)為保留網(wǎng)站現(xiàn)有的全局作用域 (global scope) 不去改變它,是較好的選擇?它的意思是: 盡量少在全局作用域定義變量。
目的:減少名稱沖突 利于模塊化
為何你會(huì)使用 load 之類的事件 (event)?此事件有缺點(diǎn)嗎?你是否知道其他替代品,以及為何使用它們?要等到等頁面完全加載后(所有圖像、javascript文件、CSS等外部文件)。替代:把script標(biāo)簽放到最后面。
請(qǐng)解釋什么是單頁應(yīng)用 (single page app), 以及如何使其對(duì)搜索引擎友好 (SEO-friendly)。單頁應(yīng)用是一種特殊的web應(yīng)用,它將所有的活動(dòng)局限于一個(gè)web頁面中,僅在該Web頁面初始化時(shí)加載相應(yīng)的HTML、JavaScript 和 CSS。
優(yōu)點(diǎn):
用戶體驗(yàn):對(duì)于內(nèi)容的改動(dòng)不需要加載整個(gè)頁面
高效:服務(wù)器壓力很小,消耗更少的帶寬,能夠與面向服務(wù)的架構(gòu)更好地結(jié)合。
經(jīng)典MVC開發(fā)模式,前后端各負(fù)其責(zé)。
一套Server API,多端使用(web、移動(dòng)APP等)
重前端,業(yè)務(wù)邏輯全部在本地操作,數(shù)據(jù)都需要通過AJAX同步、提交
缺點(diǎn):
不利于SEO:解決方案也有一些:H5pushState,通過瀏覽器歷史記錄讓搜索引擎抓取;url中#!
復(fù)雜的單頁架構(gòu)頁面,對(duì)Google來說抓取比較困難,于是給開發(fā)者制定一個(gè)規(guī)范:
1)、網(wǎng)站提交sitemap給Google;
2)、Google發(fā)現(xiàn)URL里有#!符號(hào),例如example.com/#!/detail/1,于是Google開始抓取example.com/?_escaped_fragment_=/detail/1;_escaped_fragment_這個(gè)參數(shù)是Google指定的命名,如果開發(fā)者希望把網(wǎng)站內(nèi)容提交給Google,就必須通過這個(gè)參數(shù)生成靜態(tài)頁面。首屏渲染速度慢
使用 Promises 而非回調(diào) (callbacks) 優(yōu)缺點(diǎn)是什么?優(yōu)點(diǎn):易讀性改善
使用一種可以編譯成 JavaScript 的語言來寫 JavaScript 代碼有哪些優(yōu)缺點(diǎn)?以Typescript為例子:
typescript是javascript的強(qiáng)類型版本,在編譯期去掉類型和特有語法,生成純粹的javascript代碼。TypeScript 是 JavaScript 的超集,這意味著他支持所有的 JavaScript 語法。并在此之上對(duì) JavaScript 添加了一些擴(kuò)展,如 class / interface / module 等。這樣會(huì)大大提升代碼的可閱讀性。優(yōu)點(diǎn):
靜態(tài)類型檢查
IDE 智能提示 (編譯階段即可發(fā)現(xiàn)類型不匹配的錯(cuò)誤)
代碼重構(gòu)
可讀性
缺點(diǎn):
不指定類型就寫不了程序,類型只是輔助信息,并不是程序的本之后
靈活性問題
你使用哪些工具和技術(shù)來調(diào)試 JavaScript 代碼?alert
console.log
斷點(diǎn)調(diào)試(這三種調(diào)試方式都是打斷點(diǎn))
js斷點(diǎn)調(diào)試
source斷點(diǎn)調(diào)試
Debugger斷點(diǎn)(具體的說就是通過在代碼中添加”debugger;”語句,當(dāng)代碼執(zhí)行到該語句的時(shí)候就會(huì)自動(dòng)斷點(diǎn)。)
DOM斷點(diǎn)調(diào)試
當(dāng)節(jié)點(diǎn)內(nèi)部子節(jié)點(diǎn)變化時(shí)斷點(diǎn)
當(dāng)節(jié)點(diǎn)屬性發(fā)生變化時(shí)斷點(diǎn)
當(dāng)節(jié)點(diǎn)被移除時(shí)斷點(diǎn)
請(qǐng)解釋可變 (mutable) 和不變 (immutable) 對(duì)象的區(qū)別?javascript中的原始值(undefined、null、布爾值、數(shù)字和字符串)與對(duì)象(包括數(shù)組和函數(shù))有著根本區(qū)別。原始值是不可更改的:任何方法都無法更改(或“突變”)一個(gè)原始值。對(duì)數(shù)字和布爾值來說顯然如此—-改變數(shù)字的值本身就說不通,而對(duì)字符串來說就不那么明顯了,因?yàn)樽址雌饋硐裼勺址M成的數(shù)組,我們期望可以通過指定索引來假改字符串中的字符。實(shí)際上,javascript是禁止這樣做的。字符串中所有的方法看上去返回了一個(gè)修改后的字符串,實(shí)際上返回的是一個(gè)新的字符串值。
區(qū)別:
可變性:對(duì)象和原始值不同,首先,它們是可變的–它們的值是可修改的
值的比較:對(duì)象的比較并非值的比較:即使兩個(gè)對(duì)象包含同樣的屬性及相同的值,它們也是不相等的。各個(gè)索引元素相等的兩個(gè)數(shù)組也不相等。
不變性 (immutability) 有哪些優(yōu)缺點(diǎn)?優(yōu)點(diǎn):
*因?yàn)椴荒苄薷囊粋€(gè)不變對(duì)象的狀態(tài),所以可以避免由此引起的不必要的程序錯(cuò)誤;一個(gè)不變的對(duì)象要比一個(gè)可變的對(duì)象更加容易維護(hù)。因?yàn)闆]有任何一個(gè)線程能夠修改不變對(duì)象的內(nèi)部狀態(tài),一個(gè)不變對(duì)象自動(dòng)就是線程安全的,這樣可以省掉處理同步化的開銷。一個(gè)不變對(duì)象可以自由地被不同的客戶端共享。
缺點(diǎn):
一旦需要修改一個(gè)不變對(duì)象的狀態(tài),就只好創(chuàng)建一個(gè)新的同類對(duì)象。在需要頻繁修改不變對(duì)象的環(huán)境里,會(huì)有大量的不變對(duì)象作為中間結(jié)果被創(chuàng)建出來,這是一種資源上的浪費(fèi)。
如何用你自己的代碼來實(shí)現(xiàn)不變性 (immutability)?可以使用const 修飾變量不可變
你會(huì)使用怎樣的語言結(jié)構(gòu)來遍歷對(duì)象屬性 (object properties) 和數(shù)組內(nèi)容? 請(qǐng)解釋同步 (synchronous) 和異步 (asynchronous) 函數(shù)的區(qū)別。同步式:當(dāng)計(jì)算機(jī)調(diào)度線程進(jìn)行I/O操作命令后,由于文件的讀寫或者網(wǎng)絡(luò)通信需要較長(zhǎng)的操作時(shí)間,操作系統(tǒng)為了充分利用cpu,此時(shí)會(huì)暫停到當(dāng)前的I/O線程對(duì)CPU的控制(故又稱同步式為阻塞式I/O),把cup資源然給其他的線程資源,當(dāng)I/O線程完成了操作時(shí),此時(shí)操作系統(tǒng)會(huì)恢復(fù)此時(shí)的I/O線程,從而當(dāng)前I/O線程重新獲得了cup的的控制權(quán),繼續(xù)完成其他操作。
異步式:異步式IO又稱非阻塞式I/O,異步式與同步式不同的是,當(dāng)線程進(jìn)行IO操作時(shí),操作系統(tǒng)并不是暫停當(dāng)前的線程操作,而是執(zhí)行完I/O指令后,操作系統(tǒng)繼續(xù)讓當(dāng)前線程執(zhí)行下一條指令,當(dāng)I/O操作完成后,會(huì)通過事件(event)通知I/O線程,而線程在接收到通知后,會(huì)處理響應(yīng)事件。
什么是事件循環(huán) (event loop)?主線程從“任務(wù)隊(duì)列”中讀取事件,這個(gè)過程是循環(huán)不斷的,所以整個(gè)的這種運(yùn)行機(jī)制又稱為Event Loop(事件循環(huán))。
請(qǐng)問調(diào)用棧 (call stack) 和任務(wù)隊(duì)列 (task queue) 的區(qū)別是什么?所謂“回調(diào)函數(shù)”(callback),就是那些會(huì)被主線程掛起來的代碼。異步任務(wù)必須指定回調(diào)函數(shù),當(dāng)異步任務(wù)從“任務(wù)隊(duì)列”回到執(zhí)行棧,回調(diào)函數(shù)就會(huì)執(zhí)行。
“任務(wù)隊(duì)列”是一個(gè)先進(jìn)先出的數(shù)據(jù)結(jié)構(gòu),排在前面的事件,優(yōu)先返回主線程。主線程的讀取過程基本上是自動(dòng)的,只要執(zhí)行棧一清空,“任務(wù)隊(duì)列”上第一位的事件就自動(dòng)返回主線程。但是,由于存在后文提到的“定時(shí)器”功能,主線程要檢查一下執(zhí)行時(shí)間,某些事件必須要在規(guī)定的時(shí)間返回主線程。
解釋 function foo() {} 與 var foo = function() {} 用法的區(qū)別?第一個(gè)未函數(shù)聲明,第二個(gè)為函數(shù)定義表達(dá)式(函數(shù)定義表達(dá)式foo,變量聲明提前單賦值并未提前)
下面的代碼將輸出什么到控制臺(tái),為什么?(function(){ var a = b = 3; })(); console.log("a defined? " + (typeof a !== "undefined")); console.log("b defined? " + (typeof b !== "undefined"));輸出: a defined? false b defined? true
封裝JavaScript源文件的全部?jī)?nèi)容到一個(gè)函數(shù)塊有什么意義及理由?這是一個(gè)越來越普遍的做法,被許多流行的JavaScript庫(jQuery,Node.js等)采用。這種技術(shù)創(chuàng)建了一個(gè)圍繞文件全部?jī)?nèi)容的閉包,也許是最重要的是,創(chuàng)建了一個(gè)私有的命名空間,從而有助于避免不同JavaScript模塊和庫之間潛在的名稱沖突。
在JavaScript源文件的開頭包含 use strict 有什么意義和好處?
這種技術(shù)的另一個(gè)特點(diǎn)是,允許一個(gè)易于引用的(假設(shè)更短的)別名用于全局變量。這通常用于,例如,jQuery插件中。jQuery允許你使用jQuery.noConflict(),來禁用 $ 引用到j(luò)Query命名空間。在完成這項(xiàng)工作之后,你的代碼仍然可以使用$ 利用這種閉包技術(shù),如下所示:
(function($) { /* jQuery plugin code referencing $ */ } )(jQuery);use strict 是一種在JavaScript代碼運(yùn)行時(shí)自動(dòng)實(shí)行更嚴(yán)格解析和錯(cuò)誤處理的方法。那些被忽略或默默失敗了的代碼錯(cuò)誤,會(huì)產(chǎn)生錯(cuò)誤或拋出異常。通常而言,這是一個(gè)很好的做法。
嚴(yán)格模式的一些主要優(yōu)點(diǎn)包括:
使調(diào)試更加容易。那些被忽略或默默失敗了的代碼錯(cuò)誤,會(huì)產(chǎn)生錯(cuò)誤或拋出異常,因此盡早提醒你代碼中的問題,你才能更快地指引到它們的源代碼。
防止意外的全局變量。如果沒有嚴(yán)格模式,將值分配給一個(gè)未聲明的變量會(huì)自動(dòng)創(chuàng)建該名稱的全局變量。這是JavaScript中最常見的錯(cuò)誤之一。在嚴(yán)格模式下,這樣做的話會(huì)拋出錯(cuò)誤。
消除 this 強(qiáng)制。如果沒有嚴(yán)格模式,引用null或未定義的值到 this 值會(huì)自動(dòng)強(qiáng)制到全局變量。這可能會(huì)導(dǎo)致許多令人頭痛的問題和讓人恨不得拔自己頭發(fā)的bug。在嚴(yán)格模式下,引用 null或未定義的 this 值會(huì)拋出錯(cuò)誤。
不允許重復(fù)的屬性名稱或參數(shù)值。當(dāng)檢測(cè)到對(duì)象(例如,var object = {foo: "bar", foo: "baz"};)中重復(fù)命名的屬性,或檢測(cè)到函數(shù)中(例如,function foo(val1, val2, val1){})重復(fù)命名的參數(shù)時(shí),嚴(yán)格模式會(huì)拋出錯(cuò)誤,因此捕捉幾乎可以肯定是代碼中的bug可以避免浪費(fèi)大量的跟蹤時(shí)間。
使eval() 更安全。在嚴(yán)格模式和非嚴(yán)格模式下,eval() 的行為方式有所不同。最顯而易見的是,在嚴(yán)格模式下,變量和聲明在 eval() 語句內(nèi)部的函數(shù)不會(huì)在包含范圍內(nèi)創(chuàng)建(它們會(huì)在非嚴(yán)格模式下的包含范圍中被創(chuàng)建,這也是一個(gè)常見的問題源)。
在 delete使用無效時(shí)拋出錯(cuò)誤。delete操作符(用于從對(duì)象中刪除屬性)不能用在對(duì)象不可配置的屬性上。當(dāng)試圖刪除一個(gè)不可配置的屬性時(shí),非嚴(yán)格代碼將默默地失敗,而嚴(yán)格模式將在這樣的情況下拋出異常。
考慮以下兩個(gè)函數(shù)。它們會(huì)返回相同的東西嗎? 為什么相同或?yàn)槭裁床幌嗤?function foo1(){ return { bar: "hello" }; }function foo2(){ return { bar: "hello" }; }返回不相同: foo1 returns:Object {bar: "hello"}foo2 returns:undefined
分號(hào)在JavaScript中是一個(gè)可選項(xiàng)(盡管省略它們通常是非常糟糕的形式)。其結(jié)果就是,當(dāng)碰到 foo2()中包含 return語句的代碼行(代碼行上沒有其他任何代碼),分號(hào)會(huì)立即自動(dòng)插入到返回語句之后。也不會(huì)拋出錯(cuò)誤,因?yàn)榇a的其余部分是完全有效的,即使它沒有得到調(diào)用或做任何事情(相當(dāng)于它就是是一個(gè)未使用的代碼塊,定義了等同于字符串 "hello"的屬性 bar)。
這種行為也支持放置左括號(hào)于JavaScript代碼行的末尾,而不是新代碼行開頭的約定。正如這里所示,這不僅僅只是JavaScript中的一個(gè)風(fēng)格偏好。
NaN 是什么?它的類型是什么?你如何可靠地測(cè)試一個(gè)值是否等于 NaN ?NaN 屬性代表一個(gè)“不是數(shù)字”的值。這個(gè)特殊的值是因?yàn)檫\(yùn)算不能執(zhí)行而導(dǎo)致的,不能執(zhí)行的原因要么是因?yàn)槠渲械倪\(yùn)算對(duì)象之一非數(shù)字(例如, "abc" / 4),要么是因?yàn)檫\(yùn)算的結(jié)果非數(shù)字(例如,除數(shù)為零)。
NaN的特點(diǎn):
NaN的類型為Number: console.log(typeof NaN === "Number"); // logs "true"
NaN 和任何東西比較——甚至是它自己本身!——結(jié)果是false:console.log(NaN === NaN); // logs "false"
測(cè)試數(shù)字為NaN的方法:
使用內(nèi)置函數(shù)iSNaN(半可靠)
value !== value,如果值等于NaN,只會(huì)產(chǎn)生true
ES6提供: Number.isNaN() 比老的isNaN更可靠
下列代碼將輸出什么?并解釋原因console.log(0.1 + 0.2);console.log(0.1 + 0.2 == 0.3);JavaScript中的數(shù)字和浮點(diǎn)精度的處理相同,因此,可能不會(huì)總是產(chǎn)生預(yù)期的結(jié)果。
討論寫函數(shù) isInteger(x) 的可能方法,用于確定x是否是整數(shù)
以上所提供的例子就是一個(gè)演示了這個(gè)問題的典型例子。但出人意料的是,它會(huì)輸出:
0.30000000000000004falseECMAScript 6 之前沒有提供類似 Number.isInteger 的方法。在ECMAScript規(guī)格說明中,整數(shù)只概念上存在:即,數(shù)字值總是存儲(chǔ)為浮點(diǎn)值。
方法:最簡(jiǎn)單又最干凈的ECMAScript6之前的解決方法(同時(shí)也非常穩(wěn)健地返回 false ,即使一個(gè)非數(shù)字的值,如字符串或 null ,被傳遞給函數(shù))如:function isInteger(x) { return (x^0) === x; }
function isInteger(x) { return Math.round(x) === x; } (不如第一個(gè)優(yōu)雅)
function isInteger(x) { return (typeof x === "number") && (x % 1 === 0);
function isInteger(x) { return parseInt(x, 10) === x; }
雖然這個(gè)以 parseInt函數(shù)為基礎(chǔ)的方法在 x 取許多值時(shí)都能工作良好,但一旦 x 取值相當(dāng)大的時(shí)候,就會(huì)無法正常工作。問題在于 parseInt() 在解析數(shù)字之前強(qiáng)制其第一個(gè)參數(shù)到字符串。因此,一旦數(shù)目變得足夠大,它的字符串就會(huì)表達(dá)為指數(shù)形式(例如, 1e+21)。因此,parseInt() 函數(shù)就會(huì)去解析 1e+21,但當(dāng)?shù)竭_(dá) e字符串的時(shí)候,就會(huì)停止解析,因此只會(huì)返回值 1。注意:
String(1000000000000000000000)"1e+21"> parseInt(1000000000000000000000, 10)1> parseInt(1000000000000000000000, 10) === 1000000000000000000000false下列代碼行1-4如何排序,使之能夠在執(zhí)行代碼時(shí)輸出到控制臺(tái)? 為什么?(function() { console.log(1); setTimeout(function(){console.log(2)}, 1000); setTimeout(function(){console.log(3)}, 0); console.log(4); })();序號(hào)如下:
1 4 3 2比較明顯而易見的那部分:
寫一個(gè)簡(jiǎn)單的函數(shù)(少于80個(gè)字符),要求返回一個(gè)布爾值指明字符串是否為回文結(jié)構(gòu)。
1 和 4之所以放在前面,是因?yàn)樗鼈兪峭ㄟ^簡(jiǎn)單調(diào)用 console.log() 而沒有任何延遲輸出的
2 之所以放在 3的后面,是因?yàn)?2 是延遲了1000毫秒(即,1秒)之后輸出的,而 3 是延遲了0毫秒之后輸出的。
好的。但是,既然 3 是0毫秒延遲之后輸出的,那么是否意味著它是立即輸出的呢?如果是的話,那么它是不是應(yīng)該在 4 之前輸出,既然 4 是在第二行輸出的?
要回答這個(gè)問題,你需要正確理解JavaScript的事件和時(shí)間設(shè)置。
瀏覽器有一個(gè)事件循環(huán),會(huì)檢查事件隊(duì)列和處理未完成的事件。例如,如果時(shí)間發(fā)生在后臺(tái)(例如,腳本的 onload 事件)時(shí),瀏覽器正忙(例如,處理一個(gè) onclick),那么事件會(huì)添加到隊(duì)列中。當(dāng)onclick處理程序完成后,檢查隊(duì)列,然后處理該事件(例如,執(zhí)行 onload 腳本)。
同樣的, setTimeout() 也會(huì)把其引用的函數(shù)的執(zhí)行放到事件隊(duì)列中,如果瀏覽器正忙的話。
當(dāng)setTimeout()的第二個(gè)參數(shù)為0的時(shí)候,它的意思是“盡快”執(zhí)行指定的函數(shù)。具體而言,函數(shù)的執(zhí)行會(huì)放置在事件隊(duì)列的下一個(gè)計(jì)時(shí)器開始。但是請(qǐng)注意,這不是立即執(zhí)行:函數(shù)不會(huì)被執(zhí)行除非下一個(gè)計(jì)時(shí)器開始。這就是為什么在上述的例子中,調(diào)用 console.log(4) 發(fā)生在調(diào)用 console.log(3) 之前(因?yàn)檎{(diào)用 console.log(3) 是通過setTimeout被調(diào)用的,因此會(huì)稍微延遲)。下面這個(gè)函數(shù)在 str 是回文結(jié)構(gòu)的時(shí)候返回true,否則,返回false。
function isPalindrome(str) { str = str.replace(/W/g, "").toLowerCase(); return (str == str.split("").reverse().join("")); }例如:
console.log(isPalindrome("level")); // logs "true"console.log(isPalindrome("levels")); // logs "false"console.log(isPalindrome("A car, a man, a maraca")); // logs "true"寫一個(gè) sum方法,在使用下面任一語法調(diào)用時(shí),都可以正常工作console.log(sum(2,3)); // Outputs 5 console.log(sum(2)(3)); // Outputs 5(至少)有兩種方法可以做到:
方法1:
function sum(x) { if (arguments.length == 2) { return arguments[0] + arguments[1]; }else { return function(y) { return x + y; }; } }方法2:
function sum(x, y) { if (y !== undefined) { return x + y; } else { return function(y) { return x + y; }; } }請(qǐng)看下面的代碼片段for (var i = 0; i < 5; i++) { var btn = document.createElement("button"); btn.appendChild(document.createTextNode("Button " + i)); btn.addEventListener("click", function(){ console.log(i); }); document.body.appendChild(btn); }(a)當(dāng)用戶點(diǎn)擊“Button 4”的時(shí)候會(huì)輸出什么到控制臺(tái),為什么?
無論用戶點(diǎn)擊什么按鈕,數(shù)字5將總會(huì)輸出到控制臺(tái)。這是因?yàn)椋?dāng) onclick 方法被調(diào)用(對(duì)于任何按鈕)的時(shí)候, for 循環(huán)已經(jīng)結(jié)束,變量 i 已經(jīng)獲得了5的值。(b)提供一個(gè)或多個(gè)備用的可按預(yù)期工作的實(shí)現(xiàn)方案?
要讓代碼工作的關(guān)鍵是,通過傳遞到一個(gè)新創(chuàng)建的函數(shù)對(duì)象,在每次傳遞通過 for 循環(huán)時(shí),捕捉到 i 值。下面是三種可能實(shí)現(xiàn)的方法:
方法一
for (var i = 0; i < 5; i++) {
var btn = document.createElement("button"); btn.appendChild(document.createTextNode("Button " + i)); btn.addEventListener("click", (function(i) { return function() { console.log(i); }; })(i)); document.body.appendChild(btn);}
方法二: 封裝全部調(diào)用到在新匿名函數(shù)中的 btn.addEventListener
for (var i = 0; i < 5; i++) {
var btn = document.createElement("button"); btn.appendChild(document.createTextNode("Button " + i)); (function (i) { btn.addEventListener("click", function() { console.log(i); }); })(i); document.body.appendChild(btn);}
方法三: 調(diào)用數(shù)組對(duì)象的本地 forEach 方法來替代 for 循環(huán)
["a", "b", "c", "d", "e"].forEach(function (value, i) {
var btn = document.createElement("button"); btn.appendChild(document.createTextNode("Button " + i)); btn.addEventListener("click", function() { console.log(i); }); document.body.appendChild(btn);});
下面的代碼將輸出什么到控制臺(tái),為什么?var arr1 = "john".split(""); var arr2 = arr1.reverse(); var arr3 = "jones".split(""); arr2.push(arr3); console.log("array 1: length=" + arr1.length + " last=" + arr1.slice(-1)); console.log("array 2: length=" + arr2.length + " last=" + arr2.slice(-1));輸出結(jié)果是:
"array 1: length=5 last=j,o,n,e,s""array 2: length=5 last=j,o,n,e,s"調(diào)用數(shù)組對(duì)象的 reverse() 方法并不只返回反順序的陣列,它也反轉(zhuǎn)了數(shù)組本身的順序(即,在這種情況下,指的是 arr1)。
reverse() 方法返回一個(gè)到數(shù)組本身的引用(在這種情況下即,arr1)。其結(jié)果為,arr2 僅僅是一個(gè)到 arr1的引用(而不是副本)。因此,當(dāng)對(duì) arr2做了任何事情(即當(dāng)我們調(diào)用 arr2.push(arr3);)時(shí),arr1 也會(huì)受到影響,因?yàn)?arr1 和 arr2 引用的是同一個(gè)對(duì)象。注意:
下面的代碼將輸出什么到控制臺(tái),為什么?
傳遞數(shù)組到另一個(gè)數(shù)組的 push() 方法會(huì)讓整個(gè)數(shù)組作為單個(gè)元素映射到數(shù)組的末端。其結(jié)果是,語句 arr2.push(arr3); 在其整體中添加 arr3 作為一個(gè)單一的元素到 arr2 的末端(也就是說,它并沒有連接兩個(gè)數(shù)組,連接數(shù)組是 concat() 方法的目的)。
和Python一樣,JavaScript標(biāo)榜數(shù)組方法調(diào)用中的負(fù)數(shù)下標(biāo),例如 slice() 可作為引用數(shù)組末尾元素的方法:例如,-1下標(biāo)表示數(shù)組中的最后一個(gè)元素,等等。console.log(1 + "2" + "2"); console.log(1 + +"2" + "2"); console.log(1 + -"1" + "2"); console.log(+"1" + "1" + "2"); console.log( "A" - "B" + "2"); console.log( "A" - "B" + 2);上面的代碼將輸出以下內(nèi)容到控制臺(tái):
"122""32""02""112""NaN2"NaN根本原因是,JavaScript(ECMAScript)是一種弱類型語言,它可對(duì)值進(jìn)行自動(dòng)類型轉(zhuǎn)換,以適應(yīng)正在執(zhí)行的操作。讓我們通過上面的例子來說明這是如何做到的。
下面的遞歸代碼在數(shù)組列表偏大的情況下會(huì)導(dǎo)致堆棧溢出。在保留遞歸模式的基礎(chǔ)上,你怎么解決這個(gè)問題?
例1:1 + "2" + "2" 輸出:"122" 說明: 1 + "2" 是執(zhí)行的第一個(gè)操作。由于其中一個(gè)運(yùn)算對(duì)象("2")是字符串,JavaScript會(huì)假設(shè)它需要執(zhí)行字符串連接,因此,會(huì)將 1 的類型轉(zhuǎn)換為 "1", 1 + "2"結(jié)果就是 "12"。然后, "12" + "2" 就是 "122"。
例2: 1 + +"2" + "2" 輸出: "32" 說明:根據(jù)運(yùn)算的順序,要執(zhí)行的第一個(gè)運(yùn)算是 +"2"(第一個(gè) "2" 前面的額外 + 被視為一元運(yùn)算符)。因此,JavaScript將 "2" 的類型轉(zhuǎn)換為數(shù)字,然后應(yīng)用一元 + 號(hào)(即,將其視為一個(gè)正數(shù))。其結(jié)果是,接下來的運(yùn)算就是 1 + 2 ,這當(dāng)然是 3。然后我們需要在一個(gè)數(shù)字和一個(gè)字符串之間進(jìn)行運(yùn)算(即, 3 和 "2"),同樣的,JavaScript會(huì)將數(shù)值類型轉(zhuǎn)換為字符串,并執(zhí)行字符串的連接,產(chǎn)生 "32"。
例3: 1 + -"1" + "2" 輸出: "02" 說明:這里的解釋和前一個(gè)例子相同,除了此處的一元運(yùn)算符是 - 而不是 +。先是 "1" 變?yōu)?1,然后當(dāng)應(yīng)用 - 時(shí)又變?yōu)榱?-1 ,然后將其與 1相加,結(jié)果為 0,再將其轉(zhuǎn)換為字符串,連接最后的 "2" 運(yùn)算對(duì)象,得到 "02"。
例4: +"1" + "1" + "2" 輸出: "112" 說明:雖然第一個(gè)運(yùn)算對(duì)象 "1"因?yàn)榍熬Y的一元 + 運(yùn)算符類型轉(zhuǎn)換為數(shù)值,但又立即轉(zhuǎn)換回字符串,當(dāng)連接到第二個(gè)運(yùn)算對(duì)象 "1" 的時(shí)候,然后又和最后的運(yùn)算對(duì)象"2" 連接,產(chǎn)生了字符串 "112"。
例5: "A" - "B" + "2" 輸出: "NaN2" 說明:由于運(yùn)算符 - 不能被應(yīng)用于字符串,并且 "A" 和 "B" 都不能轉(zhuǎn)換成數(shù)值,因此,"A" - "B"的結(jié)果是 NaN,然后再和字符串 "2" 連接,得到 "NaN2" 。
例6: "A" - "B" + 2 輸出: NaN 說明:參見前一個(gè)例子, "A" - "B" 結(jié)果為 NaN。但是,應(yīng)用任何運(yùn)算符到NaN與其他任何的數(shù)字運(yùn)算對(duì)象,結(jié)果仍然是 NaN。var list = readHugeList(); var nextListItem = function() { var item = list.pop(); if (item) { // process the list item... nextListItem(); } };潛在的堆棧溢出可以通過修改nextListItem 函數(shù)避免:
var list = readHugeList(); var nextListItem = function() { var item = list.pop(); if (item) { // process the list item... setTimeout( nextListItem, 0); } };堆棧溢出之所以會(huì)被消除,是因?yàn)槭录h(huán)操縱了遞歸,而不是調(diào)用堆棧。當(dāng) nextListItem 運(yùn)行時(shí),如果 item不為空,timeout函數(shù)(nextListItem)就會(huì)被推到事件隊(duì)列,該函數(shù)退出,因此就清空調(diào)用堆棧。當(dāng)事件隊(duì)列運(yùn)行其timeout事件,且進(jìn)行到下一個(gè) item 時(shí),定時(shí)器被設(shè)置為再次調(diào)用 extListItem。因此,該方法從頭到尾都沒有直接的遞歸調(diào)用,所以無論迭代次數(shù)的多少,調(diào)用堆棧保持清空的狀態(tài)。
JavaScript中的“閉包”是什么?請(qǐng)舉一個(gè)例子閉包是一個(gè)可以訪問外部(封閉)函數(shù)作用域鏈中的變量的內(nèi)部函數(shù)。
閉包可以訪問三種范圍中的變量:這三個(gè)范圍具體為:自己范圍內(nèi)的變量
封閉函數(shù)范圍內(nèi)的變量
全局變量。
下面是一個(gè)簡(jiǎn)單的例子:
var globalVar = "xyz"; (function outerFunc(outerArg) { var outerVar = "a"; (function innerFunc(innerArg) { var innerVar = "b"; console.log( "outerArg = " + outerArg + " " + "innerArg = " + innerArg + " " + "outerVar = " + outerVar + " " + "innerVar = " + innerVar + " " + "globalVar = " + globalVar); })(456); })(123);在上面的例子中,來自于 innerFunc, outerFunc和全局命名空間的變量都在 innerFunc的范圍內(nèi)。因此,上面的代碼將輸出如下:
outerArg = 123innerArg = 456outerVar = ainnerVar = bglobalVar = xyz下面的代碼將輸出什么for (var i = 0; i < 5; i++) { setTimeout(function() { console.log(i); }, i * 1000 ); }解釋你的答案。閉包在這里能起什么作用?
上面的代碼不會(huì)按預(yù)期顯示值0,1,2,3,和4,而是會(huì)顯示5,5,5,5,和5。
原因是在循環(huán)中執(zhí)行的每個(gè)函數(shù)將整個(gè)循環(huán)完成之后被執(zhí)行,因此,將會(huì)引用存儲(chǔ)在 i中的最后一個(gè)值,那就是5。
閉包可以通過為每次迭代創(chuàng)建一個(gè)唯一的范圍,存儲(chǔ)范圍內(nèi)變量的每個(gè)唯一的值,來防止這個(gè)問題,如下:for (var i = 0; i < 5; i++) { (function(x) { setTimeout(function() { console.log(x); }, x * 1000 ); })(i); }這就會(huì)按預(yù)期輸出0,1,2,3,和4到控制臺(tái)。
以下代碼行將輸出什么到控制臺(tái)?console.log("0 || 1 = "+(0 || 1)); console.log("1 || 2 = "+(1 || 2)); console.log("0 && 1 = "+(0 && 1)); console.log("1 && 2 = "+(1 && 2));該代碼將輸出:
0 || 1 = 11 || 2 = 10 && 1 = 01 && 2 = 2在JavaScript中, || 和 &&都是邏輯運(yùn)算符,用于在從左至右計(jì)算時(shí),返回第一個(gè)可完全確定的“邏輯值”。
或( || )運(yùn)算符。在形如 X||Y的表達(dá)式中,首先計(jì)算X 并將其解釋執(zhí)行為一個(gè)布爾值。如果這個(gè)布爾值true,那么返回true(1),不再計(jì)算 Y,因?yàn)椤盎颉钡臈l件已經(jīng)滿足。如果這個(gè)布爾值為false,那么我們?nèi)匀徊荒苤?X||Y是真是假,直到我們計(jì)算 Y,并且也把它解釋執(zhí)行為一個(gè)布爾值。因此, 0 || 1 的計(jì)算結(jié)果為true(1),同理計(jì)算1 || 2。
與( &&)運(yùn)算符。在形如 X&&Y的表達(dá)式中,首先計(jì)算 X并將其解釋執(zhí)行為一個(gè)布爾值。如果這個(gè)布爾值為 false,那么返回 false(0),不再計(jì)算 Y,因?yàn)椤芭c”的條件已經(jīng)失敗。如果這個(gè)布爾值為true,但是,我們?nèi)匀徊恢?X&&Y 是真是假,直到我們?nèi)ビ?jì)算 Y,并且也把它解釋執(zhí)行為一個(gè)布爾值。不過,關(guān)于 &&運(yùn)算符有趣的地方在于,當(dāng)一個(gè)表達(dá)式計(jì)算為“true”的時(shí)候,那么就返回表達(dá)式本身。這很好,雖然它在邏輯表達(dá)式方面計(jì)算為“真”,但如果你希望的話也可用于返回該值。這就解釋了為什么,有些令人奇怪的是, 1 && 2返回 2(而不是你以為的可能返回 true 或 1)。
執(zhí)行下面的代碼時(shí)將輸出什么?請(qǐng)解釋。console.log(false == "0") console.log(false === "0")代碼將輸出:
true false在JavaScript中,有兩種等式運(yùn)算符。三個(gè)等于運(yùn)算符 === 的作用類似傳統(tǒng)的等于運(yùn)算符:如果兩側(cè)的表達(dá)式有著相同的類型和相同的值,那么計(jì)算結(jié)果為true。而雙等于運(yùn)算符,會(huì)只強(qiáng)制比較它們的值。因此,總體上而言,使用 ===而不是 ==的做法更好。 !==vs !=亦是同理。
以下代碼將輸出什么?并解釋你的答案。var a={}, b={key:"b"}, c={key:"c"}; a[b]=123; a[c]=456; console.log(a[b]);這段代碼將輸出 456(而不是 123)
原因?yàn)椋寒?dāng)設(shè)置對(duì)象屬性時(shí),JavaScript會(huì)暗中字符串化參數(shù)值。在這種情況下,由于 b 和 c都是對(duì)象,因此它們都將被轉(zhuǎn)換為"[object Object]"。結(jié)果就是, a[b]和a[c]均相當(dāng)于a["[object Object]"] ,并可以互換使用。因此,設(shè)置或引用 a[c]和設(shè)置或引用 a[b]完全相同。
以下代碼行將輸出什么到控制臺(tái)?console.log((function f(n){return ((n > 1) ? n * f(n-1) : n)})(10));代碼將輸出10!的值(即10!或3628800)。
請(qǐng)看下面的代碼段。控制臺(tái)將輸出什么,為什么?
原因是:
命名函數(shù) f()遞歸地調(diào)用本身,當(dāng)調(diào)用 f(1)的時(shí)候,只簡(jiǎn)單地返回1。下面就是它的調(diào)用過程:
f(1): returns n, which is 1f(2): returns 2 f(1), which is 2f(3): returns 3 f(2), which is 6f(4): returns 4 f(3), which is 24f(5): returns 5 f(4), which is 120f(6): returns 6 f(5), which is 720f(7): returns 7 f(6), which is 5040f(8): returns 8 f(7), which is 40320f(9): returns 9 f(8), which is 362880f(10): returns 10 * f(9), which is 3628800(function(x) { return (function(y) { console.log(x); })(2) })(1);控制臺(tái)將輸出 1,即使從來沒有在函數(shù)內(nèi)部設(shè)置過x的值。原因是:
下面的代碼將輸出什么到控制臺(tái),為什么
閉包是一個(gè)函數(shù),連同在閉包創(chuàng)建的時(shí)候,其范圍內(nèi)的所有變量或函數(shù)一起。在JavaScript中,閉包是作為一個(gè)“內(nèi)部函數(shù)”實(shí)施的:即,另一個(gè)函數(shù)主體內(nèi)定義的函數(shù)。閉包的一個(gè)重要特征是,內(nèi)部函數(shù)仍然有權(quán)訪問外部函數(shù)的變量。
因此,在本例中,由于 x未在函數(shù)內(nèi)部中定義,因此在外部函數(shù)范圍中搜索定義的變量 x,且被發(fā)現(xiàn)具有1的值。var hero = { _name: "John Doe", getSecretIdentity: function (){ return this._name; } }; var stoleSecretIdentity = hero.getSecretIdentity; console.log(stoleSecretIdentity()); console.log(hero.getSecretIdentity());代碼將輸出:
undefinedJohn Doe第一個(gè) console.log之所以輸出 undefined,是因?yàn)槲覀冋趶?hero對(duì)象提取方法,所以調(diào)用了全局上下文中(即窗口對(duì)象)的 stoleSecretIdentity(),而在此全局上下文中, _name屬性不存在。
創(chuàng)建一個(gè)給定頁面上的一個(gè)DOM元素,就會(huì)去訪問元素本身及其所有子元素(不只是它的直接子元素)的函數(shù)。對(duì)于每個(gè)被訪問的元素,函數(shù)應(yīng)該傳遞元素到提供的回調(diào)函數(shù)
其中一種修復(fù)stoleSecretIdentity() 函數(shù)的方法如下:
var stoleSecretIdentity = hero.getSecretIdentity.bind(hero);此函數(shù)的參數(shù)為:
DOM元素
回調(diào)函數(shù)(將DOM元素作為其參數(shù))
訪問樹(DOM)的所有元素是經(jīng)典的深度優(yōu)先搜索算法應(yīng)用。下面是一個(gè)示范的解決方案:function Traverse(p_element,p_callback) { p_callback(p_element); var list = p_element.children; for (var i = 0; i < list.length; i++) { Traverse(list[i],p_callback); // recursive call } }面試題收集面試題一
面試題二
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/94399.html
摘要:獲取的對(duì)象范圍方法獲取的是最終應(yīng)用在元素上的所有屬性對(duì)象即使沒有代碼,也會(huì)把默認(rèn)的祖宗八代都顯示出來而只能獲取元素屬性中的樣式。因此對(duì)于一個(gè)光禿禿的元素,方法返回對(duì)象中屬性值如果有就是據(jù)我測(cè)試不同環(huán)境結(jié)果可能有差異而就是。 花了很長(zhǎng)時(shí)間整理的前端面試資源,喜歡請(qǐng)大家不要吝嗇star~ 別只收藏,點(diǎn)個(gè)贊,點(diǎn)個(gè)star再走哈~ 持續(xù)更新中……,可以關(guān)注下github 項(xiàng)目地址 https:...
摘要:手冊(cè)網(wǎng)超級(jí)有用的前端基礎(chǔ)技術(shù)面試問題收集前端面試題目及答案匯總史上最全前端面試題含答案常見前端面試題及答案經(jīng)典面試題及答案精選總結(jié)前端面試過程中最容易出現(xiàn)的問題前端面試題整理騰訊前端面試經(jīng)驗(yàn)前端基礎(chǔ)面試題部分最新前端面試題攻略前端面試前端入 手冊(cè)網(wǎng):http://www.shouce.ren/post/index 超級(jí)有用的前端基礎(chǔ)技術(shù)面試問題收集:http://www.codec...
摘要:手冊(cè)網(wǎng)超級(jí)有用的前端基礎(chǔ)技術(shù)面試問題收集前端面試題目及答案匯總史上最全前端面試題含答案常見前端面試題及答案經(jīng)典面試題及答案精選總結(jié)前端面試過程中最容易出現(xiàn)的問題前端面試題整理騰訊前端面試經(jīng)驗(yàn)前端基礎(chǔ)面試題部分最新前端面試題攻略前端面試前端入 手冊(cè)網(wǎng):http://www.shouce.ren/post/index 超級(jí)有用的前端基礎(chǔ)技術(shù)面試問題收集:http://www.codec...
摘要:詳解十大常用設(shè)計(jì)模式力薦深度好文深入理解大設(shè)計(jì)模式收集各種疑難雜癥的問題集錦關(guān)于,工作和學(xué)習(xí)過程中遇到過許多問題,也解答過許多別人的問題。介紹了的內(nèi)存管理。 延遲加載 (Lazyload) 三種實(shí)現(xiàn)方式 延遲加載也稱為惰性加載,即在長(zhǎng)網(wǎng)頁中延遲加載圖像。用戶滾動(dòng)到它們之前,視口外的圖像不會(huì)加載。本文詳細(xì)介紹了三種延遲加載的實(shí)現(xiàn)方式。 詳解 Javascript十大常用設(shè)計(jì)模式 力薦~ ...
摘要:一基礎(chǔ)接口的意義百度規(guī)范擴(kuò)展回調(diào)抽象類的意義我的前端面試經(jīng)歷百度前端掘金博主就讀于電子科技大學(xué),大三狗一枚面試是個(gè)漫長(zhǎng)的過程,從海投到收獲電話面試,一面二面三面,一個(gè)步驟出錯(cuò)那么后面就宣告終結(jié)。 一道常被人輕視的前端 JS 面試題 - 前端 - 掘金 目錄前言第一問第二問變量聲明提升函數(shù)表達(dá)式第三問第四問第五問第六問構(gòu)造函數(shù)的返回值第七問最后前言 年前剛剛離職了,分享下我曾經(jīng)出過的一道...
摘要:并總結(jié)經(jīng)典面試題集各種算法和插件前端視頻源碼資源于一身的文檔,優(yōu)化項(xiàng)目,在瀏覽器端的層面上提升速度,幫助初中級(jí)前端工程師快速搭建項(xiàng)目。 本文是關(guān)注微信小程序的開發(fā)和面試問題,由基礎(chǔ)到困難循序漸進(jìn),適合面試和開發(fā)小程序。并總結(jié)vue React html css js 經(jīng)典面試題 集各種算法和插件、前端視頻源碼資源于一身的文檔,優(yōu)化項(xiàng)目,在瀏覽器端的層面上提升速度,幫助初中級(jí)前端工程師快...
閱讀 2020·2023-04-25 22:50
閱讀 2834·2021-09-29 09:35
閱讀 3390·2021-07-29 10:20
閱讀 3153·2019-08-29 13:57
閱讀 3356·2019-08-29 13:50
閱讀 3032·2019-08-26 12:10
閱讀 3530·2019-08-23 18:41
閱讀 2634·2019-08-23 18:01