JavaScript

1、使用typeof bar ===“object”來確定bar是否是一個對象時有什么潛在的缺陷?這個陷阱如何避免?

盡管typeof bar ===“object”是檢查bar是否是對象的可靠方法,但JavaScript中令人驚訝的問題null也被認為是一個對象!

因此,對于大多數開發人員來說,下面的代碼會將true(而不是false)打印到控制臺:

varbar=null;

console.log(typeofbar==="object");// logs true!

只要知道這一點,就可以通過檢查bar是否為空來輕松避免該問題:

console.log((bar!==null)&&(typeofbar==="object"));// logs false

為了讓我們的答案更加的完整,還有兩件事值得注意: 首先,如果bar是一個函數,上面的解決方案將返回false。在大多數情況下,這是所期望的行為,但是在您希望函數返回true的情況下,您可以將上述解決方案修改為:

console.log((bar!==null)&&((typeofbar==="object")||(typeofbar==="function")));

其次,如果bar是數組,則上述解決方案將返回true(例如,如果var bar = [];)。在大多數情況下,這是所希望的行為,因為數組確實是對象,但是在您想要對數組也是false的情況下,可以將上述解決方案修改為:

console.log((bar!==null)&&(typeofbar==="object")&&(toString.call(bar)!=="[object Array]"));

但是,還有一個替代方法對空值,數組和函數返回false,但對于對象則為true:

console.log((bar!==null)&&(bar.constructor===Object));

或者,如果您使用jQuery:

console.log((bar!==null)&&(typeofbar==="object")&&(!$.isArray(bar)));

ES5使得數組的情況非常簡單,包括它自己的空檢查:

console.log(Array.isArray(bar));

2、下面的代碼將輸出到控制臺的是什么,為什么?

(function(){

vara=b=3;

})();

console.log("a defined? "+(typeofa!=='undefined'));

console.log("b defined? "+(typeofb!=='undefined'));

由于a和b都在函數的封閉范圍內定義,并且由于它們所在的行以var關鍵字開頭,因此大多數JavaScript開發人員會希望typeof a和typeof b在上面的示例中都未定義。

但是,情況并非如此。這里的問題是大多數開發人員錯誤地理解語句var a = b = 3;以下簡寫為:

varb=3;

vara=b;

但實際上,var a = b = 3;其實是速記:

b=3;

vara=b;

因此(如果您不使用嚴格模式),代碼片段的輸出將為:

a defined?false

b defined?true

但是如何在封閉函數的范圍之外定義b?那么,因為聲明var a = b = 3;是語句b = 3的簡寫;并且var a = b; b最終成為一個全局變量(因為它不在var關鍵字后面),因此它仍然在作用域內,即使在封閉函數之外。

注意,在嚴格模式下(即,使用strict),語句var a = b = 3;會產生一個ReferenceError的運行時錯誤:b沒有定義,從而避免了可能導致的任何頭headfakes/bugs。 (這就是為什么你應該在你的代碼中使用strict,一個重要的例子!)

3、下面的代碼將輸出到控制臺的是什么?,為什么?

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();

以上代碼將輸出到控制臺:

outer func:this.foo=bar

outer func:self.foo=bar

inner func:this.foo=undefined

inner func:self.foo=bar

在外部函數中,this和self都引用myObject,因此都可以正確地引用和訪問foo。

但在內部函數中,這不再指向myObject。因此,this.foo在內部函數中是未定義的,而對局部變量self的引用仍然在范圍內并且可以在那里訪問。

4、在功能塊中封裝JavaScript源文件的全部內容的重要性和原因是什么?

這是一種日益普遍的做法,被許多流行的JavaScript庫(jQuery,Node.js等)所采用。這種技術在文件的全部內容周圍創建一個閉包,這可能最重要的是創建一個私有名稱空間,從而有助于避免不同JavaScript模塊和庫之間的潛在名稱沖突。

