44道JavaScript送命题 1. map&parseInt传参 2. typeof和instanceof 3.reduce&Math.pow传参 4. 优先级 5.变量提升问题 6. JavaScript能表示的最大数 7. 稀疏数组问题  8. 数字精度问题 9. 字面量问题 10. String()函数 11. 除法运算符% 12. parseInt 13. Array.prototype 14. if语句 15. 对象比较问题 16. +是字符串连接符也是加法运算 17. 加减运算和正负运算 18. 还是稀疏数组 19. argument对象 20. JavaScript中的最大数 21. Array.property.reverse 22. Number.MIN_VALUE 23. 强制转换 24. 数组字面量的字符串表示 25. 3.和.3 26. var和闭包问题 27. 正则表达式不可相互比较 28. 数组比较 29. 构造函数的原型 30. 函数的原型对象 31. Funct

很久以前看过一个老外写的帖子,JavaScript Puzzlers!,直译就是JavaScript难题,里面列举了44道JavaScript选择题,大部分都是让人摸不着头脑的题目,需要仔细琢磨一番才能得到正确答案。也有一些作者也没有解释清除,直接通过实验给出答案了。

这44个问题是在ECMA 262(5.1)环境下,浏览器中试验的,如果是node环境下可能不同。这是因为二者环境差异,比如node环境下顶层变量是global,浏览器环境下则是windows。

本文部分内容也参考了文章Javascript 变态题解析

["1", "2", "3"].map(parseInt)结果是什么?

map方法指定一个回调函数,重新创建一个由回调函数返回值组成的新数组。该方法的原型是:

var new_array = arr.map(function callback(currentValue[, index[, array]]) {
// Return element for new_array 
}[, thisArg])

map接受2个参数,一个是回调函数callback,一个是回调函数的this值。

解释如下:

  • callback:生成新数组元素的函数,有三个参数
  • currentValue:callbac当前正在处理的元素
  • index:callback当前正在处理的当前元素的索引
  • array:map方法调用的数组本身
  • thisArg:执行callback函数时值被当做this

Number.parseInt接受两个参数,原型Number.parseInt(string[, radix]),一个是要解析的值,一般是字符串,如果不是的话,使用toString方法将它转化为字符串。参数radix,是一个介于236之间的整数,如果省略该值或者为0,则按照10进制来解析,也就是说默认值是10,如果是“0x”或者“0X”开头,则以16进制为基数。如果小于2或者大于36,则parseInt返回NaN。

也就是说[].map(parseInt)这种写法根本就是想当然的,本题相当于下面的三句:

parseInt('1', 0);
parseInt('2', 1);
parseInt('3', 2);

这三句只有第一句会把第二个参数0默认为10,把10进制中的字符串“1”转换为10进制整数,结果是1;第二句在1进制里的字符串“2”转换为10进制整数,因为1进制里没有2,所以返回NaN;第三句把2进制中的字符串“3”转换为10进制整数,因为2进制中没有3,所以返回NaN;最终结果是[1, NaN, NaN]。

另外,如果想得到正确的结果,应该这样写["1", "2", "2"].map(i => parseInt(i))。

2. typeof和instanceof

运行[typeof null, null instanceof Object]这个表达式结果是什么?,这个主要考察typeof,instanceof两个操作符,前者是返回一个字符串表示未经计算的操作数的类型,后者是判断null的原型链中是否出现了Object函数的原型Object.property。

这两个操作符用来判断类型,前者常用来判断字面量,后者用来判断对象的类型,但是两个都有缺陷,详见另一篇文章《javascript中判断数据类型》

但是null是一个比较特殊的值,type of null返回的是“objec”,这是因为JavaScript最初实现中,值是由一个表示类型的标签和实际数值表示的。对象的类型标签是0,由于null代表是空指针(大多数平台下值为0x00),因此null的类型标签是0,typeof null也就返回“object”。

null是所有原型链的最顶端,null instanceof Object返回false,假设null这个值有构造函数Null,obj instanceof Null才会返回true。

所以上面表达式返回["object", false]。

3.reduce&Math.pow传参

表达式[ [3,2,1].reduce(Math.pow), [].reduce(Math.pow) ]返回什么?

 和第1题有些类似。arr.reduce方法对数组中每个元素执行一个自定义的reducer函数(升序执行),并将结果汇总为单个值,这个常常用来累加一个数组中的数字。原型如下:

arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue]) 
  • callback:数组中每个值要执行的函数,有四个参数。
  • accumulator:它是上一次回调函数时得到的值,或者initialValue。
  • currentValue:数组中正在处理的当前元素。
  • index:可选,数组中正在处理的元素的索引,如果提供了initialValue,则起始索引号为0,否则从索引1开始。
  • array:调用reduce()的数组
  • initialValue:作为第一次调用callback时的第一个参数的值。如果没有提供初始值,则将使用数组中的第一个元素。在没有初始值的空数组上调用reduce将报错。

注意:如果没有提供initialValue,reduce 会从索引1的地方开始执行 callback 方法,跳过第一个索引。如果提供initialValue,从索引0开始。

回调函数第一次执行时,accumulator 和currentValue的取值有两种情况:

  1. 如果调用reduce()时提供了initialValueaccumulator取值为initialValuecurrentValue取数组中的第一个值;如果没有提供 initialValue,那么accumulator取数组中的第一个值,currentValue取数组中的第二个值。
  2. 如果数组为空且没有提供initialValue,会抛出TypeError 。如果数组仅有一个元素(无论位置如何)并且没有提供initialValue, 或者有提供initialValue但是数组为空,那么此唯一值将被返回并且callback不会被执行。

Math.pow(base, exponent),返回基数base的指数exponent次幂,即baseexponent。

上面表达式调用reduce方法的时候没有提供initialValue,从索引1开始执行,第一次执行的时候accumulator取arr[0],这里是3,currentValue取第二个值,这里是2,传给Math.pow,得到9。然后[9, 1].reduce(Math.pow),相当于Math.pow(9, 1),最终得到9。

第二个表达式是在空数组上调用reduce,并且没有提供initialValue,所以抛出错误:VM146:1 Uncaught TypeError: Reduce of empty array with no initial value at Array.reduce (<anonymous>)。

最后整个表达式的结果还是抛出错误。

4. 优先级

var val = 'smtg';
console.log('Value is ' + (val === 'smtg') ? 'Something' : 'Nothing'); 

上面的表达式输出结果是什么?这个问题考察的是加号和三元运算的优先级,由于加号的优先级高于三元表达式,所以实际执行的是:

console.log('Value is true' ? 'Something' : 'Nothing'); 

因此最后输出“Something”。

5.变量提升问题

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

上面表达式输出什么?

这个是考察var变量提升问题,使用var申明的变量会提神到函数顶部,但是并不会初始化,这个是JavaScript内部机制。于是上面语句相当于:

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

name的声明放在了函数顶部,但是值是undefined。因为代码又放在一个闭包里,最外层那个name = “world”是不能访问的,闭包有隔离变量的作用。最后,上面的语句输出“Goodbye Jack”。

6. JavaScript能表示的最大数

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

上面表达式输出什么?

乍一看是100,其实是干扰,这考察的不是循环,var变量啥的,而是JavaScript能表示的最大的数字是253,即次幂表达式Math.pow(2, 53)在这个最大数的基础上加上一个整数得到的结果都是不准确的。看下面的例子:

    var END = Math.pow(2, 53);
    var START = END - 5;
    var count = 0;
    console.log(END);                   //      9007199254740992
    console.log(END + 7);               //      9007199254740997
    console.log(START++ <= END, START); // true 9007199254740988
    console.log(START++ <= END, START); // true 9007199254740989
    console.log(START++ <= END, START); // true 9007199254740990
    console.log(START++ <= END, START); // true 9007199254740991
    console.log(START++ <= END, START); // true 9007199254740992
    console.log(START++ <= END, START); // true 9007199254740992
    console.log(START++ <= END, START); // true 9007199254740992
    console.log(START++ <= END, START); // true 9007199254740992
    console.log(START++ <= END, START); // true 9007199254740992
    console.log(START++ <= END, START); // true 9007199254740992
    console.log(START++ <= END, START); // true 9007199254740992
    console.log(START++ <= END, START); // true 9007199254740992 

缩小了演示范围,END已经是最大值9007199254740992,END+7,应该得到9007199254740998,其实是得到9007199254740997,这是一个错误的计算结果。如果在START的基础上累加(每次加1),到第五次(包含第五次)得到的结果都是9007199254740992。所以在原题的i++过程中,运行到第100次之后每次得到的值都是9007199254740992,都满足i<=END,也就是说这是一个死循环,程序一直运行,得不到任何结果。

7. 稀疏数组问题

var ary = [0,1,2];
ary[10] = 10;
var result = ary.filter(function(x) { return x === undefined;});

这个表达式的结果是什么?

这个问题考察的是稀疏数组,稀疏数组中的未赋值的元素是空,并且filter会忽略这些元素,所以上面filter语句返回的是空数组[]。使用console.log()语句输出稀疏数组如下,可以看到console.log()语句也会忽略掉空位。

