44个 Javascript题附带解析

原题来自: javascript-puzzlersjavascript

你们能够先去作一下感觉感觉. 当初个人成绩是 19/44...java

第 1 题

["1", "2", "3"].map(parseInt);
复制代码

首先, map接受两个参数, 一个回调函数 callback, 一个回调函数的this值,其中回调函数接受三个参数 currentValue, index, array;而题目中, map 只传入了回调函数--parseInt.其次, parseInt 只接受两个两个参数 string, radix(基数).正则表达式

  • 在没有指定基数,或者基数为 0 的状况下,JavaScript 做以下处理:
  • 若是字符串 string 以"0x"或者"0X"开头, 则基数是 16 (16 进制).
  • 若是字符串 string 以"0"开头, 基数是 8(八进制)或者 10(十进制),那么具体是哪一个基数由实现环境决- 定。ECMAScript 5 规定使用 10,可是并非全部的浏览器都遵循这个规定。所以,永远都要明确给出radix参数的值。
  • 若是字符串 string 以其它任何值开头,则基数是 10 (十进制)。
parseInt('1', 0);
  parseInt('2', 1);
  parseInt('3', 2);
复制代码

因此答案是 [1, NaN, NaN]chrome

第 2 题

[typeof null, null instanceof Object];
复制代码

typeof 返回一个表示类型的字符串。 instanceofinstanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上。 由于 typeof null === 'object' 自语言之初就是这样....数组

typeof 的结果请看下表:浏览器

type          // result
  undefined     // "undefined"
  null          // "object"
  Boolean       // "boolean"
  Number        // "number"
  String        // "string"
  Symbol        // "symbol"
  Host object   // Implementation-dependent
  Function      // "function"
  Object        // "object"
复制代码

因此答案 [object, false]app

第 3 题

[[3, 2, 1].reduce(Math.pow), [].reduce(Math.pow)];
复制代码
arr.reduce(callback[, initialValue])
复制代码

reduce接受两个参数, 一个回调, 一个初始值. 回调函数接受四个参数 previousValue, currentValue, currentIndex, array 须要注意的是 若是数组为空且未提供初始值,则将引起类型错误。 因此第二个表达式会报异常. 第一个表达式等价于 Math.pow(3, 2) => 9; Math.pow(9, 1) =>9函数

答案 an errorpost

第 4 题

var val = "smtg";
console.log("Value is " + (val === "smtg") ? "Something" : "Nothing");
复制代码

简而言之 + 的优先级 大于 ?学习

因此原题等价于 'Value is true' ? 'Somthing' : 'Nonthing' 而不是 'Value is' + (true ? 'Something' : 'Nonthing')

答案 'Something'

第 5 题

var name = "World!";
(function() {
  if (typeof name === "undefined") {
    var name = "Jack";
    console.log("Goodbye " + name);
  } else {
    console.log("Hello " + name);
  }
})();
复制代码

javascript做用域中的变量提高。变量提高是 JavaScript 将声明移至做用域 scope (全局域或者当前函数做用域) 顶部的行为。

这题目至关于

var name = "World!";
(function() {
  var name; // 如今的name是undefined
  if (typeof name === "undefined") {
    name = "Jack";
    console.log("Goodbye " + name);
  } else {
    console.log("Hello " + name);
  }
})();
复制代码

答案是 Goodbye Jack

第 6 题

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

js 中能够表示的最大整数不是 2 的 53 次方,而是 1.7976931348623157e+308。 2 的 53 次方不是 js 能表示的最大整数而应该是能正确计算且不失精度的最大整数,能够参见 js 权威指南。 9007199254740992 +1 仍是 9007199254740992 ,这就是由于精度问题,若是 9007199254740992 +11 或者 9007199254740992 +111 的话,值是会发生改变的,只是这时候计算的结果不是正确的值,就是由于精度丢失的问题。

它进入无限循环

答案是 other

第 7 题

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

答案是 []

咱们来看一下 Array.prototype.filter 的 polyfill:

if (!Array.prototype.filter) {
  Array.prototype.filter = function(fun /*, thisArg*/) {
 "use strict";
    if (this === void 0 || this === null) {
      throw new TypeError();
    }
    var t = Object(this);
    var len = t.length >>> 0;
    if (typeof fun !== "function") {
      throw new TypeError();
    }
    var res = [];
    var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
    for (var i = 0; i < len; i++) {
      if (i in t) {
        // 注意这里!!!
        var val = t[i];
        if (fun.call(thisArg, val, i, t)) {
          res.push(val);
        }
      }
    }

    return res;
  };
}
复制代码