這種技術的另一個特點是為全局變量提供一個容易引用(可能更短)的別名。例如,這通常用于jQuery插件。 jQuery允許您使用jQuery.noConflict()來禁用對jQuery名稱空間的$引用。如果這樣做了,你的代碼仍然可以使用$使用閉包技術,如下所示:

(function($){/* jQuery plugin code referencing $ */})(jQuery);

5、在JavaScript源文件的開頭包含'use strict'的意義和有什么好處?

這里最簡單也是最重要的答案是use strict是一種在運行時自動執行更嚴格的JavaScript代碼解析和錯誤處理的方法。如果代碼錯誤被忽略或失敗,將會產生錯誤或拋出異常。總的來說,這是一個很好的做法。

嚴格模式的一些主要優點包括:

使調試更容易。如果代碼錯誤本來會被忽略或失敗,那么現在將會產生錯誤或拋出異常,從而更快地發現代碼中的問題,并更快地指引它們的源代碼。

防止意外全局。如果沒有嚴格模式,將值賦給未聲明的變量會自動創建一個具有該名稱的全局變量。這是JavaScript中最常見的錯誤之一。在嚴格模式下,嘗試這樣做會引發錯誤。

消除隱藏威脅。在沒有嚴格模式的情況下,對null或undefined的這個值的引用會自動強制到全局。這可能會導致許多headfakespull-out-your-hair類型的錯誤。在嚴格模式下,引用null或undefined的這個值會引發錯誤。

不允許重復的參數值。嚴格模式在檢測到函數的重復命名參數(例如,函數foo(val1,val2,val1){})時會引發錯誤,從而捕獲代碼中幾乎可以肯定存在的錯誤,否則您可能會浪費大量的時間追蹤。

注意:它曾經是(在ECMAScript 5中)strict模式將禁止重復的屬性名稱(例如var object = {foo:“bar”,foo:“baz”};)但是從ECMAScript 2015 開始,就不再有這種情況了。

使eval()更安全。eval()在嚴格模式和非嚴格模式下的行為方式有些不同。最重要的是,在嚴格模式下,在eval()語句內部聲明的變量和函數不會在包含范圍中創建(它們是以非嚴格模式在包含范圍中創建的,這也可能是問題的常見來源)。

拋出無效的使用錯誤的刪除符。刪除操作符(用于從對象中刪除屬性)不能用于對象的不可配置屬性。當試圖刪除一個不可配置的屬性時,非嚴格代碼將自動失敗,而在這種情況下,嚴格模式會引發錯誤。

6、考慮下面的兩個函數。他們都會返回同樣的值嗎?為什么或者為什么不?

functionfoo1(){

return{

bar:"hello"

};

}

functionfoo2(){

return

{

bar:"hello"

};

}

令人驚訝的是,這兩個函數不會返回相同的結果。而是:

console.log("foo1 returns:");

console.log(foo1());

console.log("foo2 returns:");

console.log(foo2());

會產生:

foo1 returns:

Object{bar:"hello"}

foo2 returns:

undefined

這不僅令人驚訝,而且特別令人煩惱的是,foo2()返回未定義而沒有引發任何錯誤。

原因與JavaScript中分號在技術上是可選的事實有關(盡管忽略它們通常是非常糟糕的形式)。因此,在foo2()中遇到包含return語句的行(沒有其他內容)時,會在return語句之后立即自動插入分號。

由于代碼的其余部分是完全有效的,即使它沒有被調用或做任何事情(它只是一個未使用的代碼塊,它定義了一個屬性欄,它等于字符串“hello”),所以不會拋出任何錯誤。

這種行為也被認為是遵循了在JavaScript中將一行開頭大括號放在行尾的約定,而不是在新行的開頭。如此處所示,這不僅僅是JavaScript中的一種風格偏好。

7、什么是NaN?它的類型是什么?如何可靠地測試一個值是否等于NaN?