44道JavaScript送命题
1. map&parseInt传参
2. typeof和instanceof
3.reduce&Math.pow传参
4. 优先级
5.变量提升问题
6. JavaScript能表示的最大数
7. 稀疏数组问题
 8. 数字精度问题
9. 字面量问题
10. String()函数
11. 除法运算符%
12. parseInt
13. Array.prototype
14. if语句
15. 对象比较问题
16. +是字符串连接符也是加法运算
17. 加减运算和正负运算
18. 还是稀疏数组
19. argument对象
20. JavaScript中的最大数
21. Array.property.reverse
22. Number.MIN_VALUE
23. 强制转换
24. 数组字面量的字符串表示
25. 3.和.3
26. var和闭包问题
27. 正则表达式不可相互比较
28. 数组比较
29. 构造函数的原型
30. 函数的原型对象
31. Function.name
32. str.replace
33. eval函数
34. exp.test
35. 数组元素最后一个逗号
36. class关键字
37. 时间转换问题
38. 函数的形参个数
39. 还是时间转换问题
40. Math.max()&Math.min()
41. 正则表达式的“记忆”
42. Date中的month
43. 正则表达式中的转义
44. 变量提升 

所以本题输出的结果是[]。

 8. 数字精度问题

var two   = 0.2;
var one   = 0.1;
var eight = 0.8;
var six   = 0.6;
console.log([two - one === one, eight - six === two])

上面表达式输出结果是什么?

这个考察的是JavaScript数字精度问题,JavaScript的Number类型为双精度IEEE 754 64位浮点类型,0.1不能精确的转换成二进制,会有溢出,遵循IEEE 754标准的语言都有这个毛病,包括java。这个问题造成0.1 + 0.2 != 0.3,0.8 - 0.6  != 0.2等等。这里先这样简单解释一下,下次专门写一篇来解释这个问题。

本题输出结果是[true, false]。

9. 字面量问题

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’ !== new Strting('A')返回false,所以最后输出‘Do not know’。判断恒等是三个等号'A' == new String('A')返回false,而两个等号会调用对象的toString()方法,'A'==new String('A')返回true,两个等号会先将右边的对象转换成字符串,如下图:

44道JavaScript送命题
1. map&parseInt传参
2. typeof和instanceof
3.reduce&Math.pow传参
4. 优先级
5.变量提升问题
6. JavaScript能表示的最大数
7. 稀疏数组问题
 8. 数字精度问题
9. 字面量问题
10. String()函数
11. 除法运算符%
12. parseInt
13. Array.prototype
14. if语句
15. 对象比较问题
16. +是字符串连接符也是加法运算
17. 加减运算和正负运算
18. 还是稀疏数组
19. argument对象
20. JavaScript中的最大数
21. Array.property.reverse
22. Number.MIN_VALUE
23. 强制转换
24. 数组字面量的字符串表示
25. 3.和.3
26. var和闭包问题
27. 正则表达式不可相互比较
28. 数组比较
29. 构造函数的原型
30. 函数的原型对象
31. Function.name
32. str.replace
33. eval函数
34. exp.test
35. 数组元素最后一个逗号
36. class关键字
37. 时间转换问题
38. 函数的形参个数
39. 还是时间转换问题
40. Math.max()&Math.min()
41. 正则表达式的“记忆”
42. Date中的month
43. 正则表达式中的转义
44. 变量提升

10. 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')); 

上面语句输出什么?String("A")不会创建一个对象,调用String方法返回一个字符串"A",所以输出“Case A”。

11. 除法运算符%

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); 

上面的map结果是什么?

这个题目是考察求余运算符,前两个数字7,4没什么好说的。‘13’%2,%会把字符串转换成整形参与计算,得到1;-9%2会保留负号得到-1,符号是和第一个操作数保持一致,第二个操作数的符号会忽略,所以返回false,Infinity参与计求余计算得到NaN。所以最终结果是[true, true, true, false, false]。

12. parseInt

console.log(parseInt(3, 8));
console.log(parseInt(3, 2));
console.log(parseInt(3, 0));

上面表达式分别输出什么?

这里和第一题一样,再一次考察parseInt这个函数,把题目翻译翻译是问:把8进制里的3转化成10进制整数是3;把2进制中的3转换成10进制整形得到NaN,因为2进制中没有3;把10进制中的3转换成10进制整数还是3;所以最终结果是[3, NaN, 3]。

13. Array.prototype

console.log(Array.isArray(Array.prototype)); 

上面表达式输出结果是什么?