咱们看到在迭代这个数组的时候, 首先检查了这个索引值是否是数组的一个属性, 那么咱们测试一下.

0 in ary; => true
3 in ary; => false
10 in ary; => true
复制代码

也就是说 从 3 - 9 都是没有初始化的'坑'!, 这些索引并不存在与数组中. 在 array 的函数调用的时候是会跳过这些'坑'的.

第 8 题

var two = 0.2;
var one = 0.1;
var eight = 0.8;
var six = (0.6)[(two - one == one, eight - six == two)];
复制代码

IEEE 754 标准中的浮点数并不能精确地表达小数

那何时精准, 何时不经准呢? 笔者也不知道...

答案 [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"));

// switch 是严格比较, String 实例和 字符串不同.

var s_prim = "foo";
var s_obj = new String(s_prim);

console.log(typeof s_prim); // "string"
console.log(typeof s_obj); // "object"
console.log(s_prim === s_obj); // false
复制代码

字符串字面量和直接调用String()方法(不使用new调用构造函数)的结果是原始字符串。JS自动回转化原始字符串到String对象。因此能够在原始字符串上使用用String对象的方法。而在上下文中,在原始字符串的方法被调用或者从其中获取属性时,JS会自动包裹原始字符串而后调用方法或者获取属性。

答案是 Do not know!

第 10 题

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(x) does not create an object but does return a string, i.e. typeof String(1) === "string"

仍是刚才的知识点, 只不过 String 不只是个构造函数 直接调用返回一个字符串哦.

答案 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);
复制代码
7 % 2 => 1
4 % 2 => 0
'13' % 2 => 1
-9 % % 2 => -1
Infinity % 2 => NaN
复制代码

须要注意的是 余数的正负号随第一个操做数.

答案 [true, true, true, false, false]

第 12 题

parseInt(3, 8);
parseInt(3, 2);
parseInt(3, 0);
复制代码

第一个值为3没什么好说的。若是出现的数字不符合后面输入的进制,则为NaN,因此第二个值为NaN。而radix为0时的状况第一题下面有介绍,这里也是同样为默认10

答案 3, NaN, 3

第 13 题

Array.isArray(Array.prototype);
复制代码

一个不为人知的实事: Array.prototype => [];

答案: true

第 14 题

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

解析:

  • Boolean([0]) === true
    • [0] == true
    • true 转换为数字 => 1
    • [0] 转化为数字失败, 转化为字符串 '0', 转化成数字 => 0
    • 0 !== 1 a自己是一个长度为1的数组,而当数组不为空时,其转换成bool值为true。

而==左右的转换,会使用若是一个操做值为布尔值,则在比较以前先将其转换为数值的规则来转换,Number([0]),也就是0,因而变成了0 == true,结果天然是false,因此最终结果为B

答案:false

第 15 题

[] == [];
复制代码

[] 是Object, 两个 Object 不相等

当==运算符当左右都是对象时,则会比较其是否指向同一个对象。而每次调用字面量建立,都会创造新的对象,也就是会开辟新的内存区域。因此指针的值天然不同

答案是 false

第 16 题

"5" + 3;
"5" - 3;
复制代码

+ 用来表示两个数的和或者字符串拼接, -表示两数之差.

- 会尽量的将两个操做数变成数字, 而 + 若是两边不都是数字, 那么就是字符串拼接.

答案是 '53', 2

第 17 题

var arr = Array(3);
arr[0] = 2;
arr.map(function(elem) {
  return "1";
});
复制代码

稀疏数组. 同第 7 题.

题目中的数组实际上是一个长度为3, 可是没有内容的数组, array 上的操做会跳过这些未初始化的'坑'.

因此答案是 ["1", undefined × 2]

第 18 题

const a = {};
const b = { key: "b" };
const c = { key: "c" };

a[b] = 123;
a[c] = 456;

console.log(a[b]);
复制代码

对象键自动转换为字符串。咱们试图将一个对象设置为对象 a 的键,其值为123

可是,当对象自动转换为字符串化时,它变成了[Object object]。因此咱们在这里说的a["Object object"] = 123是。而后,咱们能够尝试再次作一样的事情。 c 对象一样会发生隐式类型转换。那么,a["Object object"] = 456

而后,咱们打印a[b],它其实是a["Object object"]。咱们将其设置为456,所以返回456

答案: 456

第 19 题

function sidEffecting(ary) {
  ary[0] = ary[2];
}
function bar(a, b, c) {
  c = 10;
  sidEffecting(arguments);
  return a + b + c;
}
bar(1, 1, 1);
复制代码

