Web前端之路

JavaScript Puzzlers详解

2019-07-22  本文已影响70人  01_Jack

前言

题目来自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构造函数生成的字符串对象与原生字符串不相等,验证如下

字符串对象与原生字符串
  1. 原生字符串类型为"string",字符串对象类型为"object"
  2. 通过new调用String构造函数生成的是字符串对象,直接调用String构造函数生成的是原生字符串

除此之外,字符串对象与原生字符串在作为eval参数时,表现也是不同的:

eval处理字符串
  1. 当参数为原生字符串时,eval会将字符串当做源代码处理
  2. 当参数为字符串对象时,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;

考查知识点:

  1. 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.浮点数的表示方法

解析:这一题也不难,先看代码

浮点数的表示方法

因此,.33.都是合法的表达方式,此时相当于省略了数字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()]

考察知识点:

  1. 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!

上一篇 下一篇

猜你喜欢

热点阅读