这个是一个非常容易忽略的知识点Array.property,是Array函数的原型对象,还是一个数组,从下面的语句可以看出:

44道JavaScript送命题
1. map&parseInt传参
2. typeof和instanceof
3.reduce&Math.pow传参
4. 优先级
5.变量提升问题
6. JavaScript能表示的最大数
7. 稀疏数组问题
 8. 数字精度问题
9. 字面量问题
10. String()函数
11. 除法运算符%
12. parseInt
13. Array.prototype
14. if语句
15. 对象比较问题
16. +是字符串连接符也是加法运算
17. 加减运算和正负运算
18. 还是稀疏数组
19. argument对象
20. JavaScript中的最大数
21. Array.property.reverse
22. Number.MIN_VALUE
23. 强制转换
24. 数组字面量的字符串表示
25. 3.和.3
26. var和闭包问题
27. 正则表达式不可相互比较
28. 数组比较
29. 构造函数的原型
30. 函数的原型对象
31. Function.name
32. str.replace
33. eval函数
34. exp.test
35. 数组元素最后一个逗号
36. class关键字
37. 时间转换问题
38. 函数的形参个数
39. 还是时间转换问题
40. Math.max()&Math.min()
41. 正则表达式的“记忆”
42. Date中的month
43. 正则表达式中的转义
44. 变量提升  

 所以本题的答案是true。

14. if语句

var a = [0];
if ([0]) {
  console.log(a == true);
} else {
  console.log("wut");
} 

上面这个语句得到什么?

在JavaScript中if语句比较特殊,引用MDN中的介绍:

任何一个值,只要它不是 undefinednull、 0NaN或空字符串(""),那么无论是任何对象,即使是值为假的Boolean对象,在条件语句中都为真。

这个特性给我们写if语句带来很大的便利,不需要考虑if语句中的变量类型,因为只要它不是上述的几种“没有意义”的值,判断都能通过,都能执行if语句。

但是非严格相等就不是这回事了,用==比较一个数组和true肯定得到false,所以本题结果是输出false。

15. 对象比较问题

[]==[] 

上面语句的输出结果是什么?

这个考察的是对象比较问题,[]是一个空数组,数组属于对象,对象比较无论如何比较都不相等。如下图:

44道JavaScript送命题
1. map&parseInt传参
2. typeof和instanceof
3.reduce&Math.pow传参
4. 优先级
5.变量提升问题
6. JavaScript能表示的最大数
7. 稀疏数组问题
 8. 数字精度问题
9. 字面量问题
10. String()函数
11. 除法运算符%
12. parseInt
13. Array.prototype
14. if语句
15. 对象比较问题
16. +是字符串连接符也是加法运算
17. 加减运算和正负运算
18. 还是稀疏数组
19. argument对象
20. JavaScript中的最大数
21. Array.property.reverse
22. Number.MIN_VALUE
23. 强制转换
24. 数组字面量的字符串表示
25. 3.和.3
26. var和闭包问题
27. 正则表达式不可相互比较
28. 数组比较
29. 构造函数的原型
30. 函数的原型对象
31. Function.name
32. str.replace
33. eval函数
34. exp.test
35. 数组元素最后一个逗号
36. class关键字
37. 时间转换问题
38. 函数的形参个数
39. 还是时间转换问题
40. Math.max()&Math.min()
41. 正则表达式的“记忆”
42. Date中的month
43. 正则表达式中的转义
44. 变量提升 

 上面比较对象(复杂数据)使用普通相等符号比较得到的结果都是false,所以本题结果是false。

16. +是字符串连接符也是加法运算

console.log('5' + 3);
console.log('5' - 3); 

加号遇到字符串的时候就是字符串连接符,会调用toString方法把另一个非字符串转换成字符串,然后来连接,所以第一个结果是字符串‘53’;第二个是减号,它的行为和加号刚好相反,它是把字符串转换成整形,第二个输出2。

所以本题输出['53', 2]。

17. 加减运算和正负运算

console.log(1 + - + + + - + 1); 

上面表达式输出什么结果?

这个要搞明白这个表达式其实是1 + (-+++-+1),除了第一个+是加法,后面的+,-都是正负运算,根据正正得正,正负得负,负负得正的原则-+++-+1是1,所以最后结果得到2。

18. 还是稀疏数组

var ary = Array(3);
ary[0]=2
let s = ary.map(function(elem) { return '1'; });
console.log(s); 

上面语句输出什么?

这又是考察稀疏数组问题,map会忽略调稀疏数组中的空元素,输出结果是["1", empty × 2]。

19. argument对象

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

上面语句输出什么?

