1、使用 typeof bar === 'object' 來確定 bar 是否是對象的潛在陷阱是什么?如何避免這個陷阱?盡管 typeof bar === 'object' 是檢查 bar 是否對象的可靠方法,令人驚訝的是在JavaScript中 null 也被認為是對象! 因此,令大多數(shù)開發(fā)人員驚訝的是,下面的代碼將輸出 true (而不是false) 到控制臺: var bar = null; console.log(typeof bar === 'object'); // logs true! 只要清楚這一點,同時檢查 bar 是否為 null,就可以很容易地避免問題: console.log((bar !== null) && (typeof bar === 'object')); // logs false 要答全問題,還有其他兩件事情值得注意: 首先,上述解決方案將返回 false,當 bar 是一個函數(shù)的時候。在大多數(shù)情況下,這是期望行為,但當你也想對函數(shù)返回 true 的話,你可以修改上面的解決方案為: console.log((bar !== null) && ((typeof bar === 'object') || (typeof bar === 'function'))); 第二,上述解決方案將返回 true,當 bar 是一個數(shù)組(例如,當 var bar = [];)的時候。在大多數(shù)情況下,這是期望行為,因為數(shù)組是真正的對象,但當你也想對數(shù)組返回 false 時,你可以修改上面的解決方案為: console.log((bar !== null) && (typeof bar === 'object') && (toString.call(bar) !== '[object Array]')); 或者,如果你使用jQuery的話: console.log((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 都定義在函數(shù)的封閉范圍內(nèi),并且都始于 var關(guān)鍵字,大多數(shù)JavaScript開發(fā)人員期望 typeof a 和 typeof b 在上面的例子中都是undefined。 然而,事實并非如此。這里的問題是,大多數(shù)開發(fā)人員將語句 var a = b = 3; 錯誤地理解為是以下聲明的簡寫: var b = 3; 但事實上,var a = b = 3; 實際是以下聲明的簡寫: b = 3; var a = b; 因此(如果你不使用嚴格模式的話),該代碼段的輸出是: a defined? false 但是, b 如何才能被定義在封閉函數(shù)的范圍之外呢?是的,既然語句 var a = b = 3; 是語句 b = 3; 和 var a = b;的簡寫, b 最終成為了一個全局變量(因為它沒有前綴 var 關(guān)鍵字),因此仍然在范圍內(nèi)甚至封閉函數(shù)之外。 需要注意的是,在嚴格模式下(即使用 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(); 上面的代碼將輸出以下內(nèi)容到控制臺: outer func: this.foo = bar outer func: self.foo = bar inner func: this.foo = undefined 在外部函數(shù)中, this 和self 兩者都指向了 myObject,因此兩者都可以正確地引用和訪問 foo。 在內(nèi)部函數(shù)中, this 不再指向 myObject。其結(jié)果是,this.foo 沒有在內(nèi)部函數(shù)中被定義,相反,指向到本地的變量self 保持在范圍內(nèi),并且可以訪問。 (在ECMA 5之前,在內(nèi)部函數(shù)中的this 將指向全局的 window 對象;反之,因為作為ECMA 5,內(nèi)部函數(shù)中的功能this 是未定義的。) 4.封裝JavaScript源文件的全部內(nèi)容到一個函數(shù)塊有什么意義及理由?這是一個越來越普遍的做法,被許多流行的JavaScript庫(jQuery,Node.js等)采用。這種技術(shù)創(chuàng)建了一個圍繞文件全部內(nèi)容的閉包,也許是最重要的是,創(chuàng)建了一個私有的命名空間,從而有助于避免不同JavaScript模塊和庫之間潛在的名稱沖突。 這種技術(shù)的另一個特點是,允許一個易于引用的(假設更短的)別名用于全局變量。這通常用于,例如,jQuery插件中。jQuery允許你使用jQuery.noConflict(),來禁用 $ 引用到jQuery命名空間。在完成這項工作之后,你的代碼仍然可以使用$ 利用這種閉包技術(shù),如下所示: (function($) { /* jQuery plugin code referencing $ */ } )(jQuery); 5.在JavaScript源文件的開頭包含 use strict 有什么意義和好處?對于這個問題,既簡要又最重要的答案是,use strict 是一種在JavaScript代碼運行時自動實行更嚴格解析和錯誤處理的方法。那些被忽略或默默失敗了的代碼錯誤,會產(chǎn)生錯誤或拋出異常。通常而言,這是一個很好的做法。 嚴格模式的一些主要優(yōu)點包括:
6.考慮以下兩個函數(shù)。它們會返回相同的東西嗎? 為什么相同或為什么不相同?function foo1() { return { bar: 'hello' }; } function foo2() { return { bar: 'hello' }; } 出人意料的是,這兩個函數(shù)返回的內(nèi)容并不相同。更確切地說是: console.log('foo1 returns:'); console.log(foo1()); console.log('foo2 returns:'); console.log(foo2()); 將產(chǎn)生: foo1 returns: 這不僅是令人驚訝,而且特別讓人困惑的是, foo2()返回undefined卻沒有任何錯誤拋出。 原因與這樣一個事實有關(guān),即分號在JavaScript中是一個可選項(盡管省略它們通常是非常糟糕的形式)。其結(jié)果就是,當碰到 foo2()中包含 return語句的代碼行(代碼行上沒有其他任何代碼),分號會立即自動插入到返回語句之后。 也不會拋出錯誤,因為代碼的其余部分是完全有效的,即使它沒有得到調(diào)用或做任何事情(相當于它就是是一個未使用的代碼塊,定義了等同于字符串 'hello'的屬性 bar)。 這種行為也支持放置左括號于JavaScript代碼行的末尾,而不是新代碼行開頭的約定。正如這里所示,這不僅僅只是JavaScript中的一個風格偏好。 7. NaN 是什么?它的類型是什么?你如何可靠地測試一個值是否等于 NaN ?NaN 屬性代表一個“不是數(shù)字”的值。這個特殊的值是因為運算不能執(zhí)行而導致的,不能執(zhí)行的原因要么是因為其中的運算對象之一非數(shù)字(例如, 'abc' / 4),要么是因為運算的結(jié)果非數(shù)字(例如,除數(shù)為零)。 雖然這看上去很簡單,但 NaN 有一些令人驚訝的特點,如果你不知道它們的話,可能會導致令人頭痛的bug。 首先,雖然 NaN 意味著“不是數(shù)字”,但是它的類型,不管你信不信,是 Number: console.log(typeof NaN === 'number'); // logs 'true' 此外, NaN 和任何東西比較——甚至是它自己本身!——結(jié)果是false: console.log(NaN === NaN); // logs 'false' 一種半可靠的方法來測試一個數(shù)字是否等于 NaN,是使用內(nèi)置函數(shù) isNaN(),但即使使用 isNaN() 依然并非是一個完美的解決方案。 一個更好的解決辦法是使用 value !== value,如果值等于NaN,只會產(chǎn)生true。另外,ES6提供了一個新的 Number.isNaN() 函數(shù),這是一個不同的函數(shù),并且比老的全局 isNaN() 函數(shù)更可靠。 8.下列代碼將輸出什么?并解釋原因。console.log(0.1 + 0.2); console.log(0.1 + 0.2 == 0.3); 一個稍微有點編程基礎(chǔ)的回答是:“你不能確定??赡軙敵觥?.3”和“true”,也可能不會。JavaScript中的數(shù)字和浮點精度的處理相同,因此,可能不會總是產(chǎn)生預期的結(jié)果?!?/span> 以上所提供的例子就是一個演示了這個問題的典型例子。但出人意料的是,它會輸出: 0.30000000000000004 9.討論寫函數(shù) isInteger(x) 的可能方法,用于確定x是否是整數(shù)。這可能聽起來是小菜一碟,但事實上,這很瑣碎,因為ECMAScript 6引入了一個新的正以此為目的 Number.isInteger() 函數(shù)。然而,之前的ECMAScript 6,會更復雜一點,因為沒有提供類似的 Number.isInteger() 方法。 問題是,在ECMAScript規(guī)格說明中,整數(shù)只概念上存在:即,數(shù)字值總是存儲為浮點值。 考慮到這一點,最簡單又最干凈的ECMAScript6之前的解決方法(同時也非常穩(wěn)健地返回 false ,即使一個非數(shù)字的值,如字符串或 null ,被傳遞給函數(shù))如下: function isInteger(x) { return (x^0) === x; } 下面的解決方法也是可行的,雖然不如上面那個方法優(yōu)雅: function isInteger(x) { return Math.round(x) === x; } 請注意 Math.ceil() 和 Math.floor() 在上面的實現(xiàn)中等同于 Math.round()。 或: function isInteger(x) { return (typeof x === 'number') && (x % 1 === 0); 相當普遍的一個不正確的解決方案是: function isInteger(x) { return parseInt(x, 10) === x; } 雖然這個以 parseInt函數(shù)為基礎(chǔ)的方法在 x 取許多值時都能工作良好,但一旦 x 取值相當大的時候,就會無法正常工作。問題在于 parseInt() 在解析數(shù)字之前強制其第一個參數(shù)到字符串。因此,一旦數(shù)目變得足夠大,它的字符串就會表達為指數(shù)形式(例如, 1e+21)。因此,parseInt() 函數(shù)就會去解析 1e+21,但當?shù)竭_ e字符串的時候,就會停止解析,因此只會返回值 1。注意: > String(1000000000000000000000) '1e+21' 10.下列代碼行1-4如何排序,使之能夠在執(zhí)行代碼時輸出到控制臺? 為什么?(function() { console.log(1); setTimeout(function(){console.log(2)}, 1000); setTimeout(function(){console.log(3)}, 0); console.log(4); })(); 序號如下: 1 讓我們先來解釋比較明顯而易見的那部分:
好的。但是,既然 3 是0毫秒延遲之后輸出的,那么是否意味著它是立即輸出的呢?如果是的話,那么它是不是應該在 4 之前輸出,既然 4 是在第二行輸出的? 要回答這個問題,你需要正確理解JavaScript的事件和時間設置。 瀏覽器有一個事件循環(huán),會檢查事件隊列和處理未完成的事件。例如,如果時間發(fā)生在后臺(例如,腳本的 onload 事件)時,瀏覽器正忙(例如,處理一個 onclick),那么事件會添加到隊列中。當onclick處理程序完成后,檢查隊列,然后處理該事件(例如,執(zhí)行 onload 腳本)。 同樣的, setTimeout() 也會把其引用的函數(shù)的執(zhí)行放到事件隊列中,如果瀏覽器正忙的話。 當setTimeout()的第二個參數(shù)為0的時候,它的意思是“盡快”執(zhí)行指定的函數(shù)。具體而言,函數(shù)的執(zhí)行會放置在事件隊列的下一個計時器開始。但是請注意,這不是立即執(zhí)行:函數(shù)不會被執(zhí)行除非下一個計時器開始。這就是為什么在上述的例子中,調(diào)用 console.log(4) 發(fā)生在調(diào)用 console.log(3) 之前(因為調(diào)用 console.log(3) 是通過setTimeout被調(diào)用的,因此會稍微延遲)。 11.寫一個簡單的函數(shù)(少于80個字符),要求返回一個布爾值指明字符串是否為回文結(jié)構(gòu)。下面這個函數(shù)在 str 是回文結(jié)構(gòu)的時候返回true,否則,返回false。 function isPalindrome(str) { str = str.replace(/W/g, '').toLowerCase(); return (str == str.split('').reverse().join('')); } 例如: console.log(isPalindrome('level')); // logs 'true' 12.寫一個 sum方法,在使用下面任一語法調(diào)用時,都可以正常工作。 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中,函數(shù)可以提供到 arguments 對象的訪問,arguments 對象提供傳遞到函數(shù)的實際參數(shù)的訪問。這使我們能夠使用 length 屬性來確定在運行時傳遞給函數(shù)的參數(shù)數(shù)量。 如果傳遞兩個參數(shù),那么只需加在一起,并返回。 否則,我們假設它被以 sum(2)(3)這樣的形式調(diào)用,所以我們返回一個匿名函數(shù),這個匿名函數(shù)合并了傳遞到 sum()的參數(shù)和傳遞給匿名函數(shù)的參數(shù)。 方法2 function sum(x, y) { if (y !== undefined) { return x + y; } else { return function(y) { return x + y; }; } } 當調(diào)用一個函數(shù)的時候,JavaScript不要求參數(shù)的數(shù)目匹配函數(shù)定義中的參數(shù)數(shù)量。如果傳遞的參數(shù)數(shù)量大于函數(shù)定義中參數(shù)數(shù)量,那么多余參數(shù)將簡單地被忽略。另一方面,如果傳遞的參數(shù)數(shù)量小于函數(shù)定義中的參數(shù)數(shù)量,那么缺少的參數(shù)在函數(shù)中被引用時將會給一個 undefined值。所以,在上面的例子中,簡單地檢查第2個參數(shù)是否未定義,就可以相應地確定函數(shù)被調(diào)用以及進行的方式。 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)提供一個或多個備用的可按預期工作的實現(xiàn)方案。 (a)無論用戶點擊什么按鈕,數(shù)字5將總會輸出到控制臺。這是因為,當 onclick 方法被調(diào)用(對于任何按鈕)的時候, for 循環(huán)已經(jīng)結(jié)束,變量 i 已經(jīng)獲得了5的值。(面試者如果能夠談一談有關(guān)如何執(zhí)行上下文,可變對象,激活對象和內(nèi)部“范圍”屬性貢有助于閉包行為,則可以加分)。 (b)要讓代碼工作的關(guān)鍵是,通過傳遞到一個新創(chuàng)建的函數(shù)對象,在每次傳遞通過 for 循環(huán)時,捕捉到 i 值。下面是三種可能實現(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ù)組對象的本地 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); }); 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)); 輸出結(jié)果是: 'array 1: length=5 last=j,o,n,e,s' arr1 和 arr2 在上述代碼執(zhí)行之后,兩者相同了,原因是:
這里有幾個側(cè)面點有時候會讓你在回答這個問題時,陰溝里翻船: 傳遞數(shù)組到另一個數(shù)組的 push() 方法會讓整個數(shù)組作為單個元素映射到數(shù)組的末端。其結(jié)果是,語句 arr2.push(arr3); 在其整體中添加 arr3 作為一個單一的元素到 arr2 的末端(也就是說,它并沒有連接兩個數(shù)組,連接數(shù)組是 concat() 方法的目的)。 和Python一樣,JavaScript標榜數(shù)組方法調(diào)用中的負數(shù)下標,例如 slice() 可作為引用數(shù)組末尾元素的方法:例如,-1下標表示數(shù)組中的最后一個元素,等等。 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); 上面的代碼將輸出以下內(nèi)容到控制臺: '122' 原因是… 這里的根本問題是,JavaScript(ECMAScript)是一種弱類型語言,它可對值進行自動類型轉(zhuǎn)換,以適應正在執(zhí)行的操作。讓我們通過上面的例子來說明這是如何做到的。 例1:1 + '2' + '2' 輸出:'122' 說明: 1 + '2' 是執(zhí)行的第一個操作。由于其中一個運算對象('2')是字符串,JavaScript會假設它需要執(zhí)行字符串連接,因此,會將 1 的類型轉(zhuǎn)換為 '1', 1 + '2'結(jié)果就是 '12'。然后, '12' + '2' 就是 '122'。 例2: 1 + +'2' + '2' 輸出: '32' 說明:根據(jù)運算的順序,要執(zhí)行的第一個運算是 +'2'(第一個 '2' 前面的額外 + 被視為一元運算符)。因此,JavaScript將 '2' 的類型轉(zhuǎn)換為數(shù)字,然后應用一元 + 號(即,將其視為一個正數(shù))。其結(jié)果是,接下來的運算就是 1 + 2 ,這當然是 3。然后我們需要在一個數(shù)字和一個字符串之間進行運算(即, 3 和 '2'),同樣的,JavaScript會將數(shù)值類型轉(zhuǎn)換為字符串,并執(zhí)行字符串的連接,產(chǎn)生 '32'。 例3: 1 + -'1' + '2' 輸出: '02' 說明:這里的解釋和前一個例子相同,除了此處的一元運算符是 - 而不是 +。先是 '1' 變?yōu)?nbsp;1,然后當應用 - 時又變?yōu)榱?nbsp;-1 ,然后將其與 1相加,結(jié)果為 0,再將其轉(zhuǎn)換為字符串,連接最后的 '2' 運算對象,得到 '02'。 例4: +'1' + '1' + '2' 輸出: '112' 說明:雖然第一個運算對象 '1'因為前綴的一元 + 運算符類型轉(zhuǎn)換為數(shù)值,但又立即轉(zhuǎn)換回字符串,當連接到第二個運算對象 '1' 的時候,然后又和最后的運算對象'2' 連接,產(chǎn)生了字符串 '112'。 例5: 'A' - 'B' + '2' 輸出: 'NaN2' 說明:由于運算符 - 不能被應用于字符串,并且 'A' 和 'B' 都不能轉(zhuǎn)換成數(shù)值,因此,'A' - 'B'的結(jié)果是 NaN,然后再和字符串 '2' 連接,得到 'NaN2' 。 例6: 'A' - 'B' + 2 輸出: NaN 說明:參見前一個例子, 'A' - 'B' 結(jié)果為 NaN。但是,應用任何運算符到NaN與其他任何的數(shù)字運算對象,結(jié)果仍然是 NaN。 16.下面的遞歸代碼在數(shù)組列表偏大的情況下會導致堆棧溢出。在保留遞歸模式的基礎(chǔ)上,你怎么解決這個問題?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án)操縱了遞歸,而不是調(diào)用堆棧。 當 nextListItem 運行時,如果 item不為空,timeout函數(shù)(nextListItem)就會被推到事件隊列,該函數(shù)退出,因此就清空調(diào)用堆棧。當事件隊列運行其timeout事件,且進行到下一個 item 時,定時器被設置為再次調(diào)用 nextListItem。因此,該方法從頭到尾都沒有直接的遞歸調(diào)用,所以無論迭代次數(shù)的多少,調(diào)用堆棧保持清空的狀態(tài)。 17.JavaScript中的“閉包”是什么?請舉一個例子。閉包是一個可以訪問外部(封閉)函數(shù)作用域鏈中的變量的內(nèi)部函數(shù)。閉包可以訪問三種范圍中的變量:這三個范圍具體為:(1)自己范圍內(nèi)的變量,(2)封閉函數(shù)范圍內(nèi)的變量,以及(3)全局變量。 下面是一個簡單的例子: 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 = 123 innerArg = 456 outerVar = a innerVar = b globalVar = xyz 18.下面的代碼將輸出什么:解釋你的答案。閉包在這里能起什么作用? 上面的代碼不會按預期顯示值0,1,2,3,和4,而是會顯示5,5,5,5,和5。 原因是,在循環(huán)中執(zhí)行的每個函數(shù)將整個循環(huán)完成之后被執(zhí)行,因此,將會引用存儲在 i中的最后一個值,那就是5。 閉包可以通過為每次迭代創(chuàng)建一個唯一的范圍,存儲范圍內(nèi)變量的每個唯一的值,來防止這個問題,如下: 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 在JavaScript中, || 和 &&都是邏輯運算符,用于在從左至右計算時,返回第一個可完全確定的“邏輯值”。 或( || )運算符。在形如 X||Y的表達式中,首先計算X 并將其解釋執(zhí)行為一個布爾值。如果這個布爾值true,那么返回true(1),不再計算 Y,因為“或”的條件已經(jīng)滿足。如果這個布爾值為false,那么我們?nèi)匀徊荒苤?nbsp;X||Y是真是假,直到我們計算 Y,并且也把它解釋執(zhí)行為一個布爾值。 因此, 0 || 1 的計算結(jié)果為true(1),同理計算1 || 2。 與( &&)運算符。在形如 X&&Y的表達式中,首先計算 X并將其解釋執(zhí)行為一個布爾值。如果這個布爾值為 false,那么返回 false(0),不再計算 Y,因為“與”的條件已經(jīng)失敗。如果這個布爾值為true,但是,我們?nèi)匀徊恢?nbsp;X&&Y 是真是假,直到我們?nèi)ビ嬎?nbsp;Y,并且也把它解釋執(zhí)行為一個布爾值。 不過,關(guān)于 &&運算符有趣的地方在于,當一個表達式計算為“true”的時候,那么就返回表達式本身。這很好,雖然它在邏輯表達式方面計算為“真”,但如果你希望的話也可用于返回該值。這就解釋了為什么,有些令人奇怪的是, 1 && 2返回 2(而不是你以為的可能返回 true 或 1)。 20.執(zhí)行下面的代碼時將輸出什么?請解釋。console.log(false == '0') 代碼將輸出: true 在JavaScript中,有兩種等式運算符。三個等于運算符 === 的作用類似傳統(tǒng)的等于運算符:如果兩側(cè)的表達式有著相同的類型和相同的值,那么計算結(jié)果為true。而雙等于運算符,會只強制比較它們的值。因此,總體上而言,使用 ===而不是 ==的做法更好。 !==vs !=亦是同理。 21.以下代碼將輸出什么?并解釋你的答案。var a={}, b={key:'b'}, c={key:'c'}; a[b]=123; a[c]=456; console.log(a[b]); 這段代碼將輸出 456(而不是 123)。 原因為:當設置對象屬性時,JavaScript會暗中字符串化參數(shù)值。在這種情況下,由于 b 和 c都是對象,因此它們都將被轉(zhuǎn)換為'[object Object]'。結(jié)果就是, 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(1): returns n, which is 1 命名函數(shù) f()遞歸地調(diào)用本身,當調(diào)用 f(1)的時候,只簡單地返回1。下面就是它的調(diào)用過程: 23.請看下面的代碼段??刂婆_將輸出什么,為什么? (function(x) { return (function(y) { console.log(x); })(2) })(1); 控制臺將輸出 1,即使從來沒有在函數(shù)內(nèi)部設置過x的值。原因是: 正如我們在JavaScript招聘指南中解釋過的那樣,閉包是一個函數(shù),連同在閉包創(chuàng)建的時候,其范圍內(nèi)的所有變量或函數(shù)一起。在JavaScript中,閉包是作為一個“內(nèi)部函數(shù)”實施的:即,另一個函數(shù)主體內(nèi)定義的函數(shù)。閉包的一個重要特征是,內(nèi)部函數(shù)仍然有權(quán)訪問外部函數(shù)的變量。 因此,在本例中,由于 x未在函數(shù)內(nèi)部中定義,因此在外部函數(shù)范圍中搜索定義的變量 x,且被發(fā)現(xiàn)具有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對象提取方法,所以調(diào)用了全局上下文中(即窗口對象)的 stoleSecretIdentity(),而在此全局上下文中, _name屬性不存在。 其中一種修復stoleSecretIdentity() 函數(shù)的方法如下: var stoleSecretIdentity = hero.getSecretIdentity.bind(hero); 25.創(chuàng)建一個給定頁面上的一個DOM元素,就會去訪問元素本身及其所有子元素(不只是它的直接子元素)的函數(shù)。對于每個被訪問的元素,函數(shù)應該傳遞元素到提供的回調(diào)函數(shù)。此函數(shù)的參數(shù)為: DOM元素 回調(diào)函數(shù)(將DOM元素作為其參數(shù)) 訪問樹(DOM)的所有元素是經(jīng)典的深度優(yōu)先搜索算法應用。下面是一個示范的解決方案: 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 } }
翻譯作者:碼農(nóng)網(wǎng) – 小峰 |
|