1圈膏、使用typeof bar ===“object”來確定bar是否是一個(gè)對(duì)象時(shí)有什么潛在的缺陷梧宫?這個(gè)陷阱如何避免?
盡管typeof bar ===“object”是檢查bar是否是對(duì)象的可靠方法澄成,但JavaScript中令人驚訝的問題null也被認(rèn)為是一個(gè)對(duì)象鲜棠!
因此,對(duì)于大多數(shù)開發(fā)人員來說赌朋,下面的代碼會(huì)將true(而不是false)打印到控制臺(tái):
varbar=null;
console.log(typeofbar==="object");// logs true!
只要知道這一點(diǎn)凰狞,就可以通過檢查bar是否為空來輕松避免該問題:
console.log((bar!==null)&&(typeofbar==="object"));// logs false
為了讓我們的答案更加的完整,還有兩件事值得注意: 首先沛慢,如果bar是一個(gè)函數(shù)赡若,上面的解決方案將返回false。在大多數(shù)情況下团甲,這是所期望的行為逾冬,但是在您希望函數(shù)返回true的情況下,您可以將上述解決方案修改為:
console.log((bar!==null)&&((typeofbar==="object")||(typeofbar==="function")));
其次,如果bar是數(shù)組身腻,則上述解決方案將返回true(例如产还,如果var bar = [];)。在大多數(shù)情況下嘀趟,這是所希望的行為脐区,因?yàn)閿?shù)組確實(shí)是對(duì)象,但是在您想要對(duì)數(shù)組也是false的情況下她按,可以將上述解決方案修改為:
console.log((bar!==null)&&(typeofbar==="object")&&(toString.call(bar)!=="[object Array]"));
但是牛隅,還有一個(gè)替代方法對(duì)空值,數(shù)組和函數(shù)返回false酌泰,但對(duì)于對(duì)象則為true:
console.log((bar!==null)&&(bar.constructor===Object));
或者媒佣,如果您使用jQuery:
console.log((bar!==null)&&(typeofbar==="object")&&(!$.isArray(bar)));
ES5使得數(shù)組的情況非常簡(jiǎn)單,包括它自己的空檢查:
console.log(Array.isArray(bar));
2陵刹、下面的代碼將輸出到控制臺(tái)的是什么默伍,為什么?
(function(){
vara=b=3;
})();
console.log("a defined? "+(typeofa!=='undefined'));
console.log("b defined? "+(typeofb!=='undefined'));
由于a和b都在函數(shù)的封閉范圍內(nèi)定義授霸,并且由于它們所在的行以var關(guān)鍵字開頭巡验,因此大多數(shù)JavaScript開發(fā)人員會(huì)希望typeof a和typeof b在上面的示例中都未定義。
但是碘耳,情況并非如此显设。這里的問題是大多數(shù)開發(fā)人員錯(cuò)誤地理解語(yǔ)句var a = b = 3;以下簡(jiǎn)寫為:
varb=3;
vara=b;
但實(shí)際上,var a = b = 3;其實(shí)是速記:
b=3;
vara=b;
因此(如果您不使用嚴(yán)格模式)辛辨,代碼片段的輸出將為:
a defined?false
b defined?true
但是如何在封閉函數(shù)的范圍之外定義b捕捂?那么,因?yàn)槁暶鱲ar a = b = 3;是語(yǔ)句b = 3的簡(jiǎn)寫;并且var a = b; b最終成為一個(gè)全局變量(因?yàn)樗辉趘ar關(guān)鍵字后面)斗搞,因此它仍然在作用域內(nèi)指攒,即使在封閉函數(shù)之外。
注意僻焚,在嚴(yán)格模式下(即允悦,使用strict),語(yǔ)句var a = b = 3;會(huì)產(chǎn)生一個(gè)ReferenceError的運(yùn)行時(shí)錯(cuò)誤:b沒有定義虑啤,從而避免了可能導(dǎo)致的任何頭headfakes/bugs隙弛。 (這就是為什么你應(yīng)該在你的代碼中使用strict,一個(gè)重要的例子D健)
3全闷、下面的代碼將輸出到控制臺(tái)的是什么?萍启,為什么总珠?
varmyObject={
foo:"bar",
func:function(){
varself=this;
console.log("outer func: this.foo = "+this.foo);
console.log("outer func: self.foo = "+self.foo);
(function(){
console.log("inner func: this.foo = "+this.foo);
console.log("inner func: self.foo = "+self.foo);
}());
}
};
myObject.func();
以上代碼將輸出到控制臺(tái):
outer func:this.foo=bar
outer func:self.foo=bar
inner func:this.foo=undefined
inner func:self.foo=bar
在外部函數(shù)中屏鳍,this和self都引用myObject,因此都可以正確地引用和訪問foo局服。
但在內(nèi)部函數(shù)中钓瞭,這不再指向myObject。因此腌逢,this.foo在內(nèi)部函數(shù)中是未定義的降淮,而對(duì)局部變量self的引用仍然在范圍內(nèi)并且可以在那里訪問超埋。
4搏讶、在功能塊中封裝JavaScript源文件的全部?jī)?nèi)容的重要性和原因是什么?
這是一種日益普遍的做法霍殴,被許多流行的JavaScript庫(kù)(jQuery媒惕,Node.js等)所采用。這種技術(shù)在文件的全部?jī)?nèi)容周圍創(chuàng)建一個(gè)閉包来庭,這可能最重要的是創(chuàng)建一個(gè)私有名稱空間妒蔚,從而有助于避免不同JavaScript模塊和庫(kù)之間的潛在名稱沖突。
這種技術(shù)的另一個(gè)特點(diǎn)是為全局變量提供一個(gè)容易引用(可能更短)的別名月弛。例如肴盏,這通常用于jQuery插件。 jQuery允許您使用jQuery.noConflict()來禁用對(duì)jQuery名稱空間的$引用帽衙。如果這樣做了菜皂,你的代碼仍然可以使用$使用閉包技術(shù),如下所示:
(function($){/* jQuery plugin code referencing $ */})(jQuery);
5厉萝、在JavaScript源文件的開頭包含'use strict'的意義和有什么好處恍飘?
這里最簡(jiǎn)單也是最重要的答案是use strict是一種在運(yùn)行時(shí)自動(dòng)執(zhí)行更嚴(yán)格的JavaScript代碼解析和錯(cuò)誤處理的方法。如果代碼錯(cuò)誤被忽略或失敗谴垫,將會(huì)產(chǎn)生錯(cuò)誤或拋出異常章母。總的來說翩剪,這是一個(gè)很好的做法乳怎。
嚴(yán)格模式的一些主要優(yōu)點(diǎn)包括:
使調(diào)試更容易。如果代碼錯(cuò)誤本來會(huì)被忽略或失敗前弯,那么現(xiàn)在將會(huì)產(chǎn)生錯(cuò)誤或拋出異常蚪缀,從而更快地發(fā)現(xiàn)代碼中的問題,并更快地指引它們的源代碼博杖。
防止意外全局椿胯。如果沒有嚴(yán)格模式,將值賦給未聲明的變量會(huì)自動(dòng)創(chuàng)建一個(gè)具有該名稱的全局變量剃根。這是JavaScript中最常見的錯(cuò)誤之一哩盲。在嚴(yán)格模式下,嘗試這樣做會(huì)引發(fā)錯(cuò)誤。
消除隱藏威脅廉油。在沒有嚴(yán)格模式的情況下惠险,對(duì)null或undefined的這個(gè)值的引用會(huì)自動(dòng)強(qiáng)制到全局。這可能會(huì)導(dǎo)致許多headfakes和pull-out-your-hair類型的錯(cuò)誤抒线。在嚴(yán)格模式下班巩,引用null或undefined的這個(gè)值會(huì)引發(fā)錯(cuò)誤。
不允許重復(fù)的參數(shù)值嘶炭。嚴(yán)格模式在檢測(cè)到函數(shù)的重復(fù)命名參數(shù)(例如抱慌,函數(shù)foo(val1,val2眨猎,val1){})時(shí)會(huì)引發(fā)錯(cuò)誤抑进,從而捕獲代碼中幾乎可以肯定存在的錯(cuò)誤,否則您可能會(huì)浪費(fèi)大量的時(shí)間追蹤睡陪。
注意:它曾經(jīng)是(在ECMAScript 5中)strict模式將禁止重復(fù)的屬性名稱(例如var object = {foo:“bar”寺渗,foo:“baz”};)但是從ECMAScript 2015 開始,就不再有這種情況了兰迫。
使eval()更安全信殊。eval()在嚴(yán)格模式和非嚴(yán)格模式下的行為方式有些不同。最重要的是汁果,在嚴(yán)格模式下涡拘,在eval()語(yǔ)句內(nèi)部聲明的變量和函數(shù)不會(huì)在包含范圍中創(chuàng)建(它們是以非嚴(yán)格模式在包含范圍中創(chuàng)建的,這也可能是問題的常見來源)须鼎。
拋出無效的使用錯(cuò)誤的刪除符鲸伴。刪除操作符(用于從對(duì)象中刪除屬性)不能用于對(duì)象的不可配置屬性。當(dāng)試圖刪除一個(gè)不可配置的屬性時(shí)晋控,非嚴(yán)格代碼將自動(dòng)失敗汞窗,而在這種情況下,嚴(yán)格模式會(huì)引發(fā)錯(cuò)誤赡译。
6仲吏、考慮下面的兩個(gè)函數(shù)。他們都會(huì)返回同樣的值嗎蝌焚?為什么或者為什么不裹唆?
functionfoo1(){
return{
bar:"hello"
};
}
functionfoo2(){
return
{
bar:"hello"
};
}
令人驚訝的是,這兩個(gè)函數(shù)不會(huì)返回相同的結(jié)果只洒。而是:
console.log("foo1 returns:");
console.log(foo1());
console.log("foo2 returns:");
console.log(foo2());
會(huì)產(chǎn)生:
foo1 returns:
Object{bar:"hello"}
foo2 returns:
undefined
這不僅令人驚訝许帐,而且特別令人煩惱的是,foo2()返回未定義而沒有引發(fā)任何錯(cuò)誤毕谴。
原因與JavaScript中分號(hào)在技術(shù)上是可選的事實(shí)有關(guān)(盡管忽略它們通常是非常糟糕的形式)成畦。因此距芬,在foo2()中遇到包含return語(yǔ)句的行(沒有其他內(nèi)容)時(shí),會(huì)在return語(yǔ)句之后立即自動(dòng)插入分號(hào)循帐。
由于代碼的其余部分是完全有效的框仔,即使它沒有被調(diào)用或做任何事情(它只是一個(gè)未使用的代碼塊,它定義了一個(gè)屬性欄拄养,它等于字符串“hello”)离斩,所以不會(huì)拋出任何錯(cuò)誤。
這種行為也被認(rèn)為是遵循了在JavaScript中將一行開頭大括號(hào)放在行尾的約定瘪匿,而不是在新行的開頭跛梗。如此處所示,這不僅僅是JavaScript中的一種風(fēng)格偏好柿顶。
7茄袖、什么是NaN?它的類型是什么嘁锯?如何可靠地測(cè)試一個(gè)值是否等于NaN?
NaN屬性表示“不是數(shù)字”的值聂薪。這個(gè)特殊值是由于一個(gè)操作數(shù)是非數(shù)字的(例如“abc”/ 4)或者因?yàn)椴僮鞯慕Y(jié)果是非數(shù)字而無法執(zhí)行的家乘。
雖然這看起來很簡(jiǎn)單,但NaN有一些令人驚訝的特征藏澳,如果人們沒有意識(shí)到這些特征仁锯,就會(huì)導(dǎo)致bug。
一方面翔悠,雖然NaN的意思是“不是數(shù)字”业崖,但它的類型是,數(shù)字:
console.log(typeofNaN==="number");// logs "true"
此外蓄愁,NaN相比任何事情 - 甚至本身双炕! - 是false:
console.log(NaN===NaN);// logs "false"
測(cè)試數(shù)字是否等于NaN的半可靠方法是使用內(nèi)置函數(shù)isNaN(),但即使使用 isNaN()也不是一個(gè)好的解決方案撮抓。.
一個(gè)更好的解決方案要么是使用value!==值妇斤,如果該值等于NaN,那么只會(huì)生成true丹拯。另外站超,ES6提供了一個(gè)新的Number.isNaN()函數(shù) ,它與舊的全局isNaN()函數(shù)不同乖酬,也更加可靠死相。
8、下面的代碼輸出什么咬像?解釋你的答案算撮。
console.log(0.1+0.2);
console.log(0.1+0.2==0.3);
對(duì)這個(gè)問題的一個(gè)有教養(yǎng)的回答是:“你不能確定双肤。它可能打印出0.3和true,或者可能不打印钮惠。 JavaScript中的數(shù)字全部用浮點(diǎn)精度處理茅糜,因此可能不會(huì)總是產(chǎn)生預(yù)期的結(jié)果∷赝欤“
上面提供的示例是演示此問題的經(jīng)典案例蔑赘。令人驚訝的是,它會(huì)打印出來:
0.30000000000000004
false
一個(gè)典型的解決方案是比較兩個(gè)數(shù)字與特殊常數(shù)Number.EPSILON之間的絕對(duì)差值:
functionareTheNumbersAlmostEqual(num1,num2){
returnMath.abs(num1-num2)
}
console.log(areTheNumbersAlmostEqual(0.1+0.2,0.3));
討論寫函數(shù)的可能方法isInteger(x)预明,它確定x是否是一個(gè)整數(shù)缩赛。
這聽起來很平凡,事實(shí)上撰糠,ECMAscript 6為此正好引入了一個(gè)新的Number.isInteger()函數(shù)酥馍,這是微不足道的。但是阅酪,在ECMAScript 6之前旨袒,這有點(diǎn)復(fù)雜,因?yàn)闆]有提供與Number.isInteger()方法等價(jià)的方法术辐。
問題在于砚尽,在ECMAScript規(guī)范中,整數(shù)只在概念上存在;即數(shù)值始終作為浮點(diǎn)值存儲(chǔ)辉词。
考慮到這一點(diǎn)必孤,最簡(jiǎn)單,最清潔的ECMAScript-6之前的解決方案(即使將非數(shù)字值(例如字符串或空值)傳遞給該函數(shù)瑞躺,該解決方案也具有足夠的可靠性以返回false)將成為以下用法按位異或運(yùn)算符:
functionisInteger(x){return(x^0)===x;}
下面的解決方案也可以工作敷搪,盡管不如上面那樣高雅
functionisInteger(x){returnMath.round(x)===x;}
請(qǐng)注意,在上面的實(shí)現(xiàn)中Math.ceil()或Math.floor()可以同樣使用(而不是Math.round())幢哨。
或者:
functionisInteger(x){return(typeofx==='number')&&(x%1===0);}
一個(gè)相當(dāng)常見的不正確的解決方案如下:
functionisInteger(x){returnparseInt(x,10)===x;}
雖然這個(gè)基于parseInt的方法對(duì)許多x值很有效赡勘,但一旦x變得相當(dāng)大,它將無法正常工作嘱么。問題是parseInt()在解析數(shù)字之前將其第一個(gè)參數(shù)強(qiáng)制轉(zhuǎn)換為字符串狮含。因此,一旦數(shù)字變得足夠大曼振,其字符串表示將以指數(shù)形式呈現(xiàn)(例如1e + 21)几迄。因此,parseInt()將嘗試解析1e + 21冰评,但是當(dāng)它到達(dá)e字符時(shí)將停止解析映胁,因此將返回值1.觀察:
>String(1000000000000000000000)
'1e+21'
>parseInt(1000000000000000000000,10)
1
>parseInt(1000000000000000000000,10)===1000000000000000000000
false
9、執(zhí)行下面的代碼時(shí)甲雅,按什么順序?qū)?shù)字1-4記錄到控制臺(tái)解孙?為什么坑填?
(function(){
console.log(1);
setTimeout(function(){console.log(2)},1000);
setTimeout(function(){console.log(3)},0);
console.log(4);
})();
這些值將按以下順序記錄:
1
4
3
2
我們先來解釋一下這些可能更為明顯的部分:
首先顯示1和4,因?yàn)樗鼈兪峭ㄟ^簡(jiǎn)單調(diào)用console.log()而沒有任何延遲記錄的
在3之后顯示弛姜,因?yàn)樵谘舆t1000毫秒(即1秒)之后記錄2脐瑰,而在0毫秒的延遲之后記錄3。
好的廷臼。但是苍在,如果在延遲0毫秒后記錄3,這是否意味著它正在被立即記錄荠商?而且寂恬,如果是這樣,不應(yīng)該在4之前記錄它莱没,因?yàn)?是由后面的代碼行記錄的嗎初肉?
答案與正確理解JavaScript事件和時(shí)間有關(guān)。
瀏覽器有一個(gè)事件循環(huán)饰躲,它檢查事件隊(duì)列并處理未決事件牙咏。例如,如果在瀏覽器繁忙時(shí)(例如属铁,處理onclick)在后臺(tái)發(fā)生事件(例如腳本onload事件)眠寿,則該事件被附加到隊(duì)列中。當(dāng)onclick處理程序完成時(shí)焦蘑,將檢查隊(duì)列并處理該事件(例如,執(zhí)行onload腳本)盒发。
同樣例嘱,如果瀏覽器繁忙,setTimeout()也會(huì)將其引用函數(shù)的執(zhí)行放入事件隊(duì)列中宁舰。
當(dāng)值為零作為setTimeout()的第二個(gè)參數(shù)傳遞時(shí)拼卵,它將嘗試“盡快”執(zhí)行指定的函數(shù)。具體來說蛮艰,函數(shù)的執(zhí)行放置在事件隊(duì)列中腋腮,以在下一個(gè)計(jì)時(shí)器滴答時(shí)發(fā)生。但請(qǐng)注意壤蚜,這不是直接的;該功能不會(huì)執(zhí)行即寡,直到下一個(gè)滴答聲。這就是為什么在上面的例子中袜刷,調(diào)用console.log(4)發(fā)生在調(diào)用console.log(3)之前(因?yàn)檎{(diào)用console.log(3)是通過setTimeout調(diào)用的聪富,所以稍微延遲了一點(diǎn))。
10著蟹、編寫一個(gè)簡(jiǎn)單的函數(shù)(少于160個(gè)字符)墩蔓,返回一個(gè)布爾值梢莽,指示字符串是否是palindrome。
如果str是回文奸披,以下一行函數(shù)將返回true;否則昏名,它返回false。
functionisPalindrome(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'
11阵面、寫一個(gè)sum方法轻局,當(dāng)使用下面的語(yǔ)法調(diào)用時(shí)它將正常工作。
console.log(sum(2,3));// Outputs 5
console.log(sum(2)(3));// Outputs 5
有(至少)兩種方法可以做到這一點(diǎn):
METHOD 1
functionsum(x){
if(arguments.length==2){
returnarguments[0]+arguments[1];
}else{
returnfunction(y){returnx+y;};
}
}
在JavaScript中膜钓,函數(shù)提供對(duì)參數(shù)對(duì)象的訪問嗽交,該對(duì)象提供對(duì)傳遞給函數(shù)的實(shí)際參數(shù)的訪問。這使我們能夠使用length屬性在運(yùn)行時(shí)確定傳遞給函數(shù)的參數(shù)的數(shù)量
如果傳遞兩個(gè)參數(shù)颂斜,我們只需將它們相加并返回夫壁。
否則,我們假設(shè)它是以sum(2)(3)的形式被調(diào)用的沃疮,所以我們返回一個(gè)匿名函數(shù)盒让,它將傳遞給sum()(在本例中為2)的參數(shù)和傳遞給匿名函數(shù)的參數(shù)(這種情況3)。
METHOD 2
functionsum(x,y){
if(y!==undefined){
returnx+y;
}else{
returnfunction(y){returnx+y;};
}
}
當(dāng)函數(shù)被調(diào)用時(shí)司蔬,JavaScript不需要參數(shù)的數(shù)量來匹配函數(shù)定義中參數(shù)的數(shù)量邑茄。如果傳遞的參數(shù)數(shù)量超過了函數(shù)定義中參數(shù)的數(shù)量,則超出的參數(shù)將被忽略俊啼。另一方面肺缕,如果傳遞的參數(shù)數(shù)量少于函數(shù)定義中的參數(shù)數(shù)量,則在函數(shù)內(nèi)引用時(shí)授帕,缺少的參數(shù)將具有未定義的值同木。因此,在上面的例子中跛十,通過簡(jiǎn)單地檢查第二個(gè)參數(shù)是否未定義彤路,我們可以確定函數(shù)被調(diào)用的方式并相應(yīng)地繼續(xù)。
12芥映、考慮下面的代碼片段
for(vari=0;i<5;i++){
varbtn=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)擊“按鈕4”時(shí)洲尊,什么被記錄到控制臺(tái)?為什么奈偏?
(b) 提供一個(gè)或多個(gè)可按預(yù)期工作的替代實(shí)現(xiàn)坞嘀。
答:
(a) 無論用戶點(diǎn)擊哪個(gè)按鈕,數(shù)字5將始終記錄到控制臺(tái)霎苗。這是因?yàn)槟房裕谡{(diào)用onclick方法(對(duì)于任何按鈕)時(shí),for循環(huán)已經(jīng)完成唁盏,并且變量i已經(jīng)具有值5.(如果受訪者知道足夠的話就可以獲得獎(jiǎng)勵(lì)點(diǎn)數(shù)關(guān)于執(zhí)行上下文内狸,變量對(duì)象检眯,激活對(duì)象和內(nèi)部“范圍”屬性如何影響閉包行為。)
(b) 使這項(xiàng)工作的關(guān)鍵是通過將它傳遞給新創(chuàng)建的函數(shù)對(duì)象來捕獲每次通過for循環(huán)的i的值昆淡。以下是四種可能的方法來實(shí)現(xiàn)這一點(diǎn):
for(vari=0;i<5;i++){
varbtn=document.createElement('button');
btn.appendChild(document.createTextNode('Button '+i));
btn.addEventListener('click',(function(i){
returnfunction(){console.log(i);};
})(i));
document.body.appendChild(btn);
}
或者锰瘸,您可以將新的匿名函數(shù)中的整個(gè)調(diào)用包裝為btn.addEventListener:
for(vari=0;i<5;i++){
varbtn=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){
varbtn=document.createElement('button');
btn.appendChild(document.createTextNode('Button '+i));
btn.addEventListener('click',function(){console.log(i);});
document.body.appendChild(btn);
});
最后昂灵,最簡(jiǎn)單的解決方案避凝,如果你在ES6 / ES2015上下文中,就是使用let i而不是var i:
for(leti=0;i<5;i++){
varbtn=document.createElement('button');
btn.appendChild(document.createTextNode('Button '+i));
btn.addEventListener('click',function(){console.log(i);});
document.body.appendChild(btn);
}
13眨补、假設(shè)d是范圍內(nèi)的“空”對(duì)象:
vard={};
使用下面的代碼完成了什么管削?
['zebra','horse'].forEach(function(k){
d[k]=undefined;
});
上面顯示的代碼片段在對(duì)象d上設(shè)置了兩個(gè)屬性。理想情況下撑螺,對(duì)具有未設(shè)置鍵的JavaScript對(duì)象執(zhí)行的查找評(píng)估為未定義含思。但是運(yùn)行這段代碼會(huì)將這些屬性標(biāo)記為對(duì)象的“自己的屬性”。
這是確保對(duì)象具有一組給定屬性的有用策略甘晤。將該對(duì)象傳遞給Object.keys將返回一個(gè)包含這些設(shè)置鍵的數(shù)組(即使它們的值未定義)含潘。
14、下面的代碼將輸出到控制臺(tái)线婚,為什么遏弱?
vararr1="john".split('');
vararr2=arr1.reverse();
vararr3="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));
記錄的輸出將是:
"array 1: length=5 last=j,o,n,e,s"
"array 2: length=5 last=j,o,n,e,s"
arr1和arr2是相同的(即['n','h'塞弊,'o'漱逸,'j',['j'游沿,'o'虹脯,'n','e'奏候,'s']])上述代碼由于以下原因而被執(zhí)行:
調(diào)用數(shù)組對(duì)象的reverse()方法不僅以相反的順序返回?cái)?shù)組,它還顛倒了數(shù)組本身的順序(即在這種情況下唇敞,arr1)蔗草。
reverse()方法返回對(duì)數(shù)組本身的引用(即,在這種情況下為arr1)疆柔。因此咒精,arr2僅僅是對(duì)arr1的引用(而不是副本)。因此旷档,當(dāng)對(duì)arr2做任何事情時(shí)(即模叙,當(dāng)我們調(diào)用arr2.push(arr3);)時(shí),arr1也會(huì)受到影響鞋屈,因?yàn)閍rr1和arr2只是對(duì)同一個(gè)對(duì)象的引用范咨。
這里有幾個(gè)觀點(diǎn)可以讓人們回答這個(gè)問題:
將數(shù)組傳遞給另一個(gè)數(shù)組的push()方法會(huì)將整個(gè)數(shù)組作為單個(gè)元素推入數(shù)組的末尾故觅。結(jié)果,聲明arr2.push(arr3);將arr3作為一個(gè)整體添加到arr2的末尾(即渠啊,它不連接兩個(gè)數(shù)組输吏,這就是concat()方法的用途)。
像Python一樣替蛉,JavaScript在調(diào)用像slice()這樣的數(shù)組方法時(shí)贯溅,會(huì)承認(rèn)負(fù)面下標(biāo),以此作為在數(shù)組末尾引用元素的方式;例如躲查,下標(biāo)-1表示數(shù)組中的最后一個(gè)元素它浅,依此類推。
15镣煮、下面的代碼將輸出到控制臺(tái)姐霍,為什么?
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);
以上代碼將輸出到控制臺(tái):
"122"
"32"
"02"
"112"
"NaN2"
NaN
這是為什么...
這里的基本問題是JavaScript(ECMAScript)是一種松散類型的語(yǔ)言怎静,它對(duì)值執(zhí)行自動(dòng)類型轉(zhuǎn)換以適應(yīng)正在執(zhí)行的操作邮弹。讓我們來看看這是如何與上面的每個(gè)例子進(jìn)行比較。
示例1:1 +“2”+“2”輸出:“122”說明:第一個(gè)操作在1 +“2”中執(zhí)行蚓聘。由于其中一個(gè)操作數(shù)(“2”)是一個(gè)字符串腌乡,所以JavaScript假定需要執(zhí)行字符串連接,因此將1的類型轉(zhuǎn)換為“1”夜牡,1 +“2”轉(zhuǎn)換為“12”与纽。然后,“12”+“2”產(chǎn)生“122”塘装。
示例2:1 + +“2”+“2”輸出:“32”說明:根據(jù)操作順序急迂,要執(zhí)行的第一個(gè)操作是+“2”(第一個(gè)“2”之前的額外+被視為一個(gè)一元運(yùn)算符)。因此蹦肴,JavaScript將“2”的類型轉(zhuǎn)換為數(shù)字僚碎,然后將一元+符號(hào)應(yīng)用于它(即將其視為正數(shù))。結(jié)果阴幌,下一個(gè)操作現(xiàn)在是1 + 2勺阐,當(dāng)然這會(huì)產(chǎn)生3.但是,我們有一個(gè)數(shù)字和一個(gè)字符串之間的操作(即3和“2”)矛双,所以JavaScript再次轉(zhuǎn)換數(shù)值賦給一個(gè)字符串并執(zhí)行字符串連接可训,產(chǎn)生“32”讼渊。
示例3:1 + - “1”+“2”輸出:“02”說明:這里的解釋與前面的示例相同劳吠,只是一元運(yùn)算符是 - 而不是+村刨。因此,“1”變?yōu)?,然后在應(yīng)用 - 時(shí)將其變?yōu)?1愤估,然后將其加1到產(chǎn)生0帮辟,然后轉(zhuǎn)換為字符串并與最終的“2”操作數(shù)連接,產(chǎn)生“02”灵疮。
示例4:+“1”+“1”+“2”輸出:“112”說明:盡管第一個(gè)“1”操作數(shù)是基于其前面的一元+運(yùn)算符的數(shù)值類型轉(zhuǎn)換的织阅,當(dāng)它與第二個(gè)“1”操作數(shù)連接在一起時(shí)返回一個(gè)字符串,然后與最終的“2”操作數(shù)連接震捣,產(chǎn)生字符串“112”荔棉。
示例5:“A” - “B”+“2”輸出:“NaN2”說明:由于 - 運(yùn)算符不能應(yīng)用于字符串,并且既不能將“A”也不能將“B”轉(zhuǎn)換為數(shù)值蒿赢, “ - ”B“產(chǎn)生NaN润樱,然后與字符串”2“串聯(lián)產(chǎn)生”NaN2“。
例6:“A” - “B”+2輸出:NaN說明:在前面的例子中羡棵,“A” - “B”產(chǎn)生NaN壹若。但是任何運(yùn)算符應(yīng)用于NaN和其他數(shù)字操作數(shù)仍然會(huì)產(chǎn)生NaN。
16皂冰、如果數(shù)組列表太大店展,以下遞歸代碼將導(dǎo)致堆棧溢出。你如何解決這個(gè)問題秃流,仍然保留遞歸模式赂蕴?
varlist=readHugeList();
varnextListItem=function(){
varitem=list.pop();
if(item){
// process the list item...
nextListItem();
}
};
通過修改nextListItem函數(shù)可以避免潛在的堆棧溢出,如下所示:
varlist=readHugeList();
varnextListItem=function(){
varitem=list.pop();
if(item){
// process the list item...
setTimeout(nextListItem,0);
}
};
堆棧溢出被消除舶胀,因?yàn)槭录h(huán)處理遞歸概说,而不是調(diào)用堆棧。當(dāng)nextListItem運(yùn)行時(shí)嚣伐,如果item不為null糖赔,則將超時(shí)函數(shù)(nextListItem)推送到事件隊(duì)列,并且函數(shù)退出轩端,從而使調(diào)用堆棧清零放典。當(dāng)事件隊(duì)列運(yùn)行超時(shí)事件時(shí),將處理下一個(gè)項(xiàng)目基茵,并設(shè)置一個(gè)計(jì)時(shí)器以再次調(diào)用nextListItem刻撒。因此,該方法從頭到尾不經(jīng)過直接遞歸調(diào)用即可處理耿导,因此調(diào)用堆棧保持清晰,無論迭代次數(shù)如何态贤。
17舱呻、什么是JavaScript中的“閉包”?舉一個(gè)例子。
閉包是一個(gè)內(nèi)部函數(shù)箱吕,它可以訪問外部(封閉)函數(shù)的作用域鏈中的變量芥驳。閉包可以訪問三個(gè)范圍內(nèi)的變量;具體來說: (1)變量在其自己的范圍內(nèi), (2)封閉函數(shù)范圍內(nèi)的變量 (3)全局變量茬高。
這里是一個(gè)例子:
varglobalVar="xyz";
(functionouterFunc(outerArg){
varouterVar='a';
(functioninnerFunc(innerArg){
varinnerVar='b';
console.log(
"outerArg = "+outerArg+"\n"+
"innerArg = "+innerArg+"\n"+
"outerVar = "+outerVar+"\n"+
"innerVar = "+innerVar+"\n"+
"globalVar = "+globalVar);
})(456);
})(123);
在上面的例子中兆旬,innerFunc,outerFunc和全局名稱空間的變量都在innerFunc的范圍內(nèi)怎栽。上面的代碼將產(chǎn)生以下輸出:
outerArg=123
innerArg=456
outerVar=a
innerVar=b
globalVar=xyz
18丽猬、以下代碼的輸出是什么:
for(vari=0;i<5;i++){
setTimeout(function(){console.log(i);},i*1000);
}
解釋你的答案。如何在這里使用閉包熏瞄?
顯示的代碼示例不會(huì)顯示值0,1,2,3和4脚祟,這可能是預(yù)期的;而是顯示5,5,5,5。
這是因?yàn)檠h(huán)內(nèi)執(zhí)行的每個(gè)函數(shù)將在整個(gè)循環(huán)完成后執(zhí)行强饮,因此所有函數(shù)都會(huì)引用存儲(chǔ)在i中的最后一個(gè)值由桌,即5。
通過為每次迭代創(chuàng)建一個(gè)唯一的作用域邮丰,可以使用閉包來防止這個(gè)問題行您,并將該變量的每個(gè)唯一值存儲(chǔ)在其作用域中,如下所示:
for(vari=0;i<5;i++){
(function(x){
setTimeout(function(){console.log(x);},x*1000);
})(i);
}
這會(huì)產(chǎn)生將0,1,2,3和4記錄到控制臺(tái)的可能結(jié)果剪廉。
在ES2015上下文中娃循,您可以在原始代碼中簡(jiǎn)單地使用let而不是var:
for(leti=0;i<5;i++){
setTimeout(function(){console.log(i);},i*1000);
}
19、以下幾行代碼輸出到控制臺(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=1
1||2=1
0&&1=0
1&&2=2
在JavaScript中,都是||和&&是邏輯運(yùn)算符吹泡,當(dāng)從左向右計(jì)算時(shí)返回第一個(gè)完全確定的“邏輯值”骤星。
或(||)運(yùn)算符。在形式為X || Y的表達(dá)式中爆哑,首先計(jì)算X并將其解釋為布爾值洞难。如果此布爾值為真,則返回true(1)揭朝,并且不計(jì)算Y队贱,因?yàn)椤盎颉睏l件已經(jīng)滿足。但是潭袱,如果此布爾值為“假”柱嫌,我們?nèi)匀徊恢繶 || Y是真還是假,直到我們?cè)u(píng)估Y屯换,并將其解釋為布爾值编丘。
因此与学,0 || 1評(píng)估為真(1),正如1 || 2嘉抓。
和(&&)運(yùn)算符索守。在X && Y形式的表達(dá)式中,首先評(píng)估X并將其解釋為布爾值抑片。如果此布爾值為false卵佛,則返回false(0)并且不評(píng)估Y,因?yàn)椤癮nd”條件已失敗敞斋。但是截汪,如果這個(gè)布爾值為“真”,我們?nèi)匀徊恢繶 && Y是真還是假渺尘,直到我們?cè)u(píng)估Y挫鸽,并將其解釋為布爾值。
然而鸥跟,&&運(yùn)算符的有趣之處在于丢郊,當(dāng)表達(dá)式評(píng)估為“真”時(shí),則返回表達(dá)式本身医咨。這很好枫匾,因?yàn)樗谶壿嫳磉_(dá)式中被視為“真”,但也可以用于在您關(guān)心時(shí)返回該值拟淮。這解釋了為什么干茉,有點(diǎn)令人驚訝的是,1 && 2返回2(而你可能會(huì)期望它返回true或1)很泊。
20 角虫、下面的代碼執(zhí)行時(shí)輸出是什么?說明委造。
console.log(false=='0')
console.log(false==='0')
該代碼將輸出:
true
false
在JavaScript中戳鹅,有兩套相等運(yùn)算符。三重相等運(yùn)算符===的行為與任何傳統(tǒng)的相等運(yùn)算符相同:如果兩側(cè)的兩個(gè)表達(dá)式具有相同的類型和相同的值昏兆,則計(jì)算結(jié)果為true枫虏。然而,雙等號(hào)運(yùn)算符在比較它們之前試圖強(qiáng)制這些值爬虱。因此隶债,通常使用===而不是==。對(duì)于跑筝!== vs死讹!=也是如此。
21曲梗、以下代碼的輸出是什么回俐?解釋你的答案逛腿。
vara={},
b={key:'b'},
c={key:'c'};
a[b]=123;
a[c]=456;
console.log(a[b]);
此代碼的輸出將是456(不是123)。
原因如下:設(shè)置對(duì)象屬性時(shí)仅颇,JavaScript會(huì)隱式地將參數(shù)值串聯(lián)起來。在這種情況下碘举,由于b和c都是對(duì)象忘瓦,它們都將被轉(zhuǎn)換為“[object Object]”。因此引颈,a [b]和a [c]都等價(jià)于[“[object Object]”]耕皮,并且可以互換使用。因此蝙场,設(shè)置或引用[c]與設(shè)置或引用[b]完全相同凌停。
22、以下代碼將輸出到控制臺(tái)中.
console.log((functionf(n){return((n>1)?n*f(n-1):n)})(10));
該代碼將輸出10階乘的值(即10售滤!或3,628,800)罚拟。
原因如下:
命名函數(shù)f()以遞歸方式調(diào)用自身,直到它調(diào)用f(1)完箩,它簡(jiǎn)單地返回1.因此赐俗,這就是它的作用:
f(1):returns n,which is1
f(2):returns2*f(1),which is2
f(3):returns3*f(2),which is6
f(4):returns4*f(3),which is24
f(5):returns5*f(4),which is120
f(6):returns6*f(5),which is720
f(7):returns7*f(6),which is5040
f(8):returns8*f(7),which is40320
f(9):returns9*f(8),which is362880
f(10):returns10*f(9),which is3628800
23 、考慮下面的代碼片段弊知∽璐控制臺(tái)的輸出是什么,為什么秩彤?
(function(x){
return(function(y){
console.log(x);
})(2)
})(1);
輸出將為1叔扼,即使x的值從未在內(nèi)部函數(shù)中設(shè)置。原因如下:
正如我們的JavaScript招聘指南中所解釋的漫雷,閉包是一個(gè)函數(shù)瓜富,以及創(chuàng)建閉包時(shí)在范圍內(nèi)的所有變量或函數(shù)。在JavaScript中珊拼,閉包被實(shí)現(xiàn)為“內(nèi)部函數(shù)”;即在另一功能的主體內(nèi)定義的功能食呻。閉包的一個(gè)重要特征是內(nèi)部函數(shù)仍然可以訪問外部函數(shù)的變量。
因此澎现,在這個(gè)例子中仅胞,因?yàn)閤沒有在內(nèi)部函數(shù)中定義,所以在外部函數(shù)的作用域中搜索一個(gè)定義的變量x剑辫,該變量的值為1干旧。
24、以下代碼將輸出到控制臺(tái)以及為什么
varhero={
_name:'John Doe',
getSecretIdentity:function(){
returnthis._name;
}
};
varstoleSecretIdentity=hero.getSecretIdentity;
console.log(stoleSecretIdentity());
console.log(hero.getSecretIdentity());
這段代碼有什么問題妹蔽,以及如何解決這個(gè)問題椎眯。
該代碼將輸出:
undefined
John Doe
第一個(gè)console.log打印未定義挠将,因?yàn)槲覀儚膆ero對(duì)象中提取方法,所以stoleSecretIdentity()在_name屬性不存在的全局上下文(即窗口對(duì)象)中被調(diào)用编整。
修復(fù)stoleSecretIdentity()函數(shù)的一種方法如下:
varstoleSecretIdentity=hero.getSecretIdentity.bind(hero);
25舔稀、創(chuàng)建一個(gè)函數(shù),給定頁(yè)面上的DOM元素掌测,將訪問元素本身及其所有后代(不僅僅是它的直接子元素)内贮。對(duì)于每個(gè)訪問的元素,函數(shù)應(yīng)該將該元素傳遞給提供的回調(diào)函數(shù)汞斧。
該函數(shù)的參數(shù)應(yīng)該是:
一個(gè) DOM 元素
一個(gè)回調(diào)函數(shù)(以DOM元素作為參數(shù))
訪問樹中的所有元素(DOM)是經(jīng)典的深度優(yōu)先搜索算法應(yīng)用程序夜郁。以下是一個(gè)示例解決方案:
functionTraverse(p_element,p_callback){
p_callback(p_element);
varlist=p_element.children;
for(vari=0;i
Traverse(list[i],p_callback);// recursive call
}
}
27、在JavaScript中測(cè)試您的這些知識(shí):以下代碼的輸出是什么粘勒?
varlength=10;
functionfn(){
console.log(this.length);
}
varobj={
length:5,
method:function(fn){
fn();
arguments[0]();
}
};
obj.method(fn,1);
輸出:
10
2
為什么不是10和5竞端?
首先,由于fn作為函數(shù)方法的參數(shù)傳遞庙睡,函數(shù)fn的作用域(this)是窗口事富。 var length = 10;在窗口級(jí)別聲明。它也可以作為window.length或length或this.length來訪問(當(dāng)這個(gè)===窗口時(shí))埃撵。
方法綁定到Object obj赵颅,obj.method用參數(shù)fn和1調(diào)用。雖然方法只接受一個(gè)參數(shù)暂刘,但調(diào)用它時(shí)已經(jīng)傳遞了兩個(gè)參數(shù);第一個(gè)是函數(shù)回調(diào)饺谬,其他只是一個(gè)數(shù)字。
當(dāng)在內(nèi)部方法中調(diào)用fn()時(shí)谣拣,該函數(shù)在全局級(jí)別作為參數(shù)傳遞募寨,this.length將有權(quán)訪問在Object obj中定義的var length = 10(全局聲明)而不是length = 5。
現(xiàn)在森缠,我們知道我們可以使用arguments []數(shù)組訪問JavaScript函數(shù)中的任意數(shù)量的參數(shù)拔鹰。
因此arguments0只不過是調(diào)用fn()。在fn里面贵涵,這個(gè)函數(shù)的作用域成為參數(shù)數(shù)組列肢,并且記錄參數(shù)[]的長(zhǎng)度將返回2。
因此輸出將如上所述宾茂。
28瓷马、考慮下面的代碼。輸出是什么跨晴,為什么欧聘?
(function(){
try{
thrownewError();
}catch(x){
varx=1,y=2;
console.log(x);
}
console.log(x);
console.log(y);
})();
1
undefined
2
var語(yǔ)句被掛起(沒有它們的值初始化)到它所屬的全局或函數(shù)作用域的頂部,即使它位于with或catch塊內(nèi)端盆。但是怀骤,錯(cuò)誤的標(biāo)識(shí)符只在catch塊內(nèi)部可見费封。它相當(dāng)于:
(function(){
varx,y;// outer and hoisted
try{
thrownewError();
}catch(x/* inner */){
x=1;// inner x, not the outer one
y=2;// there is only one y, which is in the outer scope
console.log(x/* inner */);
}
console.log(x);
console.log(y);
})();
29、這段代碼的輸出是什么蒋伦?
varx=21;
vargirl=function(){
console.log(x);
varx=20;
};
girl();
21弓摘,也不是20,結(jié)果是‘undefined’的
這是因?yàn)镴avaScript初始化沒有被掛起痕届。
(為什么它不顯示21的全局值衣盾?原因是當(dāng)函數(shù)執(zhí)行時(shí),它檢查是否存在本地x變量但尚未聲明它爷抓,因此它不會(huì)查找全局變量。)
30阻塑、你如何克隆一個(gè)對(duì)象蓝撇?
varobj={a:1,b:2}
varobjclone=Object.assign({},obj);
現(xiàn)在objclone的值是{a:1,b:2}陈莽,但指向與obj不同的對(duì)象渤昌。
但請(qǐng)注意潛在的缺陷:Object.clone()只會(huì)執(zhí)行淺拷貝,而不是深拷貝走搁。這意味著嵌套的對(duì)象不會(huì)被復(fù)制独柑。他們?nèi)匀灰门c原始相同的嵌套對(duì)象:
letobj={
a:1,
b:2,
c:{
age:30
}
};
varobjclone=Object.assign({},obj);
console.log('objclone: ',objclone);
obj.c.age=45;
console.log('After Change - obj: ',obj);// 45 - This also changes
console.log('After Change - objclone: ',objclone);// 45
31、此代碼將打印什么私植?
for(leti=0;i<5;i++){
setTimeout(function(){console.log(i);},i*1000);
}
它會(huì)打印0 1 2 3 4忌栅,因?yàn)槲覀冊(cè)谶@里使用let而不是var。變量i只能在for循環(huán)的塊范圍中看到曲稼。
32索绪、以下幾行輸出什么,為什么贫悄?
console.log(1<2<3);
console.log(3>2>1);
第一條語(yǔ)句返回true瑞驱,如預(yù)期的那樣。
第二個(gè)返回false是因?yàn)橐嫒绾吾槍?duì)<和>的操作符關(guān)聯(lián)性工作窄坦。它比較從左到右唤反,所以3> 2> 1 JavaScript翻譯為true> 1. true具有值1,因此它比較1> 1鸭津,這是錯(cuò)誤的彤侍。
33、如何在數(shù)組的開頭添加元素曙博?最后如何添加一個(gè)拥刻?
varmyArray=['a','b','c','d'];
myArray.push('end');
myArray.unshift('start');
console.log(myArray);// ["start", "a", "b", "c", "d", "end"]
使用ES6,可以使用擴(kuò)展運(yùn)算符:
myArray=['start',...myArray];
myArray=[...myArray,'end'];
或者父泳,簡(jiǎn)而言之:
myArray=['start',...myArray,'end'];
34般哼、想象一下你有這樣的代碼:
vara=[1,2,3];
a)這會(huì)導(dǎo)致崩潰嗎吴汪?
a[10]=99;
b)這個(gè)輸出是什么?
console.log(a[6]);
a)它不會(huì)崩潰蒸眠。 JavaScript引擎將使陣列插槽3至9成為“空插槽”漾橙。
b)在這里,a [6]將輸出未定義的值楞卡,但時(shí)隙仍為空霜运,而不是未定義的。在某些情況下蒋腮,這可能是一個(gè)重要的細(xì)微差別淘捡。例如,使用map()時(shí)池摧,map()的輸出中的空插槽將保持為空焦除,但未定義的插槽將使用傳遞給它的函數(shù)重映射:
varb=[undefined];
b[2]=1;
console.log(b);// (3) [undefined, empty × 1, 1]
console.log(b.map(e=>7));// (3) [7, empty × 1, 7]
35、typeof undefined == typeof NULL的值是什么作彤?
該表達(dá)式將被評(píng)估為true膘魄,因?yàn)镹ULL將被視為任何其他未定義的變量。
注意:JavaScript區(qū)分大小寫竭讳,我們?cè)谶@里使用NULL而不是null创葡。
36、代碼返回后會(huì)怎么樣绢慢?
console.log(typeoftypeof1);
打印結(jié)果:string
typeof 1將返回“number”灿渴,typeof“number”將返回字符串。
37呐芥、以下代碼輸出什么逻杖?為什么?
varb=1;
functionouter(){
varb=2
functioninner(){
b++;
varb=3;
console.log(b)
}
inner();
}
outer();
輸出到控制臺(tái)將是“3”思瘟。
在這個(gè)例子中有三個(gè)閉包荸百,每個(gè)都有它自己的var b聲明。當(dāng)調(diào)用變量時(shí)滨攻,將按照從本地到全局的順序檢查閉包够话,直到找到實(shí)例。由于內(nèi)部閉包有自己的b變量光绕,這就是輸出女嘲。
此外,由于提升內(nèi)部的代碼將被解釋如下:
functioninner(){
varb;// b is undefined
b++;// b is NaN
b=3;// b is 3
console.log(b);// output "3"
}
面試比棘手的技術(shù)問題要多诞帐,所以這些僅僅是作為指導(dǎo)欣尼。并不是每個(gè)值得聘用的“A”候選人都能夠回答所有問題,能夠回答所有問題的也不能保證是“A”候選人。最后愕鼓,招聘仍然是一門藝術(shù)钙态,一門科學(xué) - 還有很多工作要做。