這種模式對前端童鞋要求不是特別高,會設計,切圖,就差不多了,什麼前端組件化,模塊化,自動化這些概念都沒有,可能是小公司沒有接觸到,大公司應該很早就開始實踐了。那個時候常用的就是yahoo的yui,和jquery ui還有jquery一系列插件了等等
What is a potential pitfall with using typeof bar === "object" to determine if bar is an object? How can this pitfall be avoided?
老生常談的問題,用 typeof 是否能準確判斷一個對象變量,答案是否定的,null 的結果也是 object,Array 的結果也是 object,有時候我們需要的是 「純粹」 的 object 對象。
如何規避這個問題?
var obj = {};
// 1
console.log((obj !== null) & (typeof obj === "object") && (toString.call(obj) !== "[object Array]"));
// 2
console.log(Object.prototype.toString.call(obj) === "[object Object]");
二
What will the code below output to the console and why?
(function(){
var a = b = 3;
})();
console.log("a defined? " + (typeof a !== 'undefined'));
console.log("b defined? " + (typeof b !== 'undefined'));
這題不難,IIFE 中的賦值過程其實是(賦值過程從右到左):
(function(){
b = 3;
var a = b;
})();
接下去就不難了,a 是局部變量,b 是全局變量。
三
What will the code below output to the console and why?
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();
前面兩個輸出沒有問題,都是 bar,問題出在後面兩個。用了 IIFE 後,匿名函數內的 this 其實已經指到了 window,所以第三個輸出 this.foo 其實是 window.foo,而全局對象並沒有 foo 這個 key,所以輸出 undefined,而第四個輸出,因為 self 引用了 myObject,所以還是 bar。
四
What is the significance of, and reason for, wrapping the entire content of a JavaScript source file in a function block?
為什麼要用 IIFE?
簡單來說就是為了能模塊化,創建私有變量等等,很多類庫(比如 jQuery)都用了這樣的寫法。
可以參考我以前翻譯的一篇文章 詳解javascript立即執行函數表達式(IIFE)
五
What is the significance, and what are the benefits, of including 『use strict』 at the beginning of a JavaScript source file?
嚴格模式下進行 Javascript 開發有啥好處?
這個就不展開來了,可以參考阮一峰老師的 Javascript 嚴格模式詳解 或者自行谷歌百度。
六
Consider the two functions below. Will they both return the same thing? Why or why not?
function foo1()
{
return {
bar: "hello"
};
}
function foo2()
{
return
{
bar: "hello"
};
}
執行以上兩個函數,會返回相同的東西嗎?
不會,第二個函數會返回 undefined。這是由於 Javascript 的封號插入機制決定的,如果某行代碼,return 關鍵詞後沒有任何東西了,將會自動插入一個封號,顯然 foo2 函數中,當 return 後被插入一個封號後,儘管後面的語句不符合規定,但是因為沒有執行到,所以也不會報錯了。沒有 return 任何東西的函數,默認返回 undefined。
所以很多 Javascript 規範建議把 { 寫在一行中,而不是另起一行。
七
What is NaN? What is its type? How can you reliably test if a value is equal to NaN?
NaN 是什麼鬼?typeof 的結果是?如果一個變量的值是 NaN,怎麼確定?
NaN 是 『not a number』 的縮寫,表示 「不是一個數字」,通常會在運算過程中產生:
console.log('abc' / 4);
console.log(4 * 'a');
雖然它 「不是一個數字」,但是 NaN 的 typeof 結果卻是 number:
console.log(typeof (4 * 'a')); // number
NaN 和任何變量都不相等,包括 NaN 自己:
console.log(NaN === NaN); // false
判斷一個變量是不是 NaN 可以用 isNaN() 函數,但是這 並不是一個完美的函數,有些時候用 value !== value 似乎更準確,幸運的是,ES6 已經有 Number.isNaN() 方法,將比 isNaN() 準確的多。
八
What will the code below output? Explain your answer.
console.log(0.1 + 0.2);
console.log(0.1 + 0.2 == 0.3);
上面代碼的輸出結果是什麼?
這個問題正好我之前研究過,有興趣的可以參考下 【0.1 + 0.2 = 0.30000000000000004】該怎樣理解?,看懂了還有興趣的可以看下這篇 玉伯的一道課後題題解(關於 IEEE 754 雙精度浮點型精度損失)
九
Discuss possible ways to write a function isInteger(x) that determines if x is an integer.
寫一個方法 isInterger(x),可以用來判斷一個變量是否是整數。
ES6 中自帶了 Number.isInteger() 方法。但是目前 ES5 中沒有自帶的方法,可以把一個數去掉小數點後和原數進行比較,判斷是否相等,那麼問題就演變成如何對一個數進行取整了。
var a = -1.2223;
console.log(a ^ 0); // -1
console.log(a | 0); // -1
console.log(a > 0); // -1
console.log(Math.round(a)); // -1
console.log(Math.floor(a)); // -2
console.log(Math.ceil(a)); // -1
十
In what order will the numbers 1-4 be logged to the console when the code below is executed? Why?
(function() {
console.log(1);
setTimeout(function(){console.log(2)}, 1000);
setTimeout(function(){console.log(3)}, 0);
console.log(4);
})();
以上代碼的輸出結果是?
這題不難,只要知道 Javascript 是單線程的語言, 一些異步事件是在主體 js 執行完之後執行即可,所以主體的 1、4 先輸出,而後是 3、2,沒有問題,因為 3 的定時設置比 2 早。
具體可以參考我之前的文章 從setTimeout談JavaScript運行機制
十一
Write a simple function (less than 80 characters) that returns a boolean indicating whether or not a string is a palindrome.
判斷一個字符串是不是回文。
function isPalindrome(str) {
str = str.replace(/W/g, '').toLowerCase();
return (str == str.split('').reverse().join(''));
}
這裡想到一個進階題,求字符串最長回文子串,可以參考 求最長回文子串 – leetcode 5. Longest Palindromic Substring
十二
Write a sum method which will work properly when invoked using either syntax below.
console.log(sum(2,3)); // Outputs 5
console.log(sum(2)(3)); // Outputs 5
寫一個 sum 方法,使得以上代碼得到預期結果。這題可以參考我以前的文章 湯姆大叔的6道javascript編程題題解 中的最後一題,理論上此題更簡單,因為它沒要求能擴展(比如 sum(2)(3)(4)),甚至可以這樣:
function sum(x) {
if (arguments.length == 2) {
return arguments[0] + arguments[1];
} else {
return function(y) { return x + y; };
}
}
或者這樣:
function sum(x, y) {
if (y !== undefined) {
return x + y;
} else {
return function(y) { return x + y; };
}
}
十三
Consider the following code snippet:
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) What gets logged to the console when the user clicks on 「Button 4」 and why?
(b) Provide one or more alternate implementations that will work as expected.
點擊 『Button 4′ 後輸出什麼?如何使得輸出能跟預期相同?
答案是輸出 5,事實上點擊任意的 button,輸出都是 5。因為循環結束後,i 值變成了 5。如何改,使得輸出分別是 0, 1, 2, 3, 4?用閉包在內存中保存變量,可以參考我之前的文章 這10道javascript筆試題你都會麼 中的第 8 題。
十四
What will the code below output to the console and why?
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));
上面代碼輸出是?
這道題我答錯了,忽略了 reverse() 方法的一個要重性質,reverse() 方法執行的結果並不是創建一個副本,而是在原數組上直接操作,並返回該數組的引用。
知道了這一點,該題也就迎刃而解了。arr2 其實和 arr1 引用了同一個對象,所以在 arr2 上的操作也會同時反映到 arr1 上。
十五
What will the code below output to the console and why ?
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);
以上代碼輸出什麼?
+」2″ 能將字符串 「2″ 轉換成整數 2,-」2″ 同理,而兩個變量進行 「+」 運算時,如果都是數字和字符串,則分別進行數字相加和字符串拼接,如果一個是數字一個是字符串,則將數字轉為字符串,如果是 「-」 運算呢?則將字符串轉為數字。
「A」 – 「B」 會返回 NaN,因為 「A」 和 「B」 無法轉成數字進行運算,這裡不要以為 「A」 和 「B」 能轉為 ASCII碼 進行運算(不要和 C 語言搞混了)。而 NaN 和字符串相加,會轉成 「NaN」 和字符串去拼接,NaN 和任何數字相加結果還是 NaN。
十六
The following recursive code will cause a stack overflow if the array list is too large. How can you fix this and still retain the recursive pattern?
var list = readHugeList();
var nextListItem = function() {
var item = list.pop();
if (item) {
// process the list item...
nextListItem();
}
};
以上代碼可能會由於遞歸調用導致棧溢出,如何規避這個問題?
首先,任何遞歸都可以用迭代來代替,所以改寫成迭代方式肯定沒有問題。
而原文給的解答令人深思:
var list = readHugeList();
var nextListItem = function() {
var item = list.pop();
if (item) {
// process the list item...
setTimeout( nextListItem, 0);
}
};
利用 setTimeout 的異步性質,完美地去除了這個調用棧。
如果你還是摸不著頭腦,簡單舉個慄子:
var list = [0, 1];
var nextListItem = function() {
var item = list.pop();
if (item) {
nextListItem();
}
console.log(item);
};
nextListItem();
上面的代碼會依次輸出 0 和 1,因為程序中形成了一個調用棧,1 被壓到了棧底,最後出棧。
把程序改成這樣:
var list = [0, 1];
var nextListItem = function() {
var item = list.pop();
if (item) {
// process the list item...
setTimeout( nextListItem, 0);
}
console.log(item);
};
nextListItem();
這回就是 1 和 0 了,因為 setTimeout 的回調只有當主體的 js 執行完後才會去執行,所以先輸出了 1,自然也就沒有棧這一說法了。
事實上,並不是所有遞歸都能這樣改寫,如果下一次遞歸調用依賴於前一次遞歸調用返回的值,就不能這麼改了。
十七
What is a 「closure」 in JavaScript? Provide an example.
談談閉包。
以前也寫過幾篇文章,可以參考下 閉包初窺 以及 閉包拾遺 & 垃圾回收機制。
十八
What will be the output of the following code:
for (var i = 0; i < 5; i++) {
setTimeout(function() { console.log(i); }, i * 1000 );
}
以上代碼輸出什麼?如何能輸出期望值?
很顯然,輸出都是 5。這題和第十三題有些類似,用立即執行函數+閉包即可。
for (var i = 0; i < 5; i++) {
!function(i) {
setTimeout(function() { console.log(i); }, i * 1000 );
}(i)
}
還有種優雅的解法,使用 bind:
for(var i = 0; i < 5; i++) {
setTimeout(console.log.bind(console, i), i * 1000);
}
十九
What would the following lines of code output to the console?
console.log("0 || 1 = "+(0 || 1));
console.log("1 || 2 = "+(1 || 2));
console.log("0 & 1 = "+(0 && 1));
console.log("1 & 2 = "+(1 && 2));
以上代碼輸出什麼?
|| 和 && 是短路運算符。先說說 ||,如果前面變量值為 false(包括 0、null、undefined、flase、空字符串等等),則返回後面變量值,否則返回前面變量值。&& 恰恰相反,如果前面變量為 false,則返回前面變量值,否則返回後面變量值。
注意不要和位運算操作符 | 以及 & 搞混淆了。
二十
What will be the output when the following code is executed? Explain.
console.log(false == '0')
console.log(false === '0')
== 和 === 的區別, 後者是全等,只有兩個值完全相同(或者兩個對象引用相同)時,才會返回 true,而前者在比較時會進行隱式的轉換。
二十一
What is the output out of the following code? Explain your answer.
var a={},
b={key:'b'},
c={key:'c'};
a[b]=123;
a[c]=456;
console.log(a[b]);
一道有趣的題目,答案是 456。
我們知道,Javascript 中對象的 key 值,一定會是一個 string 值,如果不是,則會隱式地進行轉換。當執行到 a[b]=123] 時,b 並不是一個 string 值,將 b 執行 toString() 方法轉換(得到 「[object Object]「),a[c] 也是相同道理。所以代碼其實可以看做這樣執行:
var a={},
b={key:'b'},
c={key:'c'};
// a[b]=123;
a["[object Object]"]=123;
// a[c]=456;
a["[object Object]"]=456;
console.log(a["[object Object]"]);
這樣就一目了然了。
二十二
What will the following code output to the console:
console.log((function f(n){return ((n > 1) ? n * f(n-1) : n)})(10))
輸出什麼?
其實可以寫成這樣,清楚些:
var ans = (function f(n){
return ((n > 1) ? n * f(n-1) : n)
})(10);
console.log(ans);
其實就是一個立即執行函數+遞歸,求個階乘而已(10!)。給立即執行函數加了個名字 f,方便在遞歸裡調用,其實完全可以用 arguments.callee 代替:
var ans = (function(n){
return ((n > 1) ? n * arguments.callee(n-1) : n)
})(10);
console.log(ans);
二十三
Consider the code snippet below. What will the console output be and why?
(function(x) {
return (function(y) {
console.log(x);
})(2)
})(1);
輸出什麼?
顯然是 1,閉包,能引用函數外的變量。
改成這樣呢?
(function(y) {
return (function(y) {
console.log(y);
})(2)
})(1);
二十四
What will the following code output to the console and why:
var hero = {
_name: 'John Doe',
getSecretIdentity: function (){
return this._name;
}
};
var stoleSecretIdentity = hero.getSecretIdentity;
console.log(stoleSecretIdentity());
console.log(hero.getSecretIdentity());
What is the issue with this code and how can it be fixed.
執行第一次輸出時,this 指向了 window,如何規避這個問題?用 bind 綁定 this 指向,具體可以參考 ECMAScript 5(ES5)中bind方法簡介備忘,注意低版本 IE 的兼容。
var stoleSecretIdentity = hero.getSecretIdentity.bind(hero);
二十五
Create a function that, given a DOM Element on the page, will visit the element itself and all of its descendents (not just its immediate children). For each element visited, the function should pass that element to a provided callback function.
The arguments to the function should be:
遍歷 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
}
}
這道題可以拓展,先序遍歷 DOM樹,中序遍歷,甚至後序遍歷的結果是?具體可以參考前文 二叉樹三種遍歷的遞歸和迭代解法,都是樹,原理是一樣一樣的。
想認識志同道合的朋友一起學習web
加入我們的學習QQ群 190166743
豐富的學習資源,周一到周四免費直播公開課