前端題目整理

考試可以是功利的,比如考雅思是爲了出國留學,考廚師證可以當廚師,考教師證可以做老師。我們前18年的人生中似乎都在考試,爲了18歲的高考。高考是很多家庭跨越社會階層的最好機會,這次考試意義重大,重的讓人扭曲。扭曲後的東西在外力消失的情況下一定會反彈。卸下高考的重擔後,有人似乎失去生命的意義,直到可以考什麼試,他們會把考證當作自己的興趣愛好;有人會狂歡,從此鄙視一切考試,做一個抨擊考試教育的憤青;有人會忽視考試,得考且考,做一個佛系青年。

馬克思主義哲學觀告訴我們:一切東西都要分兩面看,有好的一面,也有壞的一面,可能還有中立的一面。在賦予考試太多其他意義後,或許我們早已忘了考試最純粹的目的:查漏補缺。考試是非常好的即時反饋系統,是完善知識體系,追蹤思維謬誤的非常好的方法。

不管是爲了面試還是面試別人,不管是爲了工作還是技術本身,都可以通過考試這一簡單的即時反饋系統梳理知識的脈絡,弄清一些似是而非的概念,完善知識體系。

下面整理了一些在前端技術中極易犯錯的知識點。

js部分

題目來源:

一、數據類型

1. 整數的安全範圍?

var END = Math.pow(2, 53);
var START = END - 100;
var count = 0;
for (var i = START; i <= END; i++) {
    count++;
}
console.log(count);

0 100 101 other

解答

按正常的思考,count 爲 101 無疑。然而這可是 JavaScript Puzzlers! 中的題目,do you really know JavaScript?

這個題目考察對 Number 存儲方式的掌握。在《Javascript權威指南 第六版》3.1小節中有如下描述:

Javascript 中所有數字均用浮點數值表示。Javascript 採用 IEEE 754 標準定義的64位浮點數格式表示數字。

按照 Javascript 中的數字格式,能夠表示的整數範圍是從 253 ~ 253 ,包含邊界值。如果使用了超過此範圍的整數,則無法保證低位數字的精度。然而需要注意的是,Javascript 中實際的操作(比如數組索引,以及第4章講到的位操作符)則是基於32位整數。

在瀏覽器控制檯中測試如下:

Number.MAX_SAFE_INTEGER //  9007199254740991
Number.MIN_SAFE_INTEGER // -9007199254740991
Math.pow(2, 53)         // 9007199254740992
Math.pow(2, 53) + 1     // 9007199254740992
Math.pow(2, 53) + 2     // 9007199254740994

末尾兩行的結果出乎意料,倒數第二行導致本題出現死循環,答案是 other。可見在 MAX_SAFE_INTEGER 之外的計算確實不安全。

如果將 for 循環中的增量改爲 i += 2,結果是多少呢?

沒想到這又是對的,51。所以這個題目不用 i += 2 ,哈。

那麼爲什麼是253 呢?

引用 Js的整型你懂了嗎

使用 52 位表示一個數的整數部分,那麼最大可以精確表示的數應該是2521 纔對, 就像 64 位表示整數時那樣: 2631 (去掉 1 位符號位)。 但其實浮點數在保存數字的時候做了規格化處理,以 10 進製爲例:

20*10^2 => 2*10^3 //小數點前只需要保留 1 位數

對於二進制來說, 小數點前保留一位, 規格化後始終是 1.***, 節省了 1 bit,這個 1 並不需要保存。

2. 自動轉爲String

var lowerCaseOnly =  /^[a-z]+$/;
[lowerCaseOnly.test(null), lowerCaseOnly.test()] // [true, true]

the argument is converted to a string with the abstract ToString operation, so it is "null" and "undefined".

3. 不可 delete 特性

var a = 1;
delete a;
typeof a; // "number"

typeof a 結果是?

a = 1;
delete a;
typeof a; // "undefined"

typeof a 結果又是什麼?