NaN屬性表示“不是數字”的值。這個特殊值是由于一個操作數是非數字的(例如“abc”/ 4)或者因為操作的結果是非數字而無法執行的。

雖然這看起來很簡單,但NaN有一些令人驚訝的特征,如果人們沒有意識到這些特征,就會導致bug。

一方面,雖然NaN的意思是“不是數字”,但它的類型是,數字:

console.log(typeofNaN==="number");// logs "true"

此外,NaN相比任何事情 - 甚至本身! - 是false:

console.log(NaN===NaN);// logs "false"

測試數字是否等于NaN的半可靠方法是使用內置函數isNaN(),但即使使用 isNaN()也不是一個好的解決方案。.

一個更好的解決方案要么是使用value!==值,如果該值等于NaN,那么只會生成true。另外,ES6提供了一個新的Number.isNaN()函數 ,它與舊的全局isNaN()函數不同,也更加可靠。

8、下面的代碼輸出什么?解釋你的答案。

console.log(0.1+0.2);

console.log(0.1+0.2==0.3);

對這個問題的一個有教養的回答是:“你不能確定。它可能打印出0.3和true,或者可能不打印。 JavaScript中的數字全部用浮點精度處理,因此可能不會總是產生預期的結果。“

上面提供的示例是演示此問題的經典案例。令人驚訝的是,它會打印出來:

0.30000000000000004

false

一個典型的解決方案是比較兩個數字與特殊常數Number.EPSILON之間的絕對差值:

functionareTheNumbersAlmostEqual(num1,num2){

returnMath.abs(num1-num2)

}

console.log(areTheNumbersAlmostEqual(0.1+0.2,0.3));

討論寫函數的可能方法isInteger(x),它確定x是否是一個整數。

這聽起來很平凡,事實上,ECMAscript 6為此正好引入了一個新的Number.isInteger()函數,這是微不足道的。但是,在ECMAScript 6之前,這有點復雜,因為沒有提供與Number.isInteger()方法等價的方法。

問題在于,在ECMAScript規范中,整數只在概念上存在;即數值始終作為浮點值存儲。

考慮到這一點,最簡單,最清潔的ECMAScript-6之前的解決方案(即使將非數字值(例如字符串或空值)傳遞給該函數,該解決方案也具有足夠的可靠性以返回false)將成為以下用法按位異或運算符:

functionisInteger(x){return(x^0)===x;}

下面的解決方案也可以工作,盡管不如上面那樣高雅

functionisInteger(x){returnMath.round(x)===x;}

請注意,在上面的實現中Math.ceil()或Math.floor()可以同樣使用(而不是Math.round())。

或者:

functionisInteger(x){return(typeofx==='number')&&(x%1===0);}

一個相當常見的不正確的解決方案如下:

functionisInteger(x){returnparseInt(x,10)===x;}

雖然這個基于parseInt的方法對許多x值很有效,但一旦x變得相當大,它將無法正常工作。問題是parseInt()在解析數字之前將其第一個參數強制轉換為字符串。因此,一旦數字變得足夠大,其字符串表示將以指數形式呈現(例如1e + 21)。因此,parseInt()將嘗試解析1e + 21,但是當它到達e字符時將停止解析,因此將返回值1.觀察:

>String(1000000000000000000000)

'1e+21'

>parseInt(1000000000000000000000,10)

1

>parseInt(1000000000000000000000,10)===1000000000000000000000

false

9、執行下面的代碼時,按什么順序將數字1-4記錄到控制臺?為什么?

