JavaScript Puzzlers详解
前言
题目来自JavaScript Puzzlers,涉及的知识点很广,本文的解题过程尽量做到详细易懂。由于文章篇幅较长,推荐先马后看,相信一定可以在文章中找到不熟或者概念模糊的知识点。(全篇共44题,笔者连蒙带猜一刷成绩为27/44,有几题都是蒙对的,勉强及格)
正文
第一题
["1", "2", "3"].map(parseInt)
考察知识点:
1.map函数
2.parseInt函数
解析:map函数的callback默认有三个参数,分别为currentValue、currentIndex、arrary。此处用parseInt作为callback,而parseInt只能接收两个参数,所以callback中的默认第三个参数array被忽略。如果把map的回调拆分开,此时相当于这样:
parseInt("1", 0) // 1
parseInt("2", 1) // NaN
parseInt("3", 2) // NaN
parseInt的第二个参数取值范围是2~36,此参数省略或者为0时,以十进制方式解析,所以“parseInt("1", 0)”返回1。
“parseInt("2", 1)”第二个参数为1,不在取值范围内,所以返回NaN。
“ parseInt("3", 2)”以二进制的方式解析字符串3,但是二进制中的有效数字只有0和1,所以返回NaN。
答案:[1, NaN, NaN]
第二题
[typeof null, null instanceof Object]
考察知识点:
1.null
2.instanceof
解析:原型链的源头为null,所有对象都是通过null派生出来的,null本身也被定义为对象,所以“typeof null”返回object字符串。instanceof用于判断构造函数的prototype属性是否在对象的原型链上,因为null为原型链顶端,而“Object.prototype”有值,显然“null instanceof Object”返回false。
答案:["object", false]
第三题
[ [3,2,1].reduce(Math.pow), [].reduce(Math.pow) ]
考察知识点:
1.reduce
解析:reduce函数的callback默认有四个参数,分别为accumulator、currentValue、currentIndex、array。此处用Math.pow作为callback,而Math.pow只能接收两个参数,所以currentIndex和array被忽略。
如果没有提供initialValue,reduce 会从索引1的地方开始执行 callback 方法,跳过第一个索引。如果提供initialValue,从索引0开始。在没有初始值的空数组上调用 reduce 将报错
--摘自MDN
“[3,2,1].reduce(Math.pow)”拆分后等价于:
Math.pow(3, 1) // 3
Math.pow(3, 2) // 9
Math.pow(9, 1) // 9
对于“[].reduce(Math.pow)”,因为使用空数组调用reduce,所以报错。
答案:error
第四题
var val = 'smtg';
console.log('Value is ' + (val === 'smtg') ? 'Something' : 'Nothing');
考察知识点:
1.运算符的优先级
2.“+”运算符的隐式转换
3.字符串的隐式转换
解析:“+”运算符的优先级大于三目运算符的优先级,所以执行过程如下:
val === 'smtg' // true
'Value is ' + true // "Value is true"
"Value is true" ? 'Something' : 'Nothing' // "Something"
答案:"Something"
第五题
var name = 'World!';
(function () {
if (typeof name === 'undefined') {
var name = 'Jack';
console.log('Goodbye ' + name);
} else {
console.log('Hello ' + name);
}
})();
考察知识点:
1.块级作用域与变量提升
解析:使用var声明的变量没有块级作用域(注意区分块级作用域与函数作用域),使用let、const声明的变量有块级作用域,因为函数内的name用var声明,所以不存在块级作用域,导致变量提升。等价写法如下:
var name = 'World!';
(function () {
var name;
if (typeof name === 'undefined') {
name = 'Jack';
console.log('Goodbye ' + name);
} else {
console.log('Hello ' + name);
}
})();
答案:"Goodbye Jack"
第六题
var END = Math.pow(2, 53);
var START = END - 100;
var count = 0;
for (var i = START; i <= END; i++) {
count++;
}
console.log(count);
考查知识点:
1.JS中能存储的最大值
64位浮点数能表示的整数范围是-2^53~2^53,即最大值为Math.pow(2, 53),当超出这个值时,自动转为最大值。因此本题的for循环会一直执行下去,console.log语句永远无法执行。
答案:死循环
第七题
var ary = [0,1,2];
ary[10] = 10;
ary.filter(function(x) { return x === undefined;});
考察知识点:
1.数组跳index赋值
2.filter
解析:ary初始化为3个元素后赋值ary[10],数组中index从3到9的元素是未定义的,不会被自动填充为undefined,而filter函数的callback不会回调数组中的未定义元素。验证如下:
未定义 filter注意区分未定义元素与undefined元素,filter函数的callback是会回调数组中undefined元素的,验证如下:
undefined filter因为本题中的数组不存在undefined元素,所以不存在通过条件筛选的元素,最终返回[]。
答案:[]
第八题
var two = 0.2
var one = 0.1
var eight = 0.8
var six = 0.6
[two - one == one, eight - six == two]
考查知识点:
1.浮点数的精度及表达方式
解析:这题比较简单,但为了说明原因还是啰嗦一下。老司机都知道,浮点数的运算会存在精度缺失问题,为什么会精度缺失?我们知道计算机中的数值是以二进制方式存储的,以3.14的小数部分0.14为例,转换如下:
// 0.14
0.14 * 2 = 0.28 0
0.28 * 2 = 0.56 0
0.56 * 2 = 1.12 1
0.12 * 2 = 0.24 0
0.24 * 2 = 0.28 0
0.48 * 2 = 0.96 0
0.96 * 2 = 1.92 1
0.92 * 2 = 1.84 1
...
所以0.14取8位二进制有效数字的表达式为0.00100011,显然当小数部分转成二进制时,可能存在有效数字范围内无法精确表达的问题,因此浮点数运算会存在精度缺失问题。
回到本题,来计算一下0.1、0.2、0.6、0.8转成二进制是怎样表达的:
// 0.1
0.1 * 2 = 0.2 0
0.2 * 2 = 0.4 0
0.4 * 2 = 0.8 0
0.8 * 2 = 1.6 1
0.6 * 2 = 1.2 0
0.2 * 2 ...
可以看到,以0.1为源,在转换的过程中出现了0.2、0.4、0.8、0.6,在0.6之后又出现了0.2。所以,本题的四个小数在转成二进制后都是循环小数。
// 0.2 - 0.1
0.2 => 0.001000100010...
0.1 => 0.000100010001...
0.2 - 0.1 => 0.000100010001...
0.1 => 0.000100010001...
显然,0.2 - 0.1的二进制运算结果与0.1的二进制表达方式相等
// 0.8 - 0.6
0.8 => 0.100010001000...
0.6 => 0.000100010001...
0.8 - 0.6 => 0.011101110111...
0.2 => 0.001000100010...
显然,0.8 - 0.6的二进制运算结果大于0.2的二进制表达方式
答案:[true, false]
第九题
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'));
考查知识点:
1.字符串对象与原生字符串的区别
解析:这一题也很简单,通过String构造函数生成的字符串对象与原生字符串不相等,验证如下
字符串对象与原生字符串- 原生字符串类型为"string",字符串对象类型为"object"
- 通过new调用String构造函数生成的是字符串对象,直接调用String构造函数生成的是原生字符串
除此之外,字符串对象与原生字符串在作为eval参数时,表现也是不同的:
eval处理字符串- 当参数为原生字符串时,eval会将字符串当做源代码处理
- 当参数为字符串对象时,eval会将字符串当做对象处理
除此之外,在代码里可以通过原生字符串访问到字符串对象的方法,这是因为JS在解析时会自动将原生字符串转换为字符串对象。
答案:"Do not know!"
第十题
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'));
考察知识点:
1.字符串对象与原生字符串的区别
解析:见第九题
答案:"Case A"
第十一题
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);
考察知识点:
1.字符串转数字
2.Infinity
3.map
解析:本题关键点在于Infinity。Infinity表示正无穷大,所以任何数乘以无穷大仍为无穷大,任何数(除Infinity与-Infinity外)除以无穷大都为0。因为Infinity无法用准确的数值来表示,所以Infinity除以Infinity的值为NaN。同理,因为无法准确表示Infinity的值,所以“Infinity % 2”的值也为NaN。另外:
-9 % 2 // -1
答案:[true, true, true, false, false]
第十二题
parseInt(3, 8)
parseInt(3, 2)
parseInt(3, 0)
考察知识点:
1.parseInt
解析:见第一题
答案:3, NaN, 3
第十三题
Array.isArray( Array.prototype )
考察知识点:
1.Array.isArray
2.Array.prototype
解析:Array.isArray用来检测对象是否为数组,Array.prototype也是数组,所以本题返回true。
对比instanceof,“Array.prototype instanceof Array”返回false。因为instanceof的检测机制为,检测构造函数的prototype是否在对象的原型链上。此时构造函数的prototype就是要检测的对象本身,显然不在检测对象的原型链上,所以返回false。再来做个试验:
instanceof只要将obj的原型指向Array.prototype,此时instanceof就返回true。那么如何检测对象的真是类型呢?typeof显然不行,他对于实例对象都返回'object'。我们可以通过toString的默认实现来获取当前对象的类型标识:
function objectType(obj) {
return Object.prototype.toString.apply(obj)
}
对象类型检测
显然,只是将obj的__proto__属性指向Array.prototype,由Object构造函数创建的obj对象并不会变成数组,他的类型标识仍然是'Object'。
答案:true
第十四题
var a = [0];
if ([0]) {
console.log(a == true);
} else {
console.log("wut");
}
考查知识点:
1.if条件判断的自动转换
解析:送分题。“if([0])”为真,“[0] == true”为假,所以输出false
答案:false
第十五题
[]==[]
考察知识点:
1.==
2.深拷贝与浅拷贝
解析:先简单说说深拷贝与浅拷贝
深拷贝与浅拷贝
深拷贝可以理解为重新创建一个与原对象各数据相同的对象,浅拷贝可以理解为引用。上图中,a0、a1互为深拷贝关系,a1、a2互为浅拷贝关系。因为深拷贝为重新创建对象,所以a0和a1是不等的。而浅拷贝为引用关系,所以a1和a2是相等的。
回到本题,“[] == []”显然等号两端为两个同类型,但不同的对象,所以返回false。
对于“==”而言,当发现等号两端数据类型不一致时会进行隐式转换,可以看一下这道题的变种:
[] == ![] // true
“!”运算符优先级高于“==”优先级,所以先计算“![]”,此时“![]”的值为false,所以相当于比较[]
与false
是否相等。因为等号两端数据类型不一致,此时进行隐式转换。[]为空数组转换成0,false转换成0,所以此时两者是相等的。
答案:false
第十六题
'5' + 3
'5' - 3
考察知识点:
1.加法运算符
2.减法运算符
解析:送分题,直接给答案。
答案:"53",2
第十七题
1 + - + + + - + 1
考察知识点:
1.加减运算符的叠加使用
解析:这道题很偏很烂,就跟当年C语言用“++”操作符出的烂题一样,没啥实际意义
加减运算符的叠加使用-1
可以看到,JS中允许加减运算符连接使用,但是不允许出现两个加或者两个减。当加减运算符连接使用时,和单独使用减法运算符是一致的。
加减运算符的叠加使用-2但是,当加减运算符中间出现空格时,此时两个加号与两个减号是可以出现的,运算规则相当于“正正得正,负负得正,正负得正,负正得正”。
回到本题,可拆解为如下运算:
1 + (- + + + - + 1)
括号内运算结果为1,所以本题等价于计算1+1的值,结果为2
答案:2
第十八题
var ary = Array(3);
ary[0]=2
ary.map(function(elem) { return '1'; });
考查知识点:
1.数组中的未定义元素是否会触发map函数的callback
解析:在“第七题”的知识点filter中,我们说过,数组中的undefined元素会触发filter函数的callback,而未定义元素不会触发,这个结论同样适用于map及reduce。reduce相对于map和filter在这一点又略有不同,map和filter可以通过空数组调用,而reduce通过空数组调用会引发error,reduce空数组调用引发error的这个特性在“第三题”中也提到过。
知识都是想通的,在后面的解题中会越来越多提到前面的知识。
回到本题,首先生成一个长度为3的空数组ary,然后将这个ary的第0个元素设置为2,此时ary的后两个元素都是未定义的,通过ary调用map。所以返回的新数组第0个元素为"1",后两个仍为未定义元素。
map答案:长度为3,第0个元素为"1",后两个元素未定义的数组(再次强调,注意区分未定义与undefined)
第十九题
function sidEffecting(ary) {
ary[0] = ary[2];
}
function bar(a,b,c) {
c = 10
sidEffecting(arguments);
return a + b + c;
}
bar(1,1,1)
考查知识点:
1.arguments
解析:本题相对来说难度不大,但是如果配合默认参数和剩余参数就有点绕了,先说本题解法。在没有默认参数及剩余参数的正常模式(非严格模式)下,函数的参数与arguments是互通的,所以在bar函数中将c赋值为10,arguments[2]也变成了10,再调用sidEffecting函数将arguments[0]赋值为10,所以a也变成了10。最终a、b、c对应的值为10、1、10,所以最终结果为21。
拓展:
1.默认参数
2.剩余参数
下面说有默认参数的情况:
function argumentsTest(a = 1) {
console.log(a)
console.log(arguments[0])
a = 2
console.log(arguments[0])
arguments[0] = 3
console.log(a)
}
默认参数-1
默认参数-2
可见,只要函数表达式中存在默认参数,不管在调用的时候是否给函数的默认参数传值,这个参数和对应arguments中的数据都是分离的。但是注意,这两种调用方式有一点不同:当调用时不给a赋值,取出来的arguments[0]是undefined。
如果函数表达式是这样的:
function argumentsTest(a = 1, b)
b与arguments[1]也是分离的,这里不再赘述。
接着说剩余参数的情况:
function argumentsTest(a, ...theArgs) {
console.log(a)
console.log(arguments[0])
a = 2
console.log(arguments[0])
arguments[0] = 3
console.log(a)
}
剩余参数
显然,含剩余参数的函数,参数与arguments中对应的元素也不互通。
答案:21
第二十题
var a = 111111111111111110000,
b = 1111;
a + b;
考查知识点:
- JS中能存储的最大值
解析:本题考点与第六题相同,a的值111111111111111110000已超出64未浮点数能表示的最大整数,因此a + b的结果仍为a
答案:111111111111111110000
第二十一题
var x = [].reverse;
x();
考察知识点:
1.reverse
解析:本题用变量x接收reverse函数,然后调用“x()”,由于reverse是属于Array.prototype,所以应该由数组来调用,而当前调用方式相当于通过全局作用域中的this调用(即window),但是window本身不是数组,所以返回error。笔者在Chrome和Firefox中验证了一下,确实返回error。
不过原网站给的答案是window,而非error。解释是,此时reverse函数(即x)中的this指向window,所以返回window。介于出题时间很早,猜测那个时候的reverse函数没有做类型校验?
答案:error
第二十二题
Number.MIN_VALUE > 0
考察知识点:
1.Number.MIN_VALUE
解析:送分题。Number.MIN_VALUE
为在JS中能表示的最小正数,值约为 5e-324,很接近于+0
,但仍然大于0。
答案:true
第二十三题
[1 < 2 < 3, 3 < 2 < 1]
考察知识点:
1.<
解析:送分题。<
运算符得到的结果为true或者false(当然,>
运算符也一样),因此本题可拆解为如下步骤:
// 1 < 2 < 3
true < 3
1 < 3
true
// 3 < 2 < 1
false < 1
0 < 1
true
答案:[true, true]
第二十四题
// the most classic wtf
2 == [[[2]]]
考察知识点:
1.==
解析:送分题。==
运算符在等号两边类型不一致时会进行隐式转换,直到转换为相同类型再做比较。数值类型与引用类型进行比较,会将引用类型转换成数值类型。因此等号右边最终被转换为2,此时相当于:
// 2 == [[[2]]]
2 == [[2]]
2 == [2]
2 == 2
答案:true
第二十五题
3.toString()
3..toString()
3...toString()
考察知识点:
1.浮点数的表示方法
解析:这一题也不难,先看代码
浮点数的表示方法因此,.3
与3.
都是合法的表达方式,此时相当于省略了数字0。回到本题,可做以下等价写法:
// 3.toString()
3.0toString()
// 3..toString()
3.0.toString()
// 3...toString()
3.0..toString()
显然,一和三语法错误,二可以解析。因此,最终答案为error, "3.0", error。
答案:error, "3.0", error
第二十六题
(function(){
var x = y = 1;
})();
console.log(y);
console.log(x);
考察知识点:
1.作用域
解析:本题其实也很简单,来看等价写法
// (function() { var x = y = 1})()
(function() {
y = 1
var x = y
)()
显然,x的作用域仅在匿名函数内部,而y的作用域为全局。因此,第一句log输出为1,而第二句log输出error。
答案:1, error
第二十七题
var a = /123/,
b = /123/;
a == b
a === b
考察知识点:
1.==
2.===
解析:送分题。正则是对象,对于==
,等号两边类型相等,但不是指向同一个对象,所以返回false。对于===
,直接比较等号两端是否指向同一个地址,由于是不同对象,所以地址不同,返回false。
答案:false, false
第二十八题
var a = [1, 2, 3],
b = [1, 2, 3],
c = [1, 2, 4]
a == b
a === b
a > c
a < c
考察知识点:
1.==
2.===
3.>、<
解析:送分题。本题前两个答案都为false,解释同第二十七题。当大于和小于运算符两端都为数组时,逐个取出进行比较,因此后两个答案分别为false和true。
答案:false, false, false, true
第二十九题
var a = {}, b = Object.prototype;
[a.prototype === b, Object.getPrototypeOf(a) === b]
考察知识点:
1.函数的prototype属性
2.对象的__proto__属性
3.大括号生成的对象的constructor
解析:送分题。a为实例对象,在为自定义prototype属性前,prototype的值为undefined。因此,“a.prtotype == b”返回false。对于用大括号生成的对象,实际是调用Object构造函数创建的。而“Object.getPrototypeOf(a)”访问的属性是对象的__proto__,实例对象的__proto__指向对象构造函数的prtotype属性,而a对象的构造函数就是Object,因此“Object.getPrototypeOf(a) === b”返回true。
答案:[false, true]
第三十题
function f() {}
var a = f.prototype, b = Object.getPrototypeOf(f);
a === b
考察知识点:
1.prototype与__proto__的区别
解析:送分题。在未手动指定两个属性指向的情况下,两者显然是不等的。(详细解读可以参考笔者之前写的《大话JavaScript对象》这篇文章)
答案:false
第三十一题
function foo() { }
var oldName = foo.name;
foo.name = "bar";
[oldName, foo.name]
考查知识点:
1.configurable与writable
解析:送分题。我们知道每个函数内部都有name属性,这个属性是只读的,显然他来自函数的原型链上。并且name属性的configurable为true,而writable为false,因此foo.name可以赋值,但是并不会改变name中存储的值。
答案:["foo", "foo"]
第三十二题
"1 2 3".replace(/\d/g, parseInt)
考察知识点:
1.replace
解析:当replace函数的第二个参数为函数时,对应的函数参数为match、p1,p2, ...、offset、string。所以可分解为如下表达方式:
parseInt('1', 0)
parseInt('2', 2) //2进制中不存在2
parseInt('3', 4)
因此答案为,"1 NaN 3"。
答案:"1 NaN 3"
第三十三题
function f() {}
var parent = Object.getPrototypeOf(f);
f.name // ?
parent.name // ?
typeof eval(f.name) // ?
typeof eval(parent.name) // ?
考察知识点:
1.typeof
2.eval
解析:f.name显然返回函数名"f",parent为f对象的原型,是个匿名函数,因此parent.name返回""
。对于后两句,等价于如下写法:
// typeof eval(f.name)
typeof eval("foo")
typeof foo
// typeof eval(parent.name)
typeof eval("")
typeof undefined
因为foo已定义为函数所以等价后的第一句返回function
,第二句仍返回undefined。
原题给的最后一个答案为error,但经Chrome和Firefox验证后确实返回undefined,猜测老版本的"typeof undefined"不符合语义所以抛错?
答案:"f", "", function, undefined
第三十四题
var lowerCaseOnly = /^[a-z]+$/;
[lowerCaseOnly.test(null), lowerCaseOnly.test()]
考察知识点:
1.test函数的隐式转换
解析:送分题。正则对象的test函数对应参数为字符串,如果传入的不是字符串,会通过toString函数进行转换,如果传入的参数不含有toString方法,这里又分为两种情况:
1.不是继承自Object.prototype的对象,如Object.create(null)
2.null、undefined
对于不是继承自Object.prototype的对象会因为找不到toString方法而报错,对于null、undefined会直接转换成对于的字符串"null"、"undefined"。
回到本题,等价写法如下:
// lowerCaseOnly.test(null)
lowerCaseOnly.test("null")
// lowerCaseOnly.test()
lowCaseOnly.test("undefined")
答案:[true, true]
第三十五题
[,,,].join(", ")
考察知识点:
1.数组中的逗号
解析:JS中的数组允许最后一个元素后跟上,
显然,这里是3个未定义元素,而非4个。因此,最终结果为:", , "(注意,join函数内部的字符串中逗号后面还有个空格)
答案:", , "
第三十六题
var a = {class: "Animal", name: 'Fido'};
a.class
考察知识点:
1.关键字作为属性名称
解析:这完全取决于JS引擎是否支持关键字作为属性名,若支持则返回"Animal",不支持则报错。经测试,Chrome和Firefox均返回"Animal"。
答案:"Animal"
第三十七题
var a = new Date("epoch")
考察知识点:
1.Date传参
解析:显然Date构造函数的参数不合法,所以返回一个不合法的Date实例对象,即Invalid Date。
答案:Invalid Date
第三十八题
var a = Function.length,
b = new Function().length
a === b
考查知识点:
1.Function的length
2.函数的length
解析:Function.length定义为1,而函数的length属性是形参的个数,这其中不包括剩余参数,如果参数中存在默认值,则length的长度为第一个含默认参数之前参数的个数。因为“new Function().length”没有形参,所以b的值为0,而a的值为1。因此,最终结果为false。
答案:false
第三十九题
var a = Date(0);
var b = new Date(0);
var c = new Date();
[a === b, b === c, a === c]
考察知识点:
1.Date作为构造函数与普通函数调用时的返回值
2.===
解析:Date作为普通函数调用返回字符串,作为构造函数调用返回Date实例对象。因此a为字符串类型,b和c为Date类型,但是b和c为两个不同的实例对象,指向的地址不同,因此“b===c”返回false。其他两个等号左右两边的数据类型都不相同,自然都返回false。
答案:[false, false, false]
第四十题
var min = Math.min(), max = Math.max()
min < max
考察知识点:
1.Math.min
2.Math.max
解析:min返回一组数据中的最小值,max返回一组数据中的最大值。但是本题min和max都没有参数,在这种情况下,min返回+Infinity
,而max返回-Infinity
。因此,“min < max”返回false。
答案:false
第四十一题
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]
考察知识点:
1.exec
解析:当正则表达式使用“g”标记时,执行exec时,查找的起点为上一次执行exec后lastIndex的值。而正则表达式中不带“g”标记的,每次重头查找。因为numRe中带“g”,所以a1的值为1, a3的值为null。而wordRe中不带“g”,所以a2的值为1,a4的值为2。
答案:[true, false]
第四十二题
var a = new Date("2014-03-19"),
b = new Date(2014, 03, 19);
[a.getDay() === b.getDay(), a.getMonth() === b.getMonth()]
考察知识点:
- Date
解析:这题有点让人难受,因为第一种写法表达的是2014年3月19号星期三,而第二种写法表达的是2014年4月19号星期六。在getDay这块也有坑,getDay返回的是星期几,因此“a.getDay()”的值为3,而“b.getDay()”的值为6,如果想获取Date实例对象当前月份的天数,要使用date函数。再说说getMonth的坑,getMonth确实是获取月份的,但是month是从0开始数的,所以获取到的月份会比实际月份少一个月。就本题而言,a.getMonth()获取到的月份是2,b.getMonth获取到的月份是3。
真难受。。
答案:[false, false]
第四十三题
if ('http://giftwrapped.com/picture.jpg'.match('.gif')) {
'a gif file'
} else {
'not a gif file'
}
考察知识点:
1.String.prototype.match()
解析:这题比较简单。如果match中传入的obj不是正则表达式对象,JS会通过 new RegExp(obj)
将其转换成一个正则表达式对象。如果match参数为空,则直接返回[""]
。回到本题,match内的参数.gif
就被转换成/.gif/
,因此可以匹配。
答案:"a gif file"
第四十四题
function foo(a) {
var a;
return a;
}
function bar(a) {
var a = 'bye';
return a;
}
[foo('hello'), bar('hello')]
考察知识点:
1.函数内部变量的声明
解析:送分题。直接上等价写法:
function foo(a) {
a;
return a
}
function bar(a) {
a = 'bye';
return a;
}
答案:["hello", "bye"]
JavaScript真让人头大🙄
Have fun!