eval("var a = 1")
delete a;
typeof a; // "undefined"

這次 typeof a 結果呢?

解答

上述 3 題只有第一行的賦值語句不同。本題考察對可刪除特性 的理解。

未聲明的賦值在一個全局對象上創建一個可刪除的屬性

中間一題是未聲明的賦值,所以是可刪除的。

在Eval中創建的變量或方法比較特別,沒有DontDelete特性,也就是說可以刪除

故第 3 題也是可以刪除的。

二、函數

1. map與parseInt?

["1", "2", "3"].map(parseInt)

["1", "2", "3"] [1, 2, 3] [0, 1, 2] other

解答

該題考察了對 mapparseInt 函數的掌握情況。

["1", "2", "3"].map(function () {
    console.log(arguments)
})

在控制檯中看看輸出了什麼?

原來 map 傳遞了3個參數給裏面的函數,分別爲 item, index, array。也就是說 parseInt 實際上會收到3個參數,以數組中的 “2” 爲例,parseInt 實際調用爲 parseInt("2", 1, ["1", "2", "3"])

parseInt 的調用方式爲 parseInt (val, radix)。 將 2 用 1 進製表示,結果是什麼呢? 我們得到 NaN, 故結果爲 [“1”, NaN, NaN],答案是 other

2. replace 與 parseInt

"1 2 3".replace(/\d/g, parseInt)

解答:

"1 2 3".replace(/\d/g, () => console.log(arguments))

看看 replace 往函數裏裝了什麼?

["1", 0, "1 2 3"]
["2", 2, "1 2 3"]
["3", 4, "1 2 3"]

依次爲 elem, position, array

故,結果爲 “1 NaN 3”

3. 計算parseInt表達式結果

parseInt(3, 8)
parseInt(3, 2)
parseInt(3, 0)

3, 3, 3 3, 3, NaN 3, NaN, NaN other

解答

本題繼續考察對 parseInt 的掌握。

parseInt(3, 2), 在2進制中不存在3,故爲 NaN。

parseInt(3, 0),當第二個參數爲 0 時,parseInt 會忽略該參數,使用默認的 10 進制。故結果爲 3, NaN, 3, 答案爲 other

4. map 函數

var ary = Array(3);
ary[0] = 2
ary.map(elem => 1);

解答

出題官怎麼會考這麼簡單的題,難道是看你知不知道有 map 這個函數?too young, too simple。

實際上是看你知不知道 map 只計算初始化了的元素。答案是:["1", empty × 2]

5. arguments

function sidEffecting(ary) {
  ary[0] = ary[2];
}
function bar(a, b, c) {
  c = 10
  sidEffecting(arguments);
  return a + b + c;
}
bar(1, 1, 1)

3 12 error other

解答

難道出題官是看你知不知道局部變量這個概念?

一開始看到答案時,我是拒絕接受的。

The result is 21, in javascript variables are tied to the arguments object so changing the variables changes arguments and changing arguments changes the local variables even when they are not in the same scope.

只能用不可思議來解釋了。我們在 bar 函數中添加兩個打印來證實一下:

function bar(a, b, c) {     
  c = 10                  
  console.log(a, b, c)     // 1 1 10
  sidEffecting(arguments);
  console.log(a, b, c)     // 10 1 10
  return a + b + c;       
}                         

不知道這算不算 js 的糟粕部分,怎麼還有這種操作?我拒絕在實際中使用這個 trick。

6. 浮點數與toString

3.toString()
3..toString()
3...toString()

"3", error, error "3", "3.0", erorr, error, "3", error other

解答

error, "3", error

3.x is a valid syntax to define “3” with a mantissa of x, toString is not a valid number, but the empty string is.

7. 函數的名稱

function foo () { }
var oldName = foo.name;
foo.name = "bar";
[oldName, foo.name]

error ["", ""] ["foo", "foo"] ["foo", "bar"]

解答“:

["foo", "foo"]

name is a read only property. Why it doesn’t throw an error when assigned, I do not know.

8. join的結果

[,,,].join(", ") // ", , "

JavaScript allows a trailing comma when defining arrays, so that turns out to be an array of three undefined.

9. Function.length

var a = Function.length,      // 1
    b = new Function().length // 0
a === b // false

It’s false. Function.length is defined to be 1. On the other hand, the length property of the Function prototype object is defined to be 0.

10. 日期

var a = Date(0);     // 函數調用,返回當前日期字符串
var b = new Date(0);
var c = new Date();
[a === b, b === c, a === c] // [false, false, false]

When Date is invoked as a constructor it returns an object relative to the epoch (Jan 01 1970). When the argument is missing it returns the current date. When it is invoked as a function, it returns a String representation of the current time.

11. Math.min 與 Math.max

var min = Math.min(), max = Math.max()
min < max  // false

解答:

Math.min returns +Infinity when supplied an empty argument list. Likewise, Math.max returns -Infinity.

12. call 與 apply 的區別

function fn (a, b, c) {
  console.log(a, b, c)
} 

fn.call(null, 'hello', 'world')
fn.apply(null, ['hello', 'world'])

區別就是:apply 第二個參數把 fn 的所有參數都打包了。

當需要傳給 fn 的參數在 args 數組中時:

fn.apply(null, args)

也可以使用 es6 的語法:

fn(...args)

13. 函數防抖

讓某個函數在執行後必須等待一定事件才能再次執行,且在等待時間內再次觸發函數時,等待時間會重新計算。

14. 匿名函數

var f = function g () {
    return 23;
};
typeof g();

"number" "undefined" "function" "Error"

解答:

答題原則之一:如果一道題看起來像是在問你 1 + 1 等於多少,那麼這很可能是一個坑。

本題中,g 只在函數體內部可訪問。故答案爲 "Error"

15. setTimeout 和 setInterval

下面這兩段代碼有什麼不同?

setTimeout(function () {
    /* 代碼塊 */
    setTimeout(arguments.callee, 1000)
}, 1000)
setInterval(function () {
    /* 代碼塊 */
}, 1000)

解答

從功能上來說,上面兩段代碼都是每隔1000ms執行一次代碼塊。不過它們也有不同之處,千萬別說名字不一樣什麼的,尬。

  • 在 strict 模式下,caller, calleearguments 不可用。所以如果用了 use strict,就不能用第一種。

  • setTimeout 每次在代碼塊執行完後設置定時器,代碼塊之間的實際間隔一定每次都大於 1000ms。對於 setInterval,有如下知識點:

    解釋器每隔time時間,就會嘗試把func插入線程隊列,但是會檢查上一次的插入有沒有被執行:如果上一次的插入還沒有被執行,則跳過此次插入;如果上一次的插入已被執行完成或者正在執行當中,則將func插入,而不跳過。

三、作用域

1. 表達式結果?

var name = 'World!';
(function () {
    if (typeof name === 'undefined') {
        var name = 'Jack';
        console.log('Goodbye ' + name);
    } else {
        console.log('Hello ' + name);
    }
})();

Boodbye Jack Hello Jack Hello undefined Hello World

解答

if 表達式中,var name = 'Jack' 屏蔽了全局的定義。

然而,當計算 typeof name === 'undefined' 時,name 的值是 undefined,所以答案是 Goodbye Jack

本題考察對變量提升的理解,在 js 中,變量的定義會提升到作用域的開頭,而賦值的位置保持不變。對於本題來說,if 表達式實際上相當於:

var name
if (typeof name === 'undefined') {
    name = 'Jack'
    console.log('Goodbye ' + name)
}

2. 它怎麼是個 global?

(function(){
  var x = y = 1;
})();
console.log(y);
console.log(x);

1, 1 error, error 1, error other

解答

肯定不是 error, error, 顯而易見的東西一般都是錯的。