(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,因為它們是通過簡單調用console.log()而沒有任何延遲記錄的

在3之后顯示,因為在延遲1000毫秒(即1秒)之后記錄2,而在0毫秒的延遲之后記錄3。

好的。但是,如果在延遲0毫秒后記錄3,這是否意味著它正在被立即記錄?而且,如果是這樣,不應該在4之前記錄它,因為4是由后面的代碼行記錄的嗎?

答案與正確理解JavaScript事件和時間有關。

瀏覽器有一個事件循環,它檢查事件隊列并處理未決事件。例如,如果在瀏覽器繁忙時(例如,處理onclick)在后臺發生事件(例如腳本onload事件),則該事件被附加到隊列中。當onclick處理程序完成時,將檢查隊列并處理該事件(例如,執行onload腳本)。

同樣,如果瀏覽器繁忙,setTimeout()也會將其引用函數的執行放入事件隊列中。

當值為零作為setTimeout()的第二個參數傳遞時,它將嘗試“盡快”執行指定的函數。具體來說,函數的執行放置在事件隊列中,以在下一個計時器滴答時發生。但請注意,這不是直接的;該功能不會執行,直到下一個滴答聲。這就是為什么在上面的例子中,調用console.log(4)發生在調用console.log(3)之前(因為調用console.log(3)是通過setTimeout調用的,所以稍微延遲了一點)。

10、編寫一個簡單的函數(少于160個字符),返回一個布爾值,指示字符串是否是palindrome。

如果str是回文,以下一行函數將返回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、寫一個sum方法,當使用下面的語法調用時它將正常工作。

console.log(sum(2,3));// Outputs 5

console.log(sum(2)(3));// Outputs 5

有(至少)兩種方法可以做到這一點:

METHOD 1

functionsum(x){

if(arguments.length==2){

returnarguments[0]+arguments[1];

}else{

returnfunction(y){returnx+y;};

}

}

在JavaScript中,函數提供對參數對象的訪問,該對象提供對傳遞給函數的實際參數的訪問。這使我們能夠使用length屬性在運行時確定傳遞給函數的參數的數量

如果傳遞兩個參數,我們只需將它們相加并返回。

否則,我們假設它是以sum(2)(3)的形式被調用的,所以我們返回一個匿名函數,它將傳遞給sum()(在本例中為2)的參數和傳遞給匿名函數的參數(這種情況3)。

METHOD 2

functionsum(x,y){

if(y!==undefined){

returnx+y;

}else{

returnfunction(y){returnx+y;};

}

}

當函數被調用時,JavaScript不需要參數的數量來匹配函數定義中參數的數量。如果傳遞的參數數量超過了函數定義中參數的數量,則超出的參數將被忽略。另一方面,如果傳遞的參數數量少于函數定義中的參數數量,則在函數內引用時,缺少的參數將具有未定義的值。因此,在上面的例子中,通過簡單地檢查第二個參數是否未定義,我們可以確定函數被調用的方式并相應地繼續。

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) 當用戶點擊“按鈕4”時,什么被記錄到控制臺?為什么?

(b) 提供一個或多個可按預期工作的替代實現。

答:

(a) 無論用戶點擊哪個按鈕,數字5將始終記錄到控制臺。這是因為,在調用onclick方法(對于任何按鈕)時,for循環已經完成,并且變量i已經具有值5.(如果受訪者知道足夠的話就可以獲得獎勵點數關于執行上下文,變量對象,激活對象和內部“范圍”屬性如何影響閉包行為。)

(b) 使這項工作的關鍵是通過將它傳遞給新創建的函數對象來捕獲每次通過for循環的i的值。以下是四種可能的方法來實現這一點:

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);

}

或者,您可以將新的匿名函數中的整個調用包裝為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);

}

或者,我們可以通過調用數組對象的原生forEach方法來替換for循環:

['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);

});

最后,最簡單的解決方案,如果你在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、假設d是范圍內的“空”對象:

vard={};

使用下面的代碼完成了什么?

['zebra','horse'].forEach(function(k){

d[k]=undefined;

});

上面顯示的代碼片段在對象d上設置了兩個屬性。理想情況下,對具有未設置鍵的JavaScript對象執行的查找評估為未定義。但是運行這段代碼會將這些屬性標記為對象的“自己的屬性”。

這是確保對象具有一組給定屬性的有用策略。將該對象傳遞給Object.keys將返回一個包含這些設置鍵的數組(即使它們的值未定義)。

