令人困惑的JavaScript

前言

今天看了一个网站,有博主将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(),则完全没有问题。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值