一、肯定變量 bar 是不是對象?面試
(bar !== null) && (typeof bar === "object") && (toString.call(bar) !== "[object Array]");
或者,使用jQuery:算法
(bar !== null) && (typeof bar === "object") && (! $.isArray(bar));編程
2.下面的代碼將輸出什麼到控制檯,爲何?
(function(){
var a = b = 3;
})();數組
console.log("a defined? " + (typeof a !== 'undefined'));
console.log("b defined? " + (typeof b !== 'undefined'));
因爲 a 和 b 都定義在函數的封閉範圍內,而且都始於 var關鍵字,大多數JavaScript開發人員指望 typeof a 和 typeof b 在上面的例子中都是undefined。瀏覽器
然而,事實並不是如此。這裏的問題是,大多數開發人員將語句 var a = b = 3; 錯誤地理解爲是如下聲明的簡寫:安全
var b = 3;
var a = b;
但事實上,var a = b = 3; 實際是如下聲明的簡寫:閉包
b = 3;
var a = b;
所以(若是你不使用嚴格模式的話),該代碼段的輸出是:app
a defined? false
b defined? true
可是, b 如何才能被定義在封閉函數的範圍以外呢?是的,既然語句 var a = b = 3; 是語句 b = 3; 和 var a = b;的簡寫, b 最終成爲了一個全局變量(由於它沒有前綴 var 關鍵字),所以仍然在範圍內甚至封閉函數以外。函數
須要注意的是,在嚴格模式下(即便用 use strict),語句var a = b = 3; 將生成ReferenceError: b is not defined的運行時錯誤,從而避免任何不然可能會致使的headfakes /bug。 (仍是你爲何應該理所固然地在代碼中使用 use strict 的最好例子!)測試
3.下面的代碼將輸出什麼到控制檯,爲何?
var myObject = {
foo: "bar",
func: function() {
var self = 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。
在內部函數中, this 再也不指向 myObject。其結果是,this.foo 沒有在內部函數中被定義,相反,指向到本地的變量self 保持在範圍內,而且能夠訪問。 (在ECMA 5以前,在內部函數中的this 將指向全局的 window 對象;反之,由於做爲ECMA 5,內部函數中的功能this 是未定義的。)
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中最多見的錯誤之一。在嚴格模式下,這樣作的話會拋出錯誤。
消除 this 強制。若是沒有嚴格模式,引用null或未定義的值到 this 值會自動強制到全局變量。這可能會致使許多使人頭痛的問題和讓人巴不得拔本身頭髮的bug。在嚴格模式下,引用 null或未定義的 this 值會拋出錯誤。
不容許重複的屬性名稱或參數值。當檢測到對象(例如,var object = {foo: "bar", foo: "baz"};)中重複命名的屬性,或檢測到函數中(例如,function foo(val1, val2, val1){})重複命名的參數時,嚴格模式會拋出錯誤,所以捕捉幾乎能夠確定是代碼中的bug能夠避免浪費大量的跟蹤時間。
使eval() 更安全。在嚴格模式和非嚴格模式下,eval() 的行爲方式有所不一樣。最顯而易見的是,在嚴格模式下,變量和聲明在 eval() 語句內部的函數不會在包含範圍內建立(它們會在非嚴格模式下的包含範圍中被建立,這也是一個常見的問題源)。
在 delete使用無效時拋出錯誤。delete操做符(用於從對象中刪除屬性)不能用在對象不可配置的屬性上。當試圖刪除一個不可配置的屬性時,非嚴格代碼將默默地失敗,而嚴格模式將在這樣的狀況下拋出異常。
6.考慮如下兩個函數。它們會返回相同的東西嗎? 爲何相同或爲何不相同?
function foo1()
{
return {
bar: "hello"
};
}
function foo2()
{
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()返回undefined卻沒有任何錯誤拋出。
緣由與這樣一個事實有關,即分號在JavaScript中是一個可選項(儘管省略它們一般是很是糟糕的形式)。其結果就是,當碰到 foo2()中包含 return語句的代碼行(代碼行上沒有其餘任何代碼),分號會當即自動插入到返回語句以後。
也不會拋出錯誤,由於代碼的其他部分是徹底有效的,即便它沒有獲得調用或作任何事情(至關於它就是是一個未使用的代碼塊,定義了等同於字符串 "hello"的屬性 bar)。
這種行爲也支持放置左括號於JavaScript代碼行的末尾,而不是新代碼行開頭的約定。正如這裏所示,這不只僅只是JavaScript中的一個風格偏好。
7. NaN 是什麼?它的類型是什麼?你如何可靠地測試一個值是否等於 NaN ?
NaN 屬性表明一個「不是數字」的值。這個特殊的值是由於運算不能執行而致使的,不能執行的緣由要麼是由於其中的運算對象之一非數字(例如, "abc" / 4),要麼是由於運算的結果非數字(例如,除數爲零)。
雖然這看上去很簡單,但 NaN 有一些使人驚訝的特色,若是你不知道它們的話,可能會致使使人頭痛的bug。
首先,雖然 NaN 意味着「不是數字」,可是它的類型,無論你信不信,是 Number:
console.log(typeof NaN === "number"); // logs "true"
此外, NaN 和任何東西比較——甚至是它本身自己!——結果是false:
console.log(NaN === NaN); // logs "false"
一種半可靠的方法來測試一個數字是否等於 NaN,是使用內置函數 isNaN(),但即便使用 isNaN() 依然並不是是一個完美的解決方案。
一個更好的解決辦法是使用 value !== 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
9.討論寫函數 isInteger(x) 的可能方法,用於肯定x是不是整數。
這可能聽起來是小菜一碟,但事實上,這很瑣碎,由於ECMAScript 6引入了一個新的正以此爲目的 Number.isInteger() 函數。然而,以前的ECMAScript 6,會更復雜一點,由於沒有提供相似的 Number.isInteger() 方法。
問題是,在ECMAScript規格說明中,整數只概念上存在:即,數字值老是存儲爲浮點值。
考慮到這一點,最簡單又最乾淨的ECMAScript6以前的解決方法(同時也很是穩健地返回 false ,即便一個非數字的值,如字符串或 null ,被傳遞給函數)以下:
function isInteger(x) { return (x^0) === x; }
^ :按位異或運算符
參與運算的兩個值,若是兩個相應位相同,則結果爲0,不然爲1。
下面的解決方法也是可行的,雖然不如上面那個方法優雅:
function isInteger(x) { return Math.round(x) === x; }
請注意 Math.ceil() 和 Math.floor() 在上面的實現中等同於 Math.round()。
或:
function isInteger(x) { return (typeof x === 'number') && (x % 1 === 0);
至關廣泛的一個不正確的解決方案是:
function isInteger(x) { return parseInt(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
10.下列代碼行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() 而沒有任何延遲輸出的
2 之因此放在 3的後面,是由於 2 是延遲了1000毫秒(即,1秒)以後輸出的,而 3 是延遲了0毫秒以後輸出的。
好的。可是,既然 3 是0毫秒延遲以後輸出的,那麼是否意味着它是當即輸出的呢?若是是的話,那麼它是否是應該在 4 以前輸出,既然 4 是在第二行輸出的?
要回答這個問題,你須要正確理解JavaScript的事件和時間設置。
瀏覽器有一個事件循環,會檢查事件隊列和處理未完成的事件。例如,若是時間發生在後臺(例如,腳本的 onload 事件)時,瀏覽器正忙(例如,處理一個 onclick),那麼事件會添加到隊列中。當onclick處理程序完成後,檢查隊列,而後處理該事件(例如,執行 onload 腳本)。
一樣的, setTimeout() 也會把其引用的函數的執行放到事件隊列中,若是瀏覽器正忙的話。
當setTimeout()的第二個參數爲0的時候,它的意思是「儘快」執行指定的函數。具體而言,函數的執行會放置在事件隊列的下一個計時器開始。可是請注意,這不是當即執行:函數不會被執行除非下一個計時器開始。這就是爲何在上述的例子中,調用 console.log(4) 發生在調用 console.log(3) 以前(由於調用 console.log(3) 是經過setTimeout被調用的,所以會稍微延遲)。
11.寫一個簡單的函數(少於80個字符),要求返回一個布爾值指明字符串是否爲迴文結構。
下面這個函數在 str 是迴文結構的時候返回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'
12.寫一個 sum方法,在使用下面任一語法調用時,均可以正常工做。
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; };
}
}
在JavaScript中,函數能夠提供到 arguments 對象的訪問,arguments 對象提供傳遞到函數的實際參數的訪問。這使咱們可以使用 length 屬性來肯定在運行時傳遞給函數的參數數量。
若是傳遞兩個參數,那麼只需加在一塊兒,並返回。
不然,咱們假設它被以 sum(2)(3)這樣的形式調用,因此咱們返回一個匿名函數,這個匿名函數合併了傳遞到 sum()的參數和傳遞給匿名函數的參數。
方法2
function sum(x, y) {
if (y !== undefined) {
return x + y;
} else {
return function(y) { return x + y; };
}
}
當調用一個函數的時候,JavaScript不要求參數的數目匹配函數定義中的參數數量。若是傳遞的參數數量大於函數定義中參數數量,那麼多餘參數將簡單地被忽略。另外一方面,若是傳遞的參數數量小於函數定義中的參數數量,那麼缺乏的參數在函數中被引用時將會給一個 undefined值。因此,在上面的例子中,簡單地檢查第2個參數是否未定義,就能夠相應地肯定函數被調用以及進行的方式。
13.請看下面的代碼片斷:
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)當用戶點擊「Button 4」的時候會輸出什麼到控制檯,爲何?(b)提供一個或多個備用的可按預期工做的實現方案。
(a)不管用戶點擊什麼按鈕,數字5將總會輸出到控制檯。這是由於,當 onclick 方法被調用(對於任何按鈕)的時候, for 循環已經結束,變量 i 已經得到了5的值。(面試者若是可以談一談有關如何執行上下文,可變對象,激活對象和內部「範圍」屬性貢有助於閉包行爲,則能夠加分)。
(b)要讓代碼工做的關鍵是,經過傳遞到一個新建立的函數對象,在每次傳遞經過 for 循環時,捕捉到 i 值。下面是三種可能實現的方法:
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);
}
或者,你能夠封裝所有調用到在新匿名函數中的 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);
}
也能夠調用數組對象的本地 forEach 方法來替代 for 循環:
['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);
});
14.下面的代碼將輸出什麼到控制檯,爲何?
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));
輸出結果是:
"array 1: length=5 last=j,o,n,e,s"
"array 2: length=5 last=j,o,n,e,s"
arr1 和 arr2 在上述代碼執行以後,二者相同了,緣由是:
調用數組對象的 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" 都不能轉換成數值,所以,"A" - "B"的結果是 NaN,而後再和字符串 "2" 鏈接,獲得 "NaN2" 。
例6: "A" - "B" + 2 輸出: NaN 說明:參見前一個例子, "A" - "B" 結果爲 NaN。可是,應用任何運算符到NaN與其餘任何的數字運算對象,結果仍然是 NaN。
16.下面的遞歸代碼在數組列表偏大的狀況下會致使堆棧溢出。在保留遞歸模式的基礎上,你怎麼解決這個問題?
var list = readHugeList();
var nextListItem = function() {
var item = list.pop();
if (item) {
// process the list item...
nextListItem();
}
};
潛在的堆棧溢出能夠經過修改nextListItem 函數避免:
var list = readHugeList();
var nextListItem = function() {
var item = list.pop();
if (item) {
// process the list item...
setTimeout( nextListItem, 0);
}
};
堆棧溢出之因此會被消除,是由於事件循環操縱了遞歸,而不是調用堆棧。當 nextListItem 運行時,若是 item不爲空,timeout函數(nextListItem)就會被推到事件隊列,該函數退出,所以就清空調用堆棧。當事件隊列運行其timeout事件,且進行到下一個 item 時,定時器被設置爲再次調用 nextListItem。所以,該方法從頭至尾都沒有直接的遞歸調用,因此不管迭代次數的多少,調用堆棧保持清空的狀態。
17.JavaScript中的「閉包」是什麼?請舉一個例子。
閉包是一個能夠訪問外部(封閉)函數做用域鏈中的變量的內部函數。閉包能夠訪問三種範圍中的變量:這三個範圍具體爲:(1)本身範圍內的變量,(2)封閉函數範圍內的變量,以及(3)全局變量。
下面是一個簡單的例子:
var globalVar = "xyz";
(function outerFunc(outerArg) {
var outerVar = 'a';
(function innerFunc(innerArg) {
var innerVar = '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 (var i = 0; i < 5; i++) {
setTimeout(function() { console.log(i); }, i * 1000 );
}
解釋你的答案。閉包在這裏能起什麼做用?
上面的代碼不會按預期顯示值0,1,2,3,和4,而是會顯示5,5,5,5,和5。
緣由是,在循環中執行的每一個函數將整個循環完成以後被執行,所以,將會引用存儲在 i中的最後一個值,那就是5。
閉包能夠經過爲每次迭代建立一個惟一的範圍,存儲範圍內變量的每一個惟一的值,來防止這個問題,以下:
for (var i = 0; i < 5; i++) {
(function(x) {
setTimeout(function() { console.log(x); }, x * 1000 );
})(i);
}
這就會按預期輸出0,1,2,3,和4到控制檯。
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,那麼返回true(1),再也不計算 Y,由於「或」的條件已經知足。若是這個布爾值爲false,那麼咱們仍然不能知道 X||Y是真是假,直到咱們計算 Y,而且也把它解釋執行爲一個布爾值。
所以, 0 || 1 的計算結果爲true(1),同理計算1 || 2。
與( &&)運算符。在形如 X&&Y的表達式中,首先計算 X並將其解釋執行爲一個布爾值。若是這個布爾值爲 false,那麼返回 false(0),再也不計算 Y,由於「與」的條件已經失敗。若是這個布爾值爲true,可是,咱們仍然不知道 X&&Y 是真是假,直到咱們去計算 Y,而且也把它解釋執行爲一個布爾值。
不過,關於 &&運算符有趣的地方在於,當一個表達式計算爲「true」的時候,那麼就返回表達式自己。這很好,雖然它在邏輯表達式方面計算爲「真」,但若是你但願的話也可用於返回該值。這就解釋了爲何,有些使人奇怪的是, 1 && 2返回 2(而不是你覺得的可能返回 true 或 1)。
20.執行下面的代碼時將輸出什麼?請解釋。
console.log(false == '0')
console.log(false === '0')
代碼將輸出:
true
false
在JavaScript中,有兩種等式運算符。三個等於運算符 === 的做用相似傳統的等於運算符:若是兩側的表達式有着相同的類型和相同的值,那麼計算結果爲true。而雙等於運算符,會只強制比較它們的值。所以,整體上而言,使用 ===而不是 ==的作法更好。 !==vs !=亦是同理。
21.如下代碼將輸出什麼?並解釋你的答案。
var a={},
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]均至關於a["[object Object]"] ,並能夠互換使用。所以,設置或引用 a[c]和設置或引用 a[b]徹底相同。
22.如下代碼行將輸出什麼到控制檯?
console.log((function f(n){return ((n > 1) ? n * f(n-1) : n)})(10));
並解釋你的答案。
代碼將輸出10!的值(即10!或3628800)。
緣由是:
命名函數 f()遞歸地調用自己,當調用 f(1)的時候,只簡單地返回1。下面就是它的調用過程:
f(1): returns n, which is 1
f(2): returns 2 * f(1), which is 2
f(3): returns 3 * f(2), which is 6
f(4): returns 4 * f(3), which is 24
f(5): returns 5 * f(4), which is 120
f(6): returns 6 * f(5), which is 720
f(7): returns 7 * f(6), which is 5040
f(8): returns 8 * f(7), which is 40320
f(9): returns 9 * f(8), which is 362880
f(10): returns 10 * f(9), which is 3628800
23.請看下面的代碼段。控制檯將輸出什麼,爲何?
(function(x) {
return (function(y) {
console.log(x);
})(2)
})(1);
控制檯將輸出 1,即便歷來沒有在函數內部設置過x的值。緣由是:
正如咱們在JavaScript招聘指南中解釋過的那樣,閉包是一個函數,連同在閉包建立的時候,其範圍內的全部變量或函數一塊兒。在JavaScript中,閉包是做爲一個「內部函數」實施的:即,另外一個函數主體內定義的函數。閉包的一個重要特徵是,內部函數仍然有權訪問外部函數的變量。
所以,在本例中,因爲 x未在函數內部中定義,所以在外部函數範圍中搜索定義的變量 x,且被發現具備1的值。
24.下面的代碼將輸出什麼到控制檯,爲何?
var hero = {
_name: 'John Doe',
getSecretIdentity: function (){
return this._name;
}
};
var stoleSecretIdentity = hero.getSecretIdentity;
console.log(stoleSecretIdentity());
console.log(hero.getSecretIdentity());
代碼有什麼問題,以及應該如何修復。
代碼將輸出:
undefined
John Doe
第一個 console.log之因此輸出 undefined,是由於咱們正在從 hero對象提取方法,因此調用了全局上下文中(即窗口對象)的 stoleSecretIdentity(),而在此全局上下文中, _name屬性不存在。
其中一種修復stoleSecretIdentity() 函數的方法以下:
var stoleSecretIdentity = hero.getSecretIdentity.bind(hero);
25.建立一個給定頁面上的一個DOM元素,就會去訪問元素自己及其全部子元素(不僅是它的直接子元素)的函數。對於每一個被訪問的元素,函數應該傳遞元素到提供的回調函數。
此函數的參數爲:
DOM元素
回調函數(將DOM元素做爲其參數)
訪問樹(DOM)的全部元素是經典的深度優先搜索算法應用。下面是一個示範的解決方案:
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 }}