14、下面的代碼將輸出到控制臺,為什么?

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']])上述代碼由于以下原因而被執行:

調用數組對象的reverse()方法不僅以相反的順序返回數組,它還顛倒了數組本身的順序(即在這種情況下,arr1)。

reverse()方法返回對數組本身的引用(即,在這種情況下為arr1)。因此,arr2僅僅是對arr1的引用(而不是副本)。因此,當對arr2做任何事情時(即,當我們調用arr2.push(arr3);)時,arr1也會受到影響,因為arr1和arr2只是對同一個對象的引用。

這里有幾個觀點可以讓人們回答這個問題:

將數組傳遞給另一個數組的push()方法會將整個數組作為單個元素推入數組的末尾。結果,聲明arr2.push(arr3);將arr3作為一個整體添加到arr2的末尾(即,它不連接兩個數組,這就是concat()方法的用途)。

像Python一樣,JavaScript在調用像slice()這樣的數組方法時,會承認負面下標,以此作為在數組末尾引用元素的方式;例如,下標-1表示數組中的最后一個元素,依此類推。

15、下面的代碼將輸出到控制臺,為什么?

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);

以上代碼將輸出到控制臺:

"122"

"32"

"02"

"112"

"NaN2"

NaN

這是為什么...

這里的基本問題是JavaScript(ECMAScript)是一種松散類型的語言,它對值執行自動類型轉換以適應正在執行的操作。讓我們來看看這是如何與上面的每個例子進行比較。

示例1:1 +“2”+“2”輸出:“122”說明:第一個操作在1 +“2”中執行。由于其中一個操作數(“2”)是一個字符串,所以JavaScript假定需要執行字符串連接,因此將1的類型轉換為“1”,1 +“2”轉換為“12”。然后,“12”+“2”產生“122”。

示例2:1 + +“2”+“2”輸出:“32”說明:根據操作順序,要執行的第一個操作是+“2”(第一個“2”之前的額外+被視為一個一元運算符)。因此,JavaScript將“2”的類型轉換為數字,然后將一元+符號應用于它(即將其視為正數)。結果,下一個操作現在是1 + 2,當然這會產生3.但是,我們有一個數字和一個字符串之間的操作(即3和“2”),所以JavaScript再次轉換數值賦給一個字符串并執行字符串連接,產生“32”。

示例3:1 + - “1”+“2”輸出:“02”說明:這里的解釋與前面的示例相同,只是一元運算符是 - 而不是+。因此,“1”變為1,然后在應用 - 時將其變為-1,然后將其加1到產生0,然后轉換為字符串并與最終的“2”操作數連接,產生“02”。

示例4:+“1”+“1”+“2”輸出:“112”說明:盡管第一個“1”操作數是基于其前面的一元+運算符的數值類型轉換的,當它與第二個“1”操作數連接在一起時返回一個字符串,然后與最終的“2”操作數連接,產生字符串“112”。

示例5:“A” - “B”+“2”輸出:“NaN2”說明:由于 - 運算符不能應用于字符串,并且既不能將“A”也不能將“B”轉換為數值, “ - ”B“產生NaN,然后與字符串”2“串聯產生”NaN2“。

例6:“A” - “B”+2輸出:NaN說明:在前面的例子中,“A” - “B”產生NaN。但是任何運算符應用于NaN和其他數字操作數仍然會產生NaN。

16、如果數組列表太大,以下遞歸代碼將導致堆棧溢出。你如何解決這個問題,仍然保留遞歸模式?

varlist=readHugeList();

varnextListItem=function(){

varitem=list.pop();

if(item){

// process the list item...

nextListItem();

}

};

通過修改nextListItem函數可以避免潛在的堆棧溢出,如下所示:

varlist=readHugeList();

varnextListItem=function(){

varitem=list.pop();

if(item){

// process the list item...

setTimeout(nextListItem,0);

}

};