这是一个大坑, 尤为是涉及到 ES6 语法的时候

在调用函数时,函数内部的arguments维护着传递到这个函数的参数列表。它看起来是一个数组,但实际上它只是一个有length属性的Object,不从Array.prototype继承。因此没法使用一些Array.prototype的方法。

arguments对象其内部属性以及函数形参建立getter和setter方法,所以改变形参的值会影响到arguments对象的值,反过来也是同样

也就是说 arguments 是一个 object, c 就是 arguments[2], 因此对于 c 的修改就是对 arguments[2] 的修改.

因此答案是 21.

然而!!!!!!

当函数参数涉及到 any rest parameters, any default parameters or any destructured parameters 的时候, 这个 arguments 就不在是一个 mapped arguments object 了.....

function sidEffecting(ary) {
  ary[0] = ary[2];
}
function bar(a, b, c = 3) {
  c = 10;
  sidEffecting(arguments);
  return a + b + c;
}
bar(1, 1, 1);
复制代码

答案是: 12 !!!!

第 20 题

var a = 111111111111111110000,
  b = 1111;
a + b;
复制代码

JavaScript 数字的题,与第七题考察点类似。因为 JavaScript 实际上只有一种数字形式 IEEE 754 标准的 64 位双精度浮点数,其所能表示的整数范围为-2^53~2^53(包括边界值)。这里的 111111111111111110000 已经超过了2^53 次方,因此会发生精度丢失的状况。

答案仍是 111111111111111110000

第 21 题

var x = [].reverse;
x();
复制代码

这题考查的是函数调用时的 this 和 Array.prototype.reverse 方法。

The reverse method transposes the elements of the calling array object in place, mutating the array, and returning a reference to the array.

也就是说 最后会返回这个调用者(this), 但是 x 执行的时候是上下文是全局. 那么最后返回的是 window. 关于 this 指向问题

答案是 window

第 22 题

Number.MIN_VALUE > 0;
复制代码

MIN_VALUE 属性是 JavaScript 中可表示的最小的数(接近 0 ,但不是负数),它的近似值为 5 x 10-324

有兴趣的话能够看一下这个MDN

答案是 true

列举 Number 的其它常量:

  • Number.MAX_VALUE:最大的正数
  • Number.MIN_VALUE:最小的正数
  • Number.NaN:特殊值,用来表示这不是一个数
  • Number.NEGATIVE_INFINITY:负无穷大
  • Number.POSITIVE_INFINITY:正无穷大

第 23 题

[1 < 2 < 3, 3 < 2 < 1];
复制代码

运算符的运算顺序和隐式类型转换的题,从MDN 上运算符优先级,'<'运算符顺序是从左到右,因此变成了[true < 3, false < 1]

3 > 2 && 2 > 1;
// returns true

3 > 2 > 1;
// returns false because 3 > 2 is true, and true > 1 is false
// Adding parentheses makes things clear: (3 > 2) > 1
复制代码

这个题等价于

1 < 2 => true;
 true < 3 =>  1 < 3 => true;
 3 < 2 => false;
 false < 1 => 0 < 1 => true;
复制代码

因此,这里首先经过**Number()**转换为数字而后进行比较,true 会转换成 1,而 false 转换成 0,就变成了[1 < 3, 0 < 1]

参考

答案是 [true, true]

第 24 题

// the most classic wtf
2 == [[[2]]];
复制代码

又是隐式类型转换的题,

也就是说左右两边都被转换成了字符串,而字符串都是"2"

这里首先须要对==右边的数组进行类型转换,根据如下规则(来自justjavac 的文章《「译」JavaScript 的怪癖 1:隐式类型转换》):

  1. 调用 valueOf()。若是结果是原始值(不是一个对象),则将其转换为一个数字。
  2. 不然,调用 toString() 方法。若是结果是原始值,则将其转换为一个数字。
  3. 不然,抛出一个类型错误。 第一步示例:
>3 * { valueOf: function () { return 5 } }
// 15
// 第三步示例:
>function returnObject() { return {} }
>3 * { valueOf: returnObject, toString: returnObject }

`TypeError: Cannot convert object to primitive value`
复制代码

若是把对象转换成字符串时,则转换操做的第一步和第二步的顺序会调换: 先尝试 toString() 进行转换,若是不是原始值,则再尝试使用 valueOf()。

因此右侧被使用 toString()方法转换为"2"

而后又经过 Number("2")转换为数字 2 进行比较,结果就是 true 了

答案是 true

第 25 题

3.toString()
3..toString()
3...toString()
复制代码

