考試可以是功利的,比如考雅思是爲了出國留學,考廚師證可以當廚師,考教師證可以做老師。我們前18年的人生中似乎都在考試,爲了18歲的高考。高考是很多家庭跨越社會階層的最好機會,這次考試意義重大,重的讓人扭曲。扭曲後的東西在外力消失的情況下一定會反彈。卸下高考的重擔後,有人似乎失去生命的意義,直到可以考什麼試,他們會把考證當作自己的興趣愛好;有人會狂歡,從此鄙視一切考試,做一個抨擊考試教育的憤青;有人會忽視考試,得考且考,做一個佛系青年。
馬克思主義哲學觀告訴我們:一切東西都要分兩面看,有好的一面,也有壞的一面,可能還有中立的一面。在賦予考試太多其他意義後,或許我們早已忘了考試最純粹的目的:查漏补缺。考試是非常好的即時反饋系統,是完善知識體系,追蹤思維謬誤的非常好的方法。
不管是爲了面試還是面試別人,不管是爲了工作還是技術本身,都可以通過考試這一簡單的即時反饋系統梳理知識的脈絡,弄清一些似是而非的概念,完善知識體系。
下面整理了一些在前端技術中極易犯錯的知識點。
js部分
題目來源:
- JavaScript Puzzlers! do you really know JavaScript?
一、數據類型
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 中的數字格式,能夠表示的整數範圍是從 $-2 ^ {53}$ ~ $2^{53}$,包含邊界值。如果使用了超過此範圍的整數,則無法保證低位數字的精度。然而需要注意的是,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
,哈。
那麼爲什麼是$2^{53}$ 呢?
引用 Js的整型你懂了嗎:
image使用 52 位表示一个数的整数部分,那么最大可以精确表示的数应该是$2^{52}-1$才对, 就像 64 位表示整数时那样: $2^{63} - 1$ (去掉 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
解答:
該題考察了對 map 和 parseInt 函數的掌握情況。
["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 thearguments
object so changing the variables changesarguments
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 ofx
,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 be1
. On the other hand, thelength
property of theFunction
prototype object is defined to be0
.
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 aString
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
,callee
和arguments
不可用。所以如果用了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
答案爲: undefined
。typeof 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 andnew 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 == b
和 a === 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 is1
, while false gets intified and becomes0
.
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 爲 undefined
,typeof 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 soa.prototype
isundefined
.
veryObject
instead has an internal property accessible viaObject.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 withnew f
whileObject.getPrototypeOf
returns 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 (thelastIndex
property). This meansa3
will benull
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,可結果卻是 undefined
,a
並未關聯到 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.write
。document.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 清空。
网友评论