堆棧溢出被消除,因為事件循環處理遞歸,而不是調用堆棧。當nextListItem運行時,如果item不為null,則將超時函數(nextListItem)推送到事件隊列,并且函數退出,從而使調用堆棧清零。當事件隊列運行超時事件時,將處理下一個項目,并設置一個計時器以再次調用nextListItem。因此,該方法從頭到尾不經過直接遞歸調用即可處理,因此調用堆棧保持清晰,無論迭代次數如何。

17、什么是JavaScript中的“閉包”?舉一個例子。

閉包是一個內部函數,它可以訪問外部(封閉)函數的作用域鏈中的變量。閉包可以訪問三個范圍內的變量;具體來說: (1)變量在其自己的范圍內, (2)封閉函數范圍內的變量 (3)全局變量。

這里是一個例子:

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的范圍內。上面的代碼將產生以下輸出:

outerArg=123

innerArg=456

outerVar=a

innerVar=b

globalVar=xyz

18、以下代碼的輸出是什么:

for(vari=0;i<5;i++){

setTimeout(function(){console.log(i);},i*1000);

}

解釋你的答案。如何在這里使用閉包?

顯示的代碼示例不會顯示值0,1,2,3和4,這可能是預期的;而是顯示5,5,5,5。

這是因為循環內執行的每個函數將在整個循環完成后執行,因此所有函數都會引用存儲在i中的最后一個值,即5。

通過為每次迭代創建一個唯一的作用域,可以使用閉包來防止這個問題,并將該變量的每個唯一值存儲在其作用域中,如下所示:

for(vari=0;i<5;i++){

(function(x){

setTimeout(function(){console.log(x);},x*1000);

})(i);

}

這會產生將0,1,2,3和4記錄到控制臺的可能結果。

在ES2015上下文中,您可以在原始代碼中簡單地使用let而不是var:

for(leti=0;i<5;i++){

setTimeout(function(){console.log(i);},i*1000);

}

19、以下幾行代碼輸出到控制臺?

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中,都是||和&&是邏輯運算符,當從左向右計算時返回第一個完全確定的“邏輯值”。

或(||)運算符。在形式為X || Y的表達式中,首先計算X并將其解釋為布爾值。如果此布爾值為真,則返回true(1),并且不計算Y,因為“或”條件已經滿足。但是,如果此布爾值為“假”,我們仍然不知道X || Y是真還是假,直到我們評估Y,并將其解釋為布爾值。

因此,0 || 1評估為真(1),正如1 || 2。

和(&&)運算符。在X && Y形式的表達式中,首先評估X并將其解釋為布爾值。如果此布爾值為false,則返回false(0)并且不評估Y,因為“and”條件已失敗。但是,如果這個布爾值為“真”,我們仍然不知道X && Y是真還是假,直到我們評估Y,并將其解釋為布爾值。

然而,&&運算符的有趣之處在于,當表達式評估為“真”時,則返回表達式本身。這很好,因為它在邏輯表達式中被視為“真”,但也可以用于在您關心時返回該值。這解釋了為什么,有點令人驚訝的是,1 && 2返回2(而你可能會期望它返回true或1)。

20 、下面的代碼執行時輸出是什么?說明。

console.log(false=='0')

console.log(false==='0')

該代碼將輸出:

true

false

在JavaScript中,有兩套相等運算符。三重相等運算符===的行為與任何傳統的相等運算符相同:如果兩側的兩個表達式具有相同的類型和相同的值,則計算結果為true。然而,雙等號運算符在比較它們之前試圖強制這些值。因此,通常使用===而不是==。對于!== vs!=也是如此。

21、以下代碼的輸出是什么?解釋你的答案。

vara={},

b={key:'b'},

c={key:'c'};

a[b]=123;

a[c]=456;

console.log(a[b]);

此代碼的輸出將是456(不是123)。