var x = y = 1 這種操作和其他語言不一樣,需要特別小心,並且不建議使用。改寫一下:

(function(){
  var x;
  y = 1;
  x = y;
})();
console.log(y);
console.log(x);

y is an automatic global, not a function local one.

3. 函數中聲明參數

function foo(a) {
    var a;
    return a;
}
function bar(a) {
    var a = 'bye';
    return a;
}
[foo('hello'), bar('hello')]

["hello", "hello"] ["hello", "bye"] ["bye", "bye"] other

解答

Variabled declarations are hoisted, but in this case since the variable exists already in the scope, they are removed altogether. In bar() the variable declaration is removed but the assignment remains, so it has effect.

我承認我選了 other, 以爲是 [undefined, "bye"]

根據上面的闡述,本題相當於:

function foo(a) {
    return a;
}
function bar(a) {
    a = 'bye';
    return a;
}
[foo('hello'), bar('hello')]

故選 ["hello", "bye"]

4. 變量提升

var y = 1,
x = y = typeof x;
x;

1 "number" undefined "undefined"

解答

考察變量提升:

var y
x
y = typeof x
x = y

答案爲: undefinedtypeof x 的結果纔是 "undefined"

5. 多個同名函數疑惑

(function f () {
    function f () {
        return 1;
    }
    return f();
    function f () {
        return 2;
    }
})();

A、1 B、2 C、Error (e.g. "Too much recursion") D、undefined

解答:

先來看個類似的:

(function f () {
    var f = function () {
        return 1;
    }
    return f();
    var f = function () {
        return 2;
    }
})();
// 答案是 1

本題考察的是變量提升。

函數的定義會提升到作用域開頭:

(function f () {
    function f () {
        return 1;
    }
    function f () {
        return 2;
    }
    return f(); 
})();

結果爲 2。

6. with

with (function(x, undefined){}) length;

A、1 B、2 C、undefined D、Error

解答

with語句的作用是將代碼的作用域設置到一個特定的作用域中

本題相當於:

function fn (x, undefined) { }
fn.length

function.length 返回函數的參數個數,故爲 2。

7. 匿名函數作用域

var x = 1;
if (function f () {}) {
   x += typeof f;
}
x;

1 "1function" "1undefined" NaN

解答

f 作爲 if 的參數,在 if 作用域中不可訪問,故答案爲: 1undefined

四、運算符

1. switch 與 ===

function showCase(value) {
    switch(value) {
    case 'A':
        console.log('Case A');
        break;
    case 'B':
        console.log('Case B');
        break;
    case undefined:
        console.log('undefined');
        break;
    default:
        console.log('Do not know!');
    }
}
showCase(new String('A'));

Case A Case B Do not konw undefined

解答

'A' == new String('A'),結果爲 true。但按照出題官的套路,是不可能選 Case A 的。不知道怎麼選,就選C,居然對了。

實際上,這裏考察的是對 switch 的掌握程度。switch,天天用,以爲沒問題,沒想到裏面也有坑。這個坑就是 js 中的 =====,站在坑外面看看:

undefined === undefined // true
undefined == null       // true
undefined === null      // false

switch uses === internally and new String(x) !== x

2. String函數

function showCase2(value) {
    switch(value) {
    case 'A':
        console.log('Case A');
        break;
    case 'B':
        console.log('Case B');
        break;
    case undefined:
        console.log('undefined');
        break;
    default:
        console.log('Do not know!');
    }
}
showCase2(String('A'));

解答

咦,又是這道題,選 Do not konw ,你中計了,卒。

出題官的套路就像俄羅斯套娃,一層又一層。

與上題不同,這裏 String(‘A’) 返回的不是 object,而是 ‘A’,故答案爲 Case A

3. 負數求模

function isOdd(num) {
    return num % 2 == 1;
}
function isEven(num) {
    return num % 2 == 0;
}
function isSane(num) {
    return isEven(num) || isOdd(num);
}
var values = [7, 4, '13', -9, Infinity];
values.map(isSane);

