前言
今天看了一个网站,有博主将js常见的问题都列举出来。刚开始时我都还是跃跃欲试,心想自己学习js还是有很长一段时间了,应该不会有太大的问题吧!但是现实打脸来的太快,果然自己的水平还是太菜了,对于底层的原理还需要好好了解一下。该篇博文是记录了做题中自己发现的盲点以及易错点。大家也可以实际去做做看,附上网址
题目和自己的理解
1.
题目: ["1", "2", "3"].map(parseInt)
输出结果:[1, NaN, NaN]
解释:本题主要考察的是 map 的用法,给原数组中的每个元素都按顺序调用一次回调函数。回调函数每次执行后的返回值(包括 undefined)组合起来形成一个新数组。回调函数的默认参数有三个,分别是 item(当前元素),index(元素序列) ,array(原数组),且函数只会在有值的索引上被调用;那些从来没被赋过值或者使用 delete 删除的索引则不会被调用。而parseInt 只需要传入2个参数,需要转换的字符串和转换的进制,所以 parseInt 会将回调函数传入的元素序列index作为转换的进制。第一个index是0,parseInt会默认作为10进制处理,而后续的元素则会转换为NaN。所以输出结果是 : [ 1, NaN, NaN ]。
举一反三: ["1], "3", "10"].map(parseInt) 输出结果: [1, NaN, 2]
2.
题目:[typeof null, null instanceof Object]
输出结果:["object", false]
解释: 本题考察的是操作符typeof 和 运算符instanceof。
typeof返回一个字符串,表示后跟参数的类型。可能的返回值有:"undefined", "object", "boolean", "number", "string", "symbol", "function"。由此可见上面的类型,返回的是没有数组类型的,因为数组是特殊的对象,所以执行 typeof array 返回的 "object"。此外还需要注意返回类型也没有"null",执行 typeof null 返回的是"object"。
typeof Infinity === 'number';
typeof NaN === 'number';
typeof undefined === 'undefined';
typeof new Date() === 'object';
typeof [1,2,4] === 'object'; // 所以在判断数组和对象是不能使用typeof操作符
// 判断数组可以使用 Array.isArray(myObj) 或者 Object.prototype.toString.call(myObj) === "[object Array]"
typeof null === 'object'; // 从一开始JavaScript就是如此
typeof Class test{} === 'function';
typeof function () {} === 'function';
“ 在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于null代表的是空指针(大多数平台下值为 0x00),因此,null的类型标签也成为了 0,typeof null 就错误的返回了"object" ”
instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性。返回的是一个Boolean型的值。一般如果该对象是构造函数的实例的话,就没有多大的问题。但需要注意的是 instanceof 的值并不是一成不变的。
3.
题目:[ [3,2,1].reduce(Math.pow), [].reduce(Math.pow) ]
输出结果: 报错
解释:在没有初始值的空数组上调用 reduce 将报错
4.
// 题目:
var val = 'smtg';
console.log('Value is ' + (val === 'smtg') ? 'Something' : 'Nothing');
// 输出结果 :
Something
解释:本题主要考察的是运算符优先级的判定,对于三目运算符的 " ? " 前面的表达式应该看做一个整体,即 ' Value is true ' ? 'Somthing' : 'Nothing'。这样的话结果是显而易见的。
5.
// 题目:
var name = 'World!';
(function () {
if (typeof name === 'undefined') {
var name = 'Jack';
console.log('Goodbye ' + name);
} else {
console.log('Hello ' + name);
}
})();
// 输出结果: Goodbye Jack
解释:本题主要考察的就是变量作用域的问题,在执行立即函数时,其中声明的变量 name 会提升,值为 undefined,符合第一个分支条件,所以打印的字符为:' 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);
// 输出结果: 没有输出,count不会加一
解释:以上程序会陷入一个无限循环,2^53是js中可能的最高数字,2^53+1赋值给2^53,所以i永远不会变大。如果将 i <= END 改为 i < END 则输出结果是100
7.
// 题目:
var ary = [0,1,2];
ary[10] = 10;
ary.filter(function(x) { return x === undefined;});
// 输出结果: []
解释:本题考察的是 filter 方法:创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。 该方法会为数组中的每个元素调用一次 回调函数,并利用所有使得回调函数返回 true 的元素创建一个新数组。回调函数只会在已经赋值的索引上被调用,对于那些已经被删除或者从未被赋值的索引(如undefined)则不会被调用。那些没有通过回调函数测试的元素会被跳过,不会被包含在新数组中。
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'));
// 输出结果: 'Do not know!'
解释:本题需要注意的是switch...case语句内部使用的是 === 来作为分支条件的判断,以及 new String('A') 和字符串 'A'是不全等的,虽然两者的值都是'A',但前者是object类型,后者是string类型。这里还需要注意的是,String('A') === 'A',String('A')的类型也是字符串string。
12.
parseInt(3, 8) // 3
parseInt(3, 2) // NaN
parseInt(3, 0) // 传入0,js会作为一个虚假参数,默认处理为10进制
解释:本题主要考察的就是parseInt()的转换方法
13.
题目: Array.isArray( Array.prototype )
输出结果:true // 因为Array.prototype是一个数组
解释:本题考察的是原型的类型,Array.prototype是数组Array;Object.prototype是对象Object;Function.prototype是函数Function
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) // 输出结果: 21
解释:在javascript中,变量是绑定到arguments对象上,因此更改变量会更改arguments,更改参数会更改局部变量,即使它们不在同一范围内也是如此。
22.
题目: Number.MIN_VALUE > 0 //
输出结果: true // MIN.VALUE属性是javascript里面最小的正值,不是负数
解释:本题考察的是Number的 MIN.VALUE 属性,相对的 MAX.VALUE 属性也可以了解一下,表示javascript里面所能表示的最大数值,大于MAX.VALUE的是Infinity。
31.
// 题目
function foo() { }
var oldName = foo.name;
foo.name = "bar";
[oldName, foo.name]
// 输出结果:['foo','foo']
解释:函数声明的数据属性name默认是不可修改的,即writable: false 。而对象声明的数据属性则是可修改的,即writable: true
32.
题目:"1 2 3".replace(/\d/g, parseInt)
输出结果:"1 NaN 3"
解释:replace在执行的时候,会向回调函数传递每个字符的值以及字符所在的序列号,如上面的代码,传递的参数为(1,0),(2,2),(3,4)。在parseInt(2,2)的时候,因为二进制的有效数字只有0,1所以转换无效,返回NaN。
34.
// 题目
var lowerCaseOnly = /^[a-z]+$/;
[lowerCaseOnly.test(null), lowerCaseOnly.test()]
// 输出结果: [true,true]
解释:正则表达式的test方法中参数转换为带有抽象ToString操作的字符串,因此它是"null"和"undefined"。
35.
题目:[,,,].join(", ")
输出结果:", , " // JavaScript在定义数组时允许使用尾随逗号,
// 因此[,,,]的结果是一个长度为3,未初始化的数组。但最后一个逗号后面则不会统计
38.
// 题目
var a = Function.length, b = new Function().length;
a === b
// 输出结果:false
解释:Function.length被定义为1,而实例化的对象继承原型的length属性,值为0
39.
var a = Date(0); // "Sun Jan 20 2019 14:48:19 GMT+0800 (CST)"
var b = new Date(0); // Thu Jan 01 1970 08:00:00 GMT+0800 (CST)
var c = new Date(); // Sun Jan 20 2019 14:48:19 GMT+0800 (CST)
[a === b, b === c, a === c] // [false, false, false]
解释:以一个函数的形式来调用JavaScript的Date对象(即不使用 new 操作符)会返回一个代表当前日期和时间的字符串。通过调用Date构造函数来实例化日期对象。new Date()不传参数,则会默认返回当前时间对象。new Date(value),参数 value 代表自世界标准时间起经过的毫秒数,会返回距离标准时间value毫秒的日期对象。new Date(dateString) & new Date(year, month[, day[, hour[, minutes[, seconds[, milliseconds]]]]]); 则是将传入的日期字符串以及多个日期参数,转化为对应的日期对象。
40.
var min = Math.min(), max = Math.max()
min < max // false
min > max // true
解释:Math.min() 返回的是+Infinity,Math.max() 返回的是-Infinity
41.
// 题目:
var tmp = {};
var A = function() {};
A.prototype = tmp;
var a = new A(); // 此时a.__proto__已经隐式的指向temp
A.prototype = {}; // 改变A.prototype的指向,从tmp到{}
var b = Object.create(tmp); // b.__proto__为tmp
b.constructor = A.constructor;
console.log(a instanceof A);
console.log(b instanceof A);
// 运行结果: false false
42.
var a = {},
b = { key: 'b' },
c = { key: 'c' };
a[b] = 123;
a[c] = 456;
var arr = [1,2]
console.log(a[b]) // 456
console.log(a[c]) // 456
console.log(a[a]) // 456
console.log(a[arr]) // undefined 数组转换为字符是[object Array]
console.log(a) // { [object Object]:456 }
解释:对象添加属性有两种方式 第一种点语法,如 obj.a=1;第二种[]语法,如 var a = 'hello', obj[a] = 'Joe' ,此时访问obj.hello可以得到'Jeo' 。题目传入的是一个对象,所以会隐式调用对象的toString方法,把对象转化为字符串 。执行 a[b] = 123 时 , a对象里是这样的 { '[object Object]' : 123 } 。同理, a[c] = 456 , 把 [object Object] 重新赋值 。所以 a对象只有一个属性; 输出a[b] 相当于输出a的[object Object]属性,得到456; a[a]同样得到456;
43.
var number = 10;
function fn() {
console.log(this.number);
}
var obj = {
number: 2,
show: function (fn) {
this.number = 3;
fn(); // 10
var foo = arguments[0];
foo(); // 10
// 直接调用执行类数组对象中的函数时不可行的
arguments[0](); // undefined
var arr = Array.prototype.slice.call(arguments);
arr[0](); // 10
}
};
obj.show(fn);
解释:调用obj.show(fn),本质执行的是传入的参数(类型为function的fn),而fn函数的作用域是全局,所以this.number为10。而这里比较有趣的是arguments了,我们都知道arguments是函数参数的引用,arguments[0]指向传入的函数fn,但是不能直接执行 arguments[0](),如果将arguments[0]赋值给变量foo,然后执行foo(),则完全没有问题。