原因如下:設置對象屬性時,JavaScript會隱式地將參數值串聯起來。在這種情況下,由于b和c都是對象,它們都將被轉換為“[object Object]”。因此,a [b]和a [c]都等價于[“[object Object]”],并且可以互換使用。因此,設置或引用[c]與設置或引用[b]完全相同。

22、以下代碼將輸出到控制臺中.

console.log((functionf(n){return((n>1)?n*f(n-1):n)})(10));

該代碼將輸出10階乘的值(即10!或3,628,800)。

原因如下:

命名函數f()以遞歸方式調用自身,直到它調用f(1),它簡單地返回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 、考慮下面的代碼片段。控制臺的輸出是什么,為什么?

(function(x){

return(function(y){

console.log(x);

})(2)

})(1);

輸出將為1,即使x的值從未在內部函數中設置。原因如下:

正如我們的JavaScript招聘指南中所解釋的,閉包是一個函數,以及創建閉包時在范圍內的所有變量或函數。在JavaScript中,閉包被實現為“內部函數”;即在另一功能的主體內定義的功能。閉包的一個重要特征是內部函數仍然可以訪問外部函數的變量。

因此,在這個例子中,因為x沒有在內部函數中定義,所以在外部函數的作用域中搜索一個定義的變量x,該變量的值為1。

24、以下代碼將輸出到控制臺以及為什么

varhero={

_name:'John Doe',

getSecretIdentity:function(){

returnthis._name;

}

};

varstoleSecretIdentity=hero.getSecretIdentity;

console.log(stoleSecretIdentity());

console.log(hero.getSecretIdentity());

這段代碼有什么問題,以及如何解決這個問題。

該代碼將輸出:

undefined

John Doe

第一個console.log打印未定義,因為我們從hero對象中提取方法,所以stoleSecretIdentity()在_name屬性不存在的全局上下文(即窗口對象)中被調用。

修復stoleSecretIdentity()函數的一種方法如下:

varstoleSecretIdentity=hero.getSecretIdentity.bind(hero);

25、創建一個函數,給定頁面上的DOM元素,將訪問元素本身及其所有后代(不僅僅是它的直接子元素)。對于每個訪問的元素,函數應該將該元素傳遞給提供的回調函數。

該函數的參數應該是:

一個 DOM 元素

一個回調函數(以DOM元素作為參數)

訪問樹中的所有元素(DOM)是經典的深度優先搜索算法應用程序。以下是一個示例解決方案:

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中測試您的這些知識:以下代碼的輸出是什么?

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作為函數方法的參數傳遞,函數fn的作用域(this)是窗口。 var length = 10;在窗口級別聲明。它也可以作為window.length或length或this.length來訪問(當這個===窗口時)。

方法綁定到Object obj,obj.method用參數fn和1調用。雖然方法只接受一個參數,但調用它時已經傳遞了兩個參數;第一個是函數回調,其他只是一個數字。

當在內部方法中調用fn()時,該函數在全局級別作為參數傳遞,this.length將有權訪問在Object obj中定義的var length = 10(全局聲明)而不是length = 5。

現在,我們知道我們可以使用arguments []數組訪問JavaScript函數中的任意數量的參數。

因此arguments0只不過是調用fn()。在fn里面,這個函數的作用域成為參數數組,并且記錄參數[]的長度將返回2。

因此輸出將如上所述。

28、考慮下面的代碼。輸出是什么,為什么?

(function(){

try{

thrownewError();

}catch(x){

varx=1,y=2;

console.log(x);

}

console.log(x);

console.log(y);

})();

1

undefined

2

var語句被掛起(沒有它們的值初始化)到它所屬的全局或函數作用域的頂部,即使它位于with或catch塊內。但是,錯誤的標識符只在catch塊內部可見。它相當于:

(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,結果是‘undefined’的

這是因為JavaScript初始化沒有被掛起。

(為什么它不顯示21的全局值?原因是當函數執行時,它檢查是否存在本地x變量但尚未聲明它,因此它不會查找全局變量。)

30、你如何克隆一個對象?

varobj={a:1,b:2}

varobjclone=Object.assign({},obj);

現在objclone的值是{a:1,b:2},但指向與obj不同的對象。

但請注意潛在的缺陷:Object.clone()只會執行淺拷貝,而不是深拷貝。這意味著嵌套的對象不會被復制。他們仍然引用與原始相同的嵌套對象:

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);

}

它會打印0 1 2 3 4,因為我們在這里使用let而不是var。變量i只能在for循環的塊范圍中看到。

32、以下幾行輸出什么,為什么?

console.log(1<2<3);

console.log(3>2>1);

第一條語句返回true,如預期的那樣。

第二個返回false是因為引擎如何針對<和>的操作符關聯性工作。它比較從左到右,所以3> 2> 1 JavaScript翻譯為true> 1. true具有值1,因此它比較1> 1,這是錯誤的。

33、如何在數組的開頭添加元素?最后如何添加一個?

varmyArray=['a','b','c','d'];

myArray.push('end');

myArray.unshift('start');

console.log(myArray);// ["start", "a", "b", "c", "d", "end"]

使用ES6,可以使用擴展運算符:

myArray=['start',...myArray];

myArray=[...myArray,'end'];

或者,簡而言之:

myArray=['start',...myArray,'end'];

34、想象一下你有這樣的代碼:

vara=[1,2,3];

a)這會導致崩潰嗎?

a[10]=99;

b)這個輸出是什么?

console.log(a[6]);

a)它不會崩潰。 JavaScript引擎將使陣列插槽3至9成為“空插槽”。

b)在這里,a [6]將輸出未定義的值,但時隙仍為空,而不是未定義的。在某些情況下,這可能是一個重要的細微差別。例如,使用map()時,map()的輸出中的空插槽將保持為空,但未定義的插槽將使用傳遞給它的函數重映射:

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的值是什么?

該表達式將被評估為true,因為NULL將被視為任何其他未定義的變量。

注意:JavaScript區分大小寫,我們在這里使用NULL而不是null。

36、代碼返回后會怎么樣?

console.log(typeoftypeof1);

打印結果:string

typeof 1將返回“number”,typeof“number”將返回字符串。

37、以下代碼輸出什么?為什么?

varb=1;

functionouter(){

varb=2

functioninner(){

b++;

varb=3;

console.log(b)

}

inner();

}

outer();

輸出到控制臺將是“3”。

在這個例子中有三個閉包,每個都有它自己的var b聲明。當調用變量時,將按照從本地到全局的順序檢查閉包,直到找到實例。由于內部閉包有自己的b變量,這就是輸出。

此外,由于提升內部的代碼將被解釋如下:

functioninner(){

varb;// b is undefined

b++;// b is NaN

b=3;// b is 3

console.log(b);// output "3"

}

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,763評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,238評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,823評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,604評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,339評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,713評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,712評論 3 445
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,893評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,448評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,201評論 3 357
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,397評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,944評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,631評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,033評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,321評論 1 293
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,128評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,347評論 2 377

推薦閱讀更多精彩內容

  • 第2章 基本語法 2.1 概述 基本句法和變量 語句 JavaScript程序的執行單位為行(line),也就是一...
    悟名先生閱讀 4,189評論 0 13
  • 原文: https://github.com/ecomfe/spec/blob/master/javascript...
    zock閱讀 3,391評論 2 36
  • 使用 typeof bar === “object” 判斷 bar 是不是一個對象有神馬潛在的弊端?如何避免這種弊...
    Roselle_c閱讀 351評論 0 0
  • 前端07班 王語句JavaScript程序的執行單位為行(line),也就是一行一行地執行。一般情況下,每一行就是...
    ea203453e188閱讀 903評論 0 4
  • 語句 JavaScript程序的執行單位為行(line),也就是一行一行地執行。一般情況下,每一行就是一個語句。 ...
    米塔塔閱讀 464評論 1 10