这一题考察的是对argument对象的了解,argument是一个类数组对象,修改对象的属性值会影响其他使用到对象的地方,即使变量不在同一范围内。再加上对象属性可以使用类似数组下标的方式来访问,对象做了字符串到值的映射,而数组做的是数字到值的映射。

根据这些可以推导结果是21。

20. JavaScript中的最大数

var a = 111111111111111110000,
      b = 1111;
console.log(a + b); 

上面的语句输出什么内容?

这个又一次考察JavaScript中的最大值问题,JavaScript中最大值是Math.pow(2, 53)=9007199254740992,计算过程中如果超出这个最大值范围就不准确了,但是怎么个不准确法,是不确定的。这里输出结果是111111111111111110000, 还是a的值。

21. Array.property.reverse

var x = [].reverse;
x(); 

上面语句输出什么?

这个有些奇怪了,原文中解释说reverse方法会返回调用这个方法的数组本身(就是this),但是x()没有调用者,所以this指向了全局对象window。但是我在chrome中试过,这个是会报错的,报错信息是:Uncaught TypeError: Cannot convert undefined or null to object at reverse (<anonymous>)。这个可能是原文写的比较早,后面的浏览器修改了这个行为。

22. Number.MIN_VALUE

console.log(Number.MIN_VALUE > 0); 

这个考察Number.MIIN_VALUE的值,MDN上解释如下:

Number.MIN_VALUE表示最小正数,即最接近 0 的正数 (实际上不会变成 0)。最大的负数是 -MIN_VALUE

所以Number.MIN_VALUE是大于0的,这里输出是true。

23. 强制转换

console.log([1 < 2 < 3, 3 < 2 < 1]); 

上面表达式输出什么?

这里考察的是在大于号,小于号运算中true会被强制转换为1false会被强制转换成0。相当于console.log([true < 3, false < 1]),转换后就是console.log([1 < 3, 0 < 1]),所以这里输出结果是[true, true]。

24. 数组字面量的字符串表示

console.log(2 == [[[2]]]); 

上面语句输出什么?

这个题套路深,我先说答案,是true。连原文作者都惊叹这是什么鬼?原文是:the most classic wtf!如果试着解释的话,非严格相等==在遇到字面量[[[2]]]的时候,会试图将它转换成字符串然后和2比较,但是[[[2]]].toString()返回‘2’,然后‘2’ == 2返回true,是不是很惊喜?是不是想骂人?如下图,这样非严格相等就返回true了。

44道JavaScript送命题
1. map&parseInt传参
2. typeof和instanceof
3.reduce&Math.pow传参
4. 优先级
5.变量提升问题
6. JavaScript能表示的最大数
7. 稀疏数组问题
 8. 数字精度问题
9. 字面量问题
10. String()函数
11. 除法运算符%
12. parseInt
13. Array.prototype
14. if语句
15. 对象比较问题
16. +是字符串连接符也是加法运算
17. 加减运算和正负运算
18. 还是稀疏数组
19. argument对象
20. JavaScript中的最大数
21. Array.property.reverse
22. Number.MIN_VALUE
23. 强制转换
24. 数组字面量的字符串表示
25. 3.和.3
26. var和闭包问题
27. 正则表达式不可相互比较
28. 数组比较
29. 构造函数的原型
30. 函数的原型对象
31. Function.name
32. str.replace
33. eval函数
34. exp.test
35. 数组元素最后一个逗号
36. class关键字
37. 时间转换问题
38. 函数的形参个数
39. 还是时间转换问题
40. Math.max()&Math.min()
41. 正则表达式的“记忆”
42. Date中的month
43. 正则表达式中的转义
44. 变量提升 

25. 3.和.3

console.log(3.toString());
console.log(3..toString());
console.log(3...toString()); 

上面语句输出什么结果?

这里要搞清楚3..3都是合法的数字3.是一个省略了尾数部分0的数字,.3是一个省略了整数部分0的数字;第一句中但是toString()不是一个数字,所以第一句报错:Uncaught SyntaxError: Invalid or unexpected token。第二句相当于(3.).toString()输出”3“。第三句和第一句一样报错,原因也是一样的,第二个.后面的一串..toString()不是一个合法的数字,于是就报错了。

所以正确的答案是error,”3“,error。

26. var和闭包问题

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

上面的语句输出什么?

这一题考察的是对闭包和var变量的了解。闭包有隔离变量的作用,所以var不能提升变量,在闭包外部访问x是失败的,所以第二句输出Undefined。闭包中如果不使用var声明变量,直接不带var,这样申明y=1,反而y是全局的,在外部是可以访问到变量y的,所以第一句输出1。是不是很惊喜?

