❝你们好,我叫TianTian,一个爱瞎折腾,爱算法的Acmer爱好者,梳理一些比较好的JS题目,复习完仍是收获很大,分享给你们❤️javascript
❞
题目主要来自看到过的易错题,还有经典的「44道 JavaScript Puzzlers!」,出自原文连接html
「stackoverflow」 这上面有不少Questions不错的,能够好好补一补基础java
❝JS易错题暂时很长一段时间就不更新啦,TypeScript都出来了,TS真香git
➡️给个小建议,能够先看完第一题,要是没有问题的话,后面的基本上也能够跳过github
❞
开始吧👇算法
「查缺补漏」送你18道浏览器面试题(1.6W字)chrome
.
VS =
操做符优先级let a = {n : 1}; let b = a; a.x = a = {n: 2}; console.log(a.x) console.log(b.x) 复制代码
输出是什么呢?数组
真的想明白了吗?浏览器
undefined
{ n : 2}
var a = 0, b = 0; function A(a) { A = function (b) { console.log(a + b++) } console.log(a++) } A(1) A(2) 复制代码
留给大家思考,我但是第一遍就作错了(;′⌒`)
「答案 1 4」
能够好好想想,你会茅塞顿开的。
var obj = { "2" : 3, "3" : 4, "length" : 2, "splice" : Array.prototype.splice, "push" : Array.prototype.push } obj.push(1) obj.push(2) console.log(obj) 复制代码
这段代码的执行结果?
答案:Object(4) [empty × 2, 1, 2, splice: ƒ, push: ƒ]
复制代码
解释就是第一次使用push,obj对象的push方法设置obj[2] = 1,obj.length++ 解释就是第一次使用push,obj对象的push方法设置obj[3] = 2,obj.length++ 使用console.log()方法输出的时候,由于obj上有length属性和splice方法,故将其做为数组输出打印 打印时由于数组未设置下标为0和1的值,故打印的结果就是empty,主动获取obj[0] = undefined 复制代码
var b = 10; (function b(){ // 'use strict' b = 20 console.log(b) })() 复制代码
输出的结果是什么?
Function b
复制代码
- 如标题同样,非匿名自执行函数,函数名不能够修改,严格模式下会TypeError, - 非严格模式下,不报错,修改也没有用。 - 查找变量b时,当即执行函数会有内部做用域,会先去查找是否有b变量的声明,有的话,直接复制 - 确实发现具名函数Function b(){} 因此就拿来作b的值 - IIFE的函数内部没法进行复制(相似于const) 复制代码
var b = 10; (function b(){ // 'use strict' var b = 20 console.log(window.b) console.log(b) })() 复制代码
输出是多少呢?
10 20 // 访问b变量的时候,发现var b = 20;在当前做用域中找到了b变量,因而把b的值做为20 复制代码
var b = 10; (function b(){ console.log(b) b = 5 console.log(window.b) var b = 20 console.log(b) })() 复制代码
输出的结果是多少呢?
这个问题应该不难,就留给大家思考吧
var name = 'World!'; (function () { if (typeof name === 'undefined') { var name = 'Jack'; console.log('Goodbye ' + name); } else { console.log('Hello ' + name); } })(); 复制代码
在 JavaScript中, Fun 和 var 会被提高
至关于
var name = 'World!'; (function () { var name; if (typeof name === 'undefined') { name = 'Jack'; console.log('Goodbye ' + name); } else { console.log('Hello ' + name); } })(); 复制代码
巩固一下:
var str = 'World!'; (function (name) { if (typeof name === 'undefined') { var name = 'Jack'; console.log('Goodbye ' + name); } else { console.log('Hello ' + name); } })(str); 答案:Hello World 由于name已经变成函数内局部变量 复制代码
var END = Math.pow(2, 53); var START = END - 100; var count = 0; for (var i = START; i <= END; i++) { count++; } console.log(count); 复制代码
一个知识点:Infinity
在 JS 里, Math.pow(2, 53) == 9007199254740992 是能够表示的最大值. 最大值加一仍是最大值. 因此循环不会停.
复制代码
var ary = [0,1,2]; ary[10] = 10; ary.filter(function(x) { return x === undefined;}); 复制代码
执行结果如何呢?
作这个题目,你须要了解稀疏数组和密集数组
看过源码的同窗应该知道,filter源码中,会去判断数组的这个索引值是否是数组的一个属性,有兴趣的同窗能够看看我写的这篇关于数组的:[干货👍]从详细操做js数组到浅析v8中array.js
0 in ary; => true 3 in ary; => false 10 in ary; => true 也就是说 从 3 - 9 都是没有初始化的'坑'!, 这些索引并不存在与数组中. 在 array 的函数调用的时候是会跳过这些'坑'的. 复制代码
因此答案就是[]
var two = 0.2 var one = 0.1 var eight = 0.8 var six = 0.6 [two - one == one, eight - six == two] 复制代码
你认为结果是多少呢?面试遇到这个问题,应该怎么回答呢?
[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')); 复制代码
运行结果如何呢?
switch 是严格比较, String 实例和 字符串不同. 答案天然是'Do not know' 因此通常状况下,写switch语句,也建议写default 复制代码
String("A")
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')); 复制代码
运行结果呢?
答案:Case A 解析:String('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); 复制代码
运行的结果如何呢?
答案:[true, true, true, false, false] 解析:%若是不是数值会调用Number()去转化 '13' % 2 // 1 Infinity % 2 //NaN Infinity 是无穷大 -9 % 2 // -1 巩固: 9 % -2 // 1 余数的正负号随第一个操做数 复制代码
Array.isArray( Array.prototype )
复制代码
这段代码的执行结果?
答案:true 解析:Array.prototype是一个数组 数组的原型是数组,对象的原型是对象,函数的原型是函数 复制代码
[]==[]
复制代码
答案是什么呢
答案:false 解析:两个引用类型, ==比较的是引用地址 复制代码
[]== ![]
复制代码
结果是什么呢?
(1)! 的优先级高于== ,右边Boolean([])是true,取返等于 false (2)一个引用类型和一个值去比较 把引用类型转化成值类型,左边0 (3)因此 0 == false 答案是true 复制代码
'5' + 3 '5' - 3 复制代码
结果是多少呢?
答案:53 2 解析:加号有拼接功能,减号就是逻辑运算 巩固:typeof (+"1") // "number" 对非数值+—常被用来作类型转换至关于Number() 复制代码
1 + - + + + - + 1
复制代码
结果是多少呢
答案:2 解析:+-又是一元加和减操做符号,就是数学里的正负号。负负得正哈。 巩固: 一元运算符还有一个经常使用的用法就是将自执行函数的function从函数声明变成表达式。 经常使用的有 + - ~ ! void + function () { } - function () { } ~ function () { } void function () { } 复制代码
var ary = Array(3); ary[0]=2 ary.map(function(elem) { return '1'; }); 复制代码
输出结果是多少呢?
稀疏数组 题目中的数组实际上是一个长度为3, 可是没有内容的数组, array 上的操做会跳过这些未初始化的'坑'. 因此答案是 ["1", empty × 2] 复制代码
这里贴上 Array.prototype.map 的 polyfill.
Array.prototype.map = function(callback, thisArg) { var T, A, k; if (this == null) { throw new TypeError(' this is null or not defined'); } var O = Object(this); var len = O.length >>> 0; if (typeof callback !== 'function') { throw new TypeError(callback + ' is not a function'); } if (arguments.length > 1) { T = thisArg; } A = new Array(len); k = 0; while (k < len) { var kValue, mappedValue; if (k in O) { kValue = O[k]; mappedValue = callback.call(T, kValue, k, O); A[k] = mappedValue; } k++; } return A; }; 复制代码
var a = 111111111111111110000, b = 1111; a + b; 复制代码
这段代码的执行结果?
答案:11111111111111111000 解析:在JavaScript中number类型在JavaScript中以64位(8byte)来存储。 这64位中有符号位1位、指数位11位、实数位52位。 2的53次方时,是最大值。 其值为:9007199254740992(0x20000000000000)。 超过这个值的话,运算的结果就会不对. 复制代码
var a = [1, 2, 3], b = [1, 2, 3], c = [1, 2, 4] a == b a === b a > c a < c 复制代码
这段代码的执行结果?
答案:false, false, false, true 解析:相等(==)和全等(===)仍是比较引用地址 引用类型间比较大小是按照字典序比较,就是先比第一项谁大,相同再去比第二项。 复制代码
var val = 'smtg'; console.log('Value is ' + (val === 'smtg') ? 'Something' : 'Nothing'); 复制代码
这段代码的执行结果?
答案:Something 解析:字符串链接比三元运算有更高的优先级 因此原题等价于 'Value is true' ? 'Somthing' : 'Nonthing' 而不是 'Value is' + (true ? 'Something' : 'Nonthing') 巩固: 1 || fn() && fn() //1 1 || 1 ? 2 : 3 ; //2 复制代码
var a = {}, b = Object.prototype; [a.prototype === b, Object.getPrototypeOf(a) === b] 复制代码
执行结果是多少呢
答案:false, true 解析:Object 的实例是 a,a上并无prototype属性 a的__poroto__ 指向的是Object.prototype,也就是Object.getPrototypeOf(a)。a的原型对象是b 复制代码
function f() {} var a = f.prototype, b = Object.getPrototypeOf(f); a === b 复制代码
这段代码的执行结果?
答案:false 解析:a是构造函数f的原型 : {constructor: ƒ} b是实例f的原型对象 : ƒ () { [native code] } 复制代码
function foo() { } var oldName = foo.name; foo.name = "bar"; [oldName, foo.name] 复制代码
代码执行结果是什么?
答案:["foo", "foo"] 解析:函数的名字不可变. 复制代码
答案:["object", false] 解析:null表明空对象指针,因此typeof判断成一个对象。能够说JS设计上的一个BUG instanceof 实际上判断的是对象上构造函数,null是空固然不可能有构造函数 巩固:null == undefined //true null === undefined //flase 复制代码
答案:Error 解析:Math.pow (x , y) x 的 y 次幂的值 reduce(fn,total) fn (total, currentValue, currentIndex, arr) 若是一个函数不传初始值,数组第一个组默认为初始值. [3,2,1].reduce(Math.pow) Math.pow(3,2) //9 Math.pow(9,1) //9 巩固题,能够作一作: [].reduce(Math.pow) //空数组会报TypeError [1].reduce(Math.pow) //只有初始值就不会执行回调函数,直接返回1 [].reduce(Math.pow,1) //只有初始值就不会执行回调函数,直接返回1 [2].reduce(Math.pow,3) //传入初始值,执行回调函数,返回9 复制代码
"1 2 3".replace(/\d/g, parseInt) 复制代码
输出是什么呢?
答案:"1 NaN 3" 解析:replace() 回调函数的四个参数: 一、匹配项 二、与模式中的子表达式匹配的字符串 三、出现的位置 四、stringObject 自己 。 若是没有与子表达式匹配的项,第二参数为出现的位置.因此第一个参数是匹配项,第二个参数是位置 parseInt('1', 0) parseInt('2', 2) //2进制中不可能有2 parseInt('3', 4) 复制代码
function f() {} var parent = Object.getPrototypeOf(f); f.name // ? parent.name // ? typeof eval(f.name) // ? typeof eval(parent.name) // ? 复制代码
这段代码的执行结果?
答案:"f", "Empty", "function", error 解析:f的函数名就是f parent是f原型对象的名字为"" , 先计算eval(f.name) 为 f,f的数据类型是function eval(parent.name) 为undefined, "undefined" 复制代码
var a = new Date("2014-03-19"), b = new Date(2014, 03, 19); [a.getDay() === b.getDay(), a.getMonth() === b.getMonth()] 复制代码
这段代码的执行结果?
答案:[false, false] 解析: var a = new Date("2014-03-19") //可以识别这样的字符串,返回想要的日期 Wed Mar 19 2014 08:00:00 GMT+0800 (CST) b = new Date(2014, 03, 19); //参数要按照索引来 Sat Apr 19 2014 00:00:00 GMT+0800 (CST) 月是从0索引,日期是从1 getDay()是获取星期几 getMonth()是获取月份因此都不一样 巩固: [a.getDate() === b.getDate()] //true 复制代码
var a = Date(0); var b = new Date(0); var c = new Date(); [a === b, b === c, a === c] 复制代码
这段代码的执行结果?
答案:[false, false, false] 解析:当日期被做为构造函数调用时,它返回一个相对于划时代的对象(JAN 01 1970)。 当参数丢失时,它返回当前日期。当它做为函数调用时,它返回当前时间的字符串表示形式。 a是字符串 a === b // 数据类型都不一样,确定是false b是对象 b === c // 引用类型,比的是引用地址 c也是对象 a === c // 数据类型都不一样,确定是false 复制代码
var a = new Date("epoch") 复制代码
你认为结果是多少呢?
答案:Invalid Date {} 解析:您获得“无效日期”,这是一个实际的日期对象(一个日期的日期为true)。但无效。这是由于时间内部保持为一个数字,在这种状况下,它是NA。 在chrome上是undefined 正确的是格式是var d = new Date(year, month, day, hours, minutes, seconds, milliseconds); 复制代码
var a = Function.length, b = new Function().length a === b 复制代码
这段代码的执行结果是?
答案:false 解析:首先new在函数带()时运算优先级和.同样因此从左向右执行 new Function() 的函数长度为0 巩固:function fn () { var a = 1; } console.log(fn.length) //0 fn和new Function()同样 复制代码
❝要是看过往期的这篇文章[诚意满满✍]带你填一些JS容易出错的坑 就能够给我点个赞👍关注一下啦,下面的内容都是这篇文章的内容。
❞
不写回调函数的话,是按照什么排序呢?
JavaScript默认使用字典序(alphanumeric)来排序。所以结果是[1,10,2,5]
正确排序的话,应该[1,2,5,10].sort( (a,b) => a-b )
你认为输出是什么?
上面的表达式至关于'b'+'a'+ (+'a')+'a',由于(+'a')是NaN,因此:
'b'+'a'+ (+'a')+'a' = 'b'+'a'+ "NaN"+'a'='baNaNa'
这是一个经典JavaScript面试题
let res = new Array() for(var i = 0; i < 10; i++){ res.push(function(){ return console.log(i) }) } res[0]() res[1]() res[2]() 复制代码
指望输出的是0,1,2,实际上却不会。缘由就是涉及「做用域」,怎么解决呢?
res.push(console.log.bind(null, i))
复制代码
解法还有其余的,好比使用IIFE,造成私有做用域等等作法。
function fun(n,o) { console.log(o) return { fun:function(m){ return fun(m,n); } }; } var a = fun(0); a.fun(1); a.fun(2); a.fun(3);//undefined,?,?,? var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,? var c = fun(0).fun(1); c.fun(2); c.fun(3);//undefined,?,?,? 复制代码
「留给大家思考」
var a = [0]; if (a) { console.log(a == true); } else { console.log("wut"); } 复制代码
大家以为答案是多少呢?这题涉及到隐式转换了,这个坑我本身的好好补一补
// 答案:false
「再来一道?」
function fn() { return 20; } console.log(fn + 10); // 输出结果是多少 复制代码
function fn() { return 20; } fn.toString = function() { return 10; } console.log(fn + 10); // 输出结果是多少? 复制代码
function fn() { return 20; } fn.toString = function() { return 10; } fn.valueOf = function() { return 5; } console.log(fn + 10); // 输出结果是多少? 复制代码
「说到底JS类型转换的好好补一补了」
[1<2<3,3<2<1] //[false,false] //[true,true] //[false,true] //[true,false] 复制代码
选一个吧,比较操做符,赋值运算符优先级哪一个更高呢?
面试的时候,问你这个问题,要是回答错误的话,估计面试官对基础非常怀疑!!!
问你这个题目的时候,你能够牵扯出不少问题,好比JS如何存储小数的呢?好比聊一聊二进制,好比实际开发中,遇到精度的问题,你是怎么解决的,你有什么好办法。
聊完这个,你能够牵扯出最大安全数,好比JavaScript的最大安全整数是多少,超出这个范围的话,怎么解决精度问题呢?
ES规范中新提出的BigInt解决了什么问题呢,你又发现了BigInt中哪些坑呢?
如何解决精度问题呢?
这里推荐Number-Precision库,不到1K的体积。
function sidEffecting(ary) { ary[0] = ary[2]; } function bar(a, b, c) { c = 10 sidEffecting(arguments); return a + b + c; } function demo (arg) { arg.name = 'new Name' } console.log(bar(2, 2, 2)) 复制代码
涉及到ES6语法,这题答案确定都会作是22,可是呢,稍微改变一下题目,就比较坑了….
function sidEffecting(ary) { ary[0] = ary[2]; } function bar(a, b, c = 4) { c = 10 sidEffecting(arguments); return a + b + c; } function demo (arg) { arg.name = 'new Name' } console.log(bar(2, 2, 2)) 复制代码
这个答案是多少呢?根据MDN上对argument有更加准确的定义,看argument
❝当非严格模式中的函数「有」包含剩余参数、默认参数和解构赋值,那么
❞arguments
对象中的值「不会」跟踪参数的值(反之亦然)。
找到这句话,bar函数存在默认参数,而且在非严格模式下,因此不会跟踪参数的值,天然结果就14
「请读者细细体会」
let demo1 = {class: "Animal", name: 'sheet'}; console.log(demo1.class) 复制代码
比较流氓,这个跟浏览器相关,class是保留字(如今的话,class是关键字),答案并没关系,重要的是本身在取属性名称的时候尽可能避免保留字. 若是使用的话请加引号 a['class']。
「保留字vs关键字」
我的理解的话,关键字就是有特殊含义的,不用用做变量名。好比
let class = 123; 复制代码
如今看来确定报错,那有什么须要咱们注意的呢?
let undefined = 123; 复制代码
这样子并不会报错,这个跟浏览器有点关系,这样子看来undefined不是关键字。因此为了保险起见,「建议你们在判断一个变量是否是未定义的话,尽可能使用void 0 === undefined」 颇有可能undefined会被看成是变量来赋值
「void 0 值就是undefined」
这个应该是常常碰见的题了,搞明白很简单,map函数怎么使用,parseInt函数怎么使用
关于Array数组的话,我以前写了一篇文章,从「源码角度解析大部分方法」
点进去重温一遍:[干货👍]从详细操做js数组到浅析v8中array.js
map接受两个参数,一个callback,一个this,即调用函数时this指向,其中callback回调函数是三个参数,一个currentValue,index,array;
parseInt接受两个参数:string,radix(基数)
返回NaN有两种状况
radix
小于 2
或大于 36
,或parseInt('1', 0); parseInt('2', 1); parseInt('3', 2); 复制代码
二者结合的话,结果天然很明显,[1,NaN,NaN]
Math.min() < Math.max() // false 复制代码
按照常规思路的话,应该是true,毕竟最小值应该小于最大值,可是实际状况是false
缘由:
要是面试官问这个问题,额。。。。
输出是什么?注意不是[].concat([1,2,3])
// [1,2,3] // Uncaught SyntaxError: .... // undefined 复制代码
答案是undefined,缘由是什么呢?
第一步计算[].concat,结果是Array.prototype.concat
第二步执行一个逗号操做符,逗号操做符对它的每一个操做对象求值(从左至右),而后返回最后一个操做对象的值。
>1,2,3 返回3 复制代码
第三步执行一个数组访问运算或属性访问运算
因此上面[].concat[1,2,3] 等价于Array.prototype.concat[3]
那么结果天然就是 undefined
。
//2 or -1
怎么办呢?
let realIsNaN = value => typeof value === 'number' && isNaN(value); 复制代码
先要判断类型,是由于字符串转换会先转换成数字,转换失败为 NaN。因此和 NaN 相等。
isNaN('jjjj') —> true 复制代码
第二种方法
let realIsNaN = value => value !== value; 复制代码
Number.isFinite('0') === isFinite('0') Number.isFinite(0) === isFinite('0') 复制代码
打印结果是什么,能不能具体说一说?
❝Number.isFinite()检测有穷性的值,惟一和全局isFinite()函数相比,这个方法不会强制将一个非数值的参数转换成数值,这就意味着,只有数值类型的值,且是有穷的(finite),才返回
❞true
。
天然答案就是 false,true
function Foo() { getName = function () { alert (1); }; return this; } Foo.getName = function () { alert (2);}; Foo.prototype.getName = function () { alert (3);}; var getName = function () { alert (4);}; function getName() { alert (5);} //请写出如下输出结果: Foo.getName(); getName(); Foo().getName(); getName(); new Foo.getName(); new Foo().getName(); new new Foo().getName(); 复制代码
let newList = [1,2,3].push(4) console.log(newList.push(4)) 复制代码
认为输出什么?
// Error
缘由在于Array.prototype.push()返回的是新数组的长度,因此呢4.push(5)天然Error
function foo1() { return { bar: "hello" }; } function foo2() { return { bar: "hello" }; } var a=foo1(); var b=foo2(); console.log(a) //Object {bar: "hello"} console.log(b) //underfind //仔细看就知道了 // 会在第10行加入一个`;` 复制代码
会在第10行自动加一个分号; 因此返回的就是undefined
function foo() { let a = b = 0; a++; return a; } foo(); typeof a; // => ??? typeof b; // => ??? 复制代码
上面的let a = b = 0; 等价于 window.b = 0, let a = b;
const length = 4; const numbers = []; for (var i = 0; i < length; i++);{ numbers.push(i + 1); } numbers; // => ??? 复制代码
惟一须要注意的就是for语句
后面带了;
沙雕题
加了;
,会认为for执行完,因此指定的都是空语句,最后numbers为[5]
console.log('Hello World'[4]) 复制代码
使用的就是方括号表示法获取字符串特定索引的字符,值得注意的是,IE7低版本使用的是charAt()
因此这题输出o
const name = 'TianTianUp' console.log(!typeof name === 'string') console.log(!typeof name === 'object') 复制代码
typeof name 返回的是 ’string‘, 字符串’string‘是一个truthy值。所以!typeof name 返回一个布尔值false。因此
false === ’string'
和 false === ’object‘返回false
(检测一个类型的值话,咱们应该使用 !==而不是!typeof)
const nums = [1, 2, 3, 4, 5, 6]; let firstEven; nums.forEach(n => { if (n % 2 ===0 ) { firstEven = n; return n; } }); console.log(firstEven); 复制代码
惟一须要注意的就是forEach源码是怎么写的,看过源码的都知道,forEach使用return是不能停止循环的,或者说每一次调用callback函数,终止的是当前的一次,而不是整个循环。
结果天然就是6
若是你以为这篇内容对你挺有有帮助的话:
点赞支持下吧,让更多的人也能看到这篇内容(收藏不点赞,都是耍流氓 -_-)
欢迎在留言区与我分享你的想法,也欢迎你在留言区记录你的思考过程。
以为不错的话,也能够看看往期文章:
[1.1W字]写给女朋友的秘籍-浏览器工做原理(渲染流程)篇
本文使用 mdnice 排版