解答

前3個爲 true 沒有問題。

-9 % 2 // -1
Infinity % 2 // NaN

故結果爲[true, true, true, false, false]

4. 表達式==的結果

[] == []

解答

這裏用的是 == 而不是 ===,所以結果爲 true 嗎? 又中計了。

這裏表面上考察的是 ==,實際考察的是值類型和引用類型。上式中,==左右是兩個完全不同的對象,所以答案是 false 。

5. 比較兩個相同的正則表達式

var a = /123/,
    b = /123/;
a == b
a === b

true, true true, false false, false other

解答

a, b 是兩個正則表達式,均爲對象,且爲不同的對象。答案爲: false, false

6. 數組比較

var a = [1, 2, 3],
    b = [1, 2, 3],
    c = [1, 2, 4]
a ==  b
a === b
a >   c
a <   c

解答

與前面2題一樣,a == ba === b 結果均爲 false。不過,當 >< 時,會依次對數組中的元素進行比較。

Arrays are compared lexicographically with > and <, but not with == and ===

7. 來做個加減法

'5' + 3
'5' - 3

解答

本題主要考察的是字符串的 + 運算。在 + 運算中,只要左右有一方是字符串,則按字符串拼接處理。所以結果爲: "53", 2

8. 再來個加減法

什麼?學 js 就爲了做個加減法?!

1 + - + + + - + 1

what? 出題官爲什麼這麼無聊?

這個式子讓人不明覺厲。我們換成人類語言吧:

1 + 空格 - 空格 + 空格 + 空格 + 空格 - 空格 + 1,這樣看起來就一目瞭然了,答案是 2 。

9. 與 0 比較的結果

Number.MIN_VALUE > 0

false true error other

解答

不知道這是第幾輪炮灰了,今天的題完全超出了我的臆測。Number.MIN_VALUE 難道是Number中的最小值?卒。

Number.MIN_VALUE is the smallest value bigger than zero, -Number.MAX_VALUE gets you a reference to something like the most negative number.

清楚了,Number.MIN_VALUE 原來是比 0 大的最小值,答案是 true

10. 連續比較

[1 < 2 < 3, 3 < 2 < 1]

[true, true] [true, false] [error] other

解答

注意:這不是數學!這不是數學!

是這樣的: [(1 < 2) < 3, (3 < 2) < 1][true < 3, false < 1]

true < 3 中,true 先轉爲 1, 1 < 2 爲 true。

true gets intified and is 1, while false gets intified and becomes 0.

11. 類型自動轉換

// the most classic wtf
2 == [[[2]]]

true false undefined other

解答

又是一個 “還有這種操作” 的題。

兩邊數據類型不同,先自動轉爲 string 類型。答案是 true

12. 逗號

var f = (function f() {
    return "1";
}, function g() {
    return 2;
})();
typeof f;

逗號表達式,f 爲 g(), typeof f"number"

13. typeof

var x = [typeof x, typeof y][1];
typeof typeof x;

"number" "string" "undefined" "object"

解答:

x 爲 undefinedtypeof x"undefined"typeof "undefined""string"

五、prototype

1. 對象的 protype

var a = {}, b = Object.prototype;
[a.prototype === b, Object.getPrototypeOf(a) === b]

[false, true] [true, true] [false, false] other

解答

[false, true]

Functions have a prototype property but other objects don’t so a.prototype is undefined.
very Object instead has an internal property accessible via Object.getPrototypeOf

2. 函數的prototype

function f() {}
var a = f.prototype, b = Object.getPrototypeOf(f);
a === b  //false

解答:

f.prototype is the object that will become the parent of any objects created with new f while Object.getPrototypeOfreturns the parent in the inheritance hierarchy.

3. getPrototypeOf