27. 正则表达式不可相互比较

var a = /123/, b = /123/;
console.log(a == b);
console.log(a === b); 

上面两句分别输出什么?

这个考察的是正则表达式比较问题,虽然字面量内容相同,但是JavaScript认为这是两个正则表达式,是对象类型,他们是不相等的。这里输出结果为false false。

28. 数组比较

    var a = [1, 2, 3], b = [1, 2, 3], c = [1, 2, 4];
    console.log(a ==  b);
    console.log(a === b);
    console.log(a > c);
    console.log(a < c); 

上面比较以此输出什么?

即使数组字面量相等,使用==或者===判断两个数组也不相等。而大于,小于比较是按照字典顺序比较的,这里以a<c具体如下:

  • 比较a[0]<c[0],相等,继续比较下一位
  • 比较a[1]<c[1],相等,继续比较下一位
  • 比较a[2]<c[2],返回true,于是a<c返回true

所以上面以此输出false,false,false,true。

29. 构造函数的原型

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

上面代码输出什么结果?

JavaScript中函数才有prototype属性,对象是没有的,对象有__proto__,指向对象的构造函数的原型,所以a.prototype是undefined。b是Object函数的原型,是一个对象,所以第一个是false。第二个是使用getPrototypeOf方法获取对象a的原型(即a.__proto__),这个和Object.prototype相等的,所以第二个表达式返回true。

a.__proto__是对象的原型,它是浏览器暴露出来的属性,JavaScript标准中没有这个属性。下面的语句输出[true]:

