美文网首页Web前端之路
JavaScript Puzzlers详解

JavaScript Puzzlers详解

作者: 01_Jack | 来源:发表于2019-07-22 17:50 被阅读70次

    前言

    题目来自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!

    相关文章

      网友评论

        本文标题:JavaScript Puzzlers详解

        本文链接:https://www.haomeiwen.com/subject/hftglctx.html