function f() {}
var parent = Object.getPrototypeOf(f);
f.name // f
parent.name // ""
typeof eval(f.name) // "function"
typeof eval(parent.name) //  undefined

The function prototype object is defined somewhere, has a name, can be invoked, but it’s not in the current scope.

4. 構造函數

function f() {
    return f;
}
new f() instanceof f;

"A、true" "B、false"

解答

先看個例子:

function Hello () {
    this.name = 'smith';
}
new Hello() instanceof Hello // true

當構造函數返回的是 Object 時,會用該對象代替構造時創建的示例對象。

六、正則表達式

1. match

function captureOne(re, str) {
  var match = re.exec(str);
  return match && match[1];
}
var numRe  = /num=(\d+)/ig,
    wordRe = /word=(\w+)/i,
    a1 = captureOne(numRe,  "num=1"),
    a2 = captureOne(wordRe, "word=1"),
    a3 = captureOne(numRe,  "NUM=2"),
    a4 = captureOne(wordRe,  "WORD=2");
[a1 === a2, a3 === a4] // [true, false]

Regular expressions in JavaScript if defined using the /g flag will carry a state across matches, even if they are actually used on different strings (the lastIndex property). This means a3 will be null as the regular expression was applied starting from the index of the last matched string, even if it was a different one.

2. 點號

if ('http://giftwrapped.com/picture.jpg'.match('.gif')) {
  'a gif file'
} else {
  'not a gif file'
}

we get a gif file, because

String.prototype.match silently converts the string into a regular expression, without escaping it, thus the ‘.’ becomes a metacharacter matching ‘/‘.

七、this

1. 對象方法中的 this 並不指代對象本身

var foo = {
    bar: function () {
        return this.baz;
    },
    baz: 1
};
(function () {
    return typeof arguments[0]();
})(foo.bar);

"undefined" "object" "number" "function"

解答

需要注意的是:js 中的 this 是弱綁定,只與調用關係有關。

bar 函數被調用時的 this 指代的是 window, 故答案爲 "undefined"

同理,下面的結果也是 "undefined"

var foo = {
bar: function () {
    return this.baz;
},
    baz: 1
};
typeof (f = foo.bar)();

2. arguments[0]()

var length = 10
function fn (){
    alert(this.length)
}
var obj = {
    length: 5,
    method: function (fn) {
        fn() // ? 10
        arguments[0]() // ? 1
    }
}
obj.method(fn)

解答

fn() 爲 10,這裏的 this 指代全局對象。

arguments[0]() 就是 fn 啊,難道還有貓膩? 是的!arguments[0]() 這樣調用時,this 綁定的是 arguments。當然了,我是不會用 arguments[0]() 這麼詭異的東西的,詭異的東西通常意味着bug。

arguments 是一個有着黑魔法的傢伙,且看下面例子:

function fn (a = 55) {
    console.log(arguments[0])
}
fn()

你以爲是 55,可結果卻是 undefineda 並未關聯到 arguments。

function func(a) { 
  a = 99;
  console.log(arguments[0]);
}
func(10);

這裏 a 就是 arguments[0],因此是 99。

html 部分

一、DOM 操作

1. document.write 和 innerHTML 區別

document.write 只能繪整個頁面

innerHTML 可以重繪頁面某個部分

第一句的回答其實很片面,只能說部分正確。 當 document 處於開啓狀態時,多次 document.write 會源源不斷的往 document 裏東西。當 document 處於關閉狀態時,document.write 會先隱式調用 document.open,再 document.writedocument.open 會清空整個 document。

document.open()
document.write('<h1>Hello World</h1>')
document.write('<p>this is the content of the document</p>')
document.close()

當頁面加載時,document 處於開啓狀態,這時 document.write 相當於添加內容:

<body>
    <h1>
        Hello world
    </h1>
    <script>
        document.write('<p>I was written by document.write</p>')
    </script>
</body>

當頁面加載完成以後,document 已關閉,document.write 則會造成 document 清空。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章