var a = {}, b = Object.prototype
console.log([a.__proto__ === b); 

如下图:

44道JavaScript送命题
1. map&parseInt传参
2. typeof和instanceof
3.reduce&Math.pow传参
4. 优先级
5.变量提升问题
6. JavaScript能表示的最大数
7. 稀疏数组问题
 8. 数字精度问题
9. 字面量问题
10. String()函数
11. 除法运算符%
12. parseInt
13. Array.prototype
14. if语句
15. 对象比较问题
16. +是字符串连接符也是加法运算
17. 加减运算和正负运算
18. 还是稀疏数组
19. argument对象
20. JavaScript中的最大数
21. Array.property.reverse
22. Number.MIN_VALUE
23. 强制转换
24. 数组字面量的字符串表示
25. 3.和.3
26. var和闭包问题
27. 正则表达式不可相互比较
28. 数组比较
29. 构造函数的原型
30. 函数的原型对象
31. Function.name
32. str.replace
33. eval函数
34. exp.test
35. 数组元素最后一个逗号
36. class关键字
37. 时间转换问题
38. 函数的形参个数
39. 还是时间转换问题
40. Math.max()&Math.min()
41. 正则表达式的“记忆”
42. Date中的month
43. 正则表达式中的转义
44. 变量提升 

30. 函数的原型对象

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

上面的语句输出什么?

这还是考察函数原型问题。这f.prototype是获取函数f的原型对象,Object.getPrototypeOf(f)是获取对象构造函数的原型,注意函数也是对象,它的构造函数是Function(),因此f的构造函数的原型是Function.property,因此这这里输出false。

31. Function.name

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

上面的语句输出什么结果?

这道题考察的是函数的name属性,function.name 属性返回函数实例的名称这个属性是不可写的,因为访问器属性中writable属性为false。所以这一题输出的是['foo', 'foo']。

32. str.replace

console.log("1 2 3".replace(/d/g, parseInt)); 

上面语句输出什么内容?这一题考察的是replace方法,str.replace方法的原型如下:

str.replace(regexp|substr, newSubStr|function

参数解释如下:

  • regexp (pattern):一个正则表达式对象(即RegExp对象)或者其字面量。该正则所匹配的内容会被第二个参数的返回值替换掉。它和substr是二选一的。
  • substr (pattern):一个将被newSubStr替换的字符串。其被视为一整个字符串,而不是一个正则表达式。仅第一个匹配项会被替换。它和regexp是二选一的。
  • newSubStr (replacement):用于替换掉第一个参数在原字符串中的匹配部分的字符串。该字符串中可以内插一些特殊的变量名。参考下面的使用字符串作为参数。它和function是二选一的。
    变量名 代表的值
    $$ 插入一个 "$"。
    $& 插入匹配的子串。
    $` 插入当前匹配的子串左边的内容。
    $' 插入当前匹配的子串右边的内容。
    $n 假如第一个参数是 RegExp对象,并且 n 是个小于100的非负整数,那么插入第 n 个括号匹配的字符串。提示:索引是从1开始
  • function (replacement):一个用来创建新子字符串的函数,该函数的返回值将替换掉第一个参数匹配到的结果。参考下面的指定一个函数作为参数。它和newSubStr是二选一的。
    变量名 代表的值
    match 匹配的子串。(对应于上述的$&。)
    p1,p2, ... 假如replace()方法的第一个参数是一个RegExp 对象,则代表第n个括号匹配的字符串。(对应于上述的$1,$2等。)例如,如果是用 /(a+)(+)/ 这个来匹配,p1 就是匹配的 a+,p2 就是匹配的 +。
    offset 匹配到的子字符串在原字符串中的偏移量。(比如,如果原字符串是 'abcd',匹配到的子字符串是 'bc',那么这个参数将会是 1)
    string 被匹配的原字符串。
    NamedCaptureGroup 命名捕获组匹配的对象

不得不说replace方法有点复杂,在这个例子中第一个参数是一个正则表达式,第二个参数是一个函数,函数参数以此是match,offset,string,NamedCaptureGroup,但是parseInt仅仅接受2个参数,所以相当于执行下面的语句:

parseInt('1', 0) // 10进制里的1是1
parseInt('2', 2) // 2进制里没有2,所以NaN
parseInt('3', 4) // 4进制中的3是3

所以最终结果是"1 NaN 3",这个作者很喜欢拿parseInt说事。

33. eval函数

function f() {}
var parent = Object.getPrototypeOf(f);
console.log(f.name);
console.log(parent.name);
console.log(typeof eval(f.name));
console.log(typeof eval(parent.name));

上面的语句输出什么?

第一句f.name就是”f“;parent是Function.prototype,这是一个对象,它没有name属性,所以第二句应该是啥都不输出;eval()函数会将传入的字符串当做 JavaScript 代码进行执行,eval(f.name)相当于eval('f'),执行结果是输出函数f的内容,type of计算返回function;最后一句返回空;

34. exp.test

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

上面语句输出什么内容?

RegExp.prototype.test()方法的参数是一个字符串,如果不是字符串会尝试转换成字符串。所以上面两句相当于lowerCaseOnly.test("null"),lowerCaseOnly.test(”Undefined“),所以返回[true, true]

35. 数组元素最后一个逗号

console.log([,,,].join(", "));

上面的表达式输出什么内容?

我们定义一个有三个元素的数组的时候可以这样var arr = [1, 2, 3]; 也可以这样var arr = [1, 2, 3,];它后面了一个逗号,但是还是三个元素。本题的关键点就是[, , ,],这其实是一个有三个空元素的数组,输出 [empty × 3]。三个空元素用逗号连接起来最后输出是两个逗号,因为都是空元素,其实最后一个逗号后面是有一个空元素的。所以本题输出",,"。如下图:

44道JavaScript送命题
1. map&parseInt传参
2. typeof和instanceof
3.reduce&Math.pow传参
4. 优先级
5.变量提升问题
6. JavaScript能表示的最大数
7. 稀疏数组问题
 8. 数字精度问题
9. 字面量问题
10. String()函数
11. 除法运算符%
12. parseInt
13. Array.prototype
14. if语句
15. 对象比较问题
16. +是字符串连接符也是加法运算
17. 加减运算和正负运算
18. 还是稀疏数组
19. argument对象
20. JavaScript中的最大数
21. Array.property.reverse
22. Number.MIN_VALUE
23. 强制转换
24. 数组字面量的字符串表示
25. 3.和.3
26. var和闭包问题
27. 正则表达式不可相互比较
28. 数组比较
29. 构造函数的原型
30. 函数的原型对象
31. Function.name
32. str.replace
33. eval函数
34. exp.test
35. 数组元素最后一个逗号
36. class关键字
37. 时间转换问题
38. 函数的形参个数
39. 还是时间转换问题
40. Math.max()&Math.min()
41. 正则表达式的“记忆”
42. Date中的month
43. 正则表达式中的转义
44. 变量提升 

其实定义一个对象也可以在最后一个属性后面加上一个逗号,例如 var obj = { name: 'zhangsan', age: 20, };

36. class关键字

var a = {class: "Animal", name: 'Fido'};
console.log(a.class); 

上面的语句输出什么?

这一题是考察class关键字,我在chrome里输出的是正确的值“Animal”,记住在定义变量或者属性名字的时候尽量避免JavaScript关键字。如果属性里有关键字,可以这样使用a["class"]

37. 时间转换问题

var a = new Date("epoch")
console.log(a); 

上面的表达式输出什么?

new Date()构造函数传入的必须是一个时间字符串,即可以通过Date.parse()解析成功。所以本题输出Invalid Date。

38. 函数的形参个数

var a = Function.length,
    b = new Function().length
console.log(a === b); 

上面表达式输出什么?

Function 构造器本身也是个Function。他的length属性值为 1,所以a=1。该属性 Writable: false, Enumerable: false, Configurable: true。但是new Function()是一个对象,他没有形参,说以b=0,本题输出false。另外函数的实参的个数可以用argument.length获取。

39. 还是时间转换问题

var a = Date(0);
var b = new Date(0);
var c = new Date();
console.log([a === b, b === c, a === c]); 

上面的语句输出什么?

直接调用函数Date(),得到的结果是一个表示时间的字符串;使用new操作符+构造函数,得到的是一个时间对象;参数是0返回的是格林威治0时,不带参数返回的是当前时间;搞清楚这些之后这题就简单了,a是一个时间字符串,b是一个时间对象,表示格林威治0时,c也是一个时间对象,不过是当前时间。所以本题输出[false, false, false]

40. Math.max()&Math.min()

var min = Math.min(), max = Math.max()
console.log(min < max); 

上面语句输出什么?

注意Max.max()和Max.min()传入的参数是一个数组,如果不传参,前者返回+Infinity,后者返回-Infinity。Infinity不能做比较,所以本题返回false。

41. 正则表达式的“记忆”

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");
console.log([a1 === a2, a3 === a4]);

上面表达式输出什么?这题考察的是正则表达式的/g选项,这个选项表示全局匹配;找到所有匹配,而不是在第一个匹配后停止。来看下面的例子:

var myRe = /ab*/g;
var str = 'abbcdefabh';
console.log(myRe.exec(str));
console.log(myRe.exec(str)); 

输出结果如下:

44道JavaScript送命题
1. map&parseInt传参
2. typeof和instanceof
3.reduce&Math.pow传参
4. 优先级
5.变量提升问题
6. JavaScript能表示的最大数
7. 稀疏数组问题
 8. 数字精度问题
9. 字面量问题
10. String()函数
11. 除法运算符%
12. parseInt
13. Array.prototype
14. if语句
15. 对象比较问题
16. +是字符串连接符也是加法运算
17. 加减运算和正负运算
18. 还是稀疏数组
19. argument对象
20. JavaScript中的最大数
21. Array.property.reverse
22. Number.MIN_VALUE
23. 强制转换
24. 数组字面量的字符串表示
25. 3.和.3
26. var和闭包问题
27. 正则表达式不可相互比较
28. 数组比较
29. 构造函数的原型
30. 函数的原型对象
31. Function.name
32. str.replace
33. eval函数
34. exp.test
35. 数组元素最后一个逗号
36. class关键字
37. 时间转换问题
38. 函数的形参个数
39. 还是时间转换问题
40. Math.max()&Math.min()
41. 正则表达式的“记忆”
42. Date中的month
43. 正则表达式中的转义
44. 变量提升 

从结果可以看出,执行两次都有返回结果,分别找到字符串中两处符合条件的匹配。

而原题中第一个正在有/g选项,第一次匹配成功之后会从当前位置往后查找,所以在执行a3 = captureOne(numRe,  "NUM=2")的时候不是从字符串位置0开始查找的,而是从第5位开始,所有匹配就失败了,找不出1。

所以本题输出[true, false]。

42. Date中的month

var a = new Date("2014-03-19"),
    b = new Date(2014, 3, 19);
console.log([a.getDate() === b.getDate(), a.getMonth() === b.getMonth()]); 

上面表达式输出什么?这个问题考察的是Date对象中的月份问题,注意使用第二种方式定义时间对象,

new Date(year, monthIndex [, day [, hours [, minutes [, seconds [, milliseconds]]]]]); 

第二个参数是monthIndex,它是从0开始的,3表示4月份,所以本题输出[false, false]。如果初学JavaScript,感觉套路深啊套路深。

43. 正则表达式中的转义

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

String.prototype.match 接受一个正则, 如果不是, 按照 new RegExp(obj) 转化. 所以 . 并不会转义,所以开头的‘http://gifwrapped......’中/gif就匹配了 /.gif/,所以输出“a gif file”

44. 变量提升

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

上面语句输出什么?a作为参数其实已经声明了, 所以 var a; var a = 'bye' 其实就是 a; a ='bye',所以本题输出["hello", "bye"]。

JavaScript语法本来就有很多不合理的地方,导致书写JavaScript很容易出错,本文中如有错误,欢迎各位看官提出来。