JavaScript 会在调用方法时对原始值进行包装,但在于这个点是小数点、仍是方法调用的点,因而乎第一个就是error了,由于 JavaScript 解释器会将其第一个点认为是小数点。

第二个则很好说通了,第一个点解释为小数点,变成了(3.0).toString(),结果就是"3"

第三个也是,第一个点为小数点,第二个是方法调用的点,可是后面接的不是一个合法的方法名,因而乎就error

若是

var a = 3;
a.toString(); // "3"
复制代码

这是由于在 js 中 1.1, 1., `.`` 都是合法的数字. 那么在解析 3.toString 的时候这个** . **究竟是属于这个数字仍是函数调用呢? 只能是数字, 由于 3.合法啊!

答案是 error, '3', error

第 26 题

(function() {
  var x = (y = 1);
})();
console.log(y);
console.log(x);
复制代码

变量提高和隐式定义全局变量的题,也是一个 JavaScript 经典的坑...

y 被赋值到全局. x 是局部变量. 因此打印 x 的时候会报 ReferenceError: x is not defined

答案是 1, error

第 27 题

var a = /123/,
  b = /123/;
a == b;
a === b;
复制代码

JavaScript 中的正则表达式依旧是对象,==运算符左右两边都是对象时,会比较他们是否指向同一个对象.

答案 false, false

第 28 题

var a = [1, 2, 3],
  b = [1, 2, 3],
  c = [1, 2, 4];
a == b;
a === b;
a > c;
a < c;
复制代码

JavaScript 中 Array 的本质也是,因此前两个的结果都是 false,

而 JavaScript 中 Array 的'>'运算符和'<'运算符的比较方式相似于字符串比较字典序

会从第一个元素开始进行比较,若是同样比较第二个,还同样就比较第三个,如此类推,因此第三个结果为 false,第四个为 true。

答案 false, false, false, true

第 29 题

var a = {},
  b = Object.prototype;
[a.prototype === b, Object.getPrototypeOf(a) === b];
复制代码

原型链的题(总会有的),考查的__proto__prototype的区别。首先要明确对象和构造函数的关系,对象在建立的时候,其__proto__会指向其构造函数的prototype属性

实例对象是没有prototype属性的,只有函数才有,因此a.prototype实际上是undefined,第一个结果为 false

Object其实是一个构造函数(typeof Object的结果为"function"),使用字面量建立对象和new Object建立对象是同样的,因此a.__proto__也就是Object.prototype,而Object.getPrototypeOf(a)与a.__proto__是同样的,因此第二个结果为true

答案 [false, true]

第 30 题

function f() {}
var a = f.prototype,
  b = Object.getPrototypeOf(f);
a === b;
复制代码

f.prototype 是使用使用 new 建立的 f 实例的原型

而 Object.getPrototypeOf 是 f 函数的原型.

a === Object.getPrototypeOf(new f()) // true
b === Function.prototype // true
复制代码

答案 false

第 31 题

function foo() {}
var oldName = foo.name;
foo.name = "bar";
[oldName, foo.name];
复制代码

考察了函数的 name 属性,使用函数定义方式时,会给 function 对象自己添加一个 name 属性,保存了函数的名称,很好理解oldName为"foo"。name 属性时只读的,不容许修改,因此foo.name = "bar";以后,foo.name仍是"foo"

答案 ['foo', 'foo']

第 32 题

"1 2 3".replace(/\d/g, parseInt);
复制代码

String.prototype.replace、正则表达式的全局匹配和 parseInt,

首先须要肯定 replace 会传给 parseInt 哪些参数。举个栗子:

"1 2 3".replace(/\d/g, function() {
  console.log(arguments);
});
//输出结果:
//["1", 0, "1 2 3"]
//["2", 2, "1 2 3"]
//["3", 4, "1 2 3"]
复制代码

一共三个:

  1. match:正则表达式被匹配到的子字符串
  2. offset:被匹配到的子字符串在原字符串中的位置
  3. string:原字符串

这样就很好理解了,又回到以前 parseInt 的问题了,结果就是 parseInt("1", 10), parseInt("2", 2), parseInt("3", 4)

答案: 1, NaN, 3

第 33 题

function f() {}
var parent = Object.getPrototypeOf(f);
f.name; // ?
parent.name; // ?
typeof eval(f.name); // ?
typeof eval(parent.name); // ?
复制代码

第一 f.name 值为"f",第三 eval("f")则会输出 f 函数,因此结果为"function"

第二在本身的浏览器测试的时候是 '', 第四是 'undefined'

第 34 题

var lowerCaseOnly = /^[a-z]+$/;
[lowerCaseOnly.test(null), lowerCaseOnly.test()];
复制代码

正则表达式的**test()**会自动将参数转换为字符串,原式就变成了[lowerCaseOnly.test("null"), lowerCaseOnly.test("undefined")]

答案:[true, true]

第 35 题

[, , ,].join(", ");
复制代码
[,,,] => [empty × 3]
复制代码

JavaScript 中使用字面量建立数组时,若是最末尾有一个逗号',',会背省略,因此这是个长度为三的稀疏数组(这是长度为三, 并无 0, 1, 2 三个属性哦)(都是 undefined);

答案: ", , "

第 36 题

var a = { class: "Animal", name: "Fido" };
a.class;
复制代码

经典坑中的一个,class 是关键字。根据浏览器的不一样,结果不一样:

chrome 的结果: "Animal"

Firefox 的结果:"Animal"

Opera 的结果:"Animal"

IE 8 以上也是: "Animal"

IE 8 及如下: 报错

因此答案不重要, 重要的是本身在取属性名称的时候尽可能避免保留字. 若是使用的话请加引号 a['class']

第 37 题

var a = new Date("epoch");
复制代码

简单来讲, 若是调用 Date 的构造函数传入一个字符串的话须要符合规范, 即知足 Date.parse 的条件.

另外须要注意的是 若是格式错误 构造函数返回的还是一个 Date 的实例 Invalid Date.

答案 Invalid Date

第 38 题

var a = Function.length,
  b = new Function().length;
a === b;
复制代码

咱们知道一个function(Function 的实例)的 length 属性就是函数签名的参数个数, 因此 b.length == 0.

另外 Function.length 定义为 1......

因此不相等......

答案 false

第 39 题

var a = Date(0);
var b = new Date(0);
var c = new Date();
[a === b, b === c, a === c];
复制代码
  • 若是不传参数等价于当前时间.
  • 若是是函数调用 返回一个字符串.

答案 [false, false, false]

第 40 题

var min = Math.min(),
  max = Math.max();
min < max;
复制代码

Math.min 的参数是 0 个或者多个。若是是多个参数很容易理解,返回参数中最小的。

若是没有参数,则返回InfinityInfinity 是 javascript 中全局对象的一个属性,在浏览器环境中就是 window 对象的一个属性,表示无穷大

Math.max() 没有传递参数时返回的是 -Infinity

所以 Math.min() 要比 Math.max() 大。

答案 false

第 41 题

function captureOne(re, str) {
  var match = re.exec(str);
  return match && match[1];
}
var numRe = /num=(\d+)/gi,
  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];
复制代码

由于第一个正则有一个 g 选项 它会‘记忆’他所匹配的内容, 等匹配后他会从上次匹配的索引继续, 而第二个正则不会

因此 a1 = '1'; a2 = '1'; a3 = null; a4 = '2'

答案: [true, false]

第 42 题

var a = new Date("2014-03-19"),
  b = new Date(2014, 03, 19);
[a.getDay() === b.getDay(), a.getMonth() === b.getMonth()];
a.getDay(); // 3
b.getDay(); // 6
a.getMonth(); // 2
b.getMonth(); // 3
复制代码

new Date("2014-03-19") 若是传入的是一个符合规则的日期字符串("2014-03-19"),获得的就是对应的标准时间对象 // Wed Mar 19 2014 08:00:00 GMT+0800 (中国标准时间)

new Date(2014, 03, 19) 若是传入的是三个,第三个识别为日期// Wed Mar 19 2014 08:00:00 GMT+0800 (中国标准时间)

答案:[false, false]

第 43 题

if ("http://giftwrapped.com/picture.jpg".match(".gif")) {
  ("a gif file");
} else {
  ("not a gif file");
}
复制代码

tring.prototype.match 接受一个正则, 若是不是, 按照 new RegExp(obj) 转化. 因此 . 并不会转义 那么 /gif 就匹配了 /.gif/

答案: 'a gif file'

第 44 题

function foo(a) {
  var a;
  return a;
}
function bar(a) {
  var a = "bye";
  return a;
}
[foo("hello"), bar("hello")];
复制代码

在两个函数里, a做为参数其实已经声明了, 因此 var a; var a = 'bye' 其实就是 a; a ='bye'

答案: ["hello", "bye"]

后言

我只是一个分享者,代码的搬运工、一个追求者、一个学习者,有不一样的意见或新的想法,提出来,咱们一块儿研究。

鲁迅曾经说过**知识遍地,拾到了就是你的**。

相似文章: 悦老板

相关文章
相关标签/搜索