最近刷面试题遇到了一些比较有意思的题目,做个记录(题目均来源于牛客网,解析均是本人的理解,有任何问题欢迎在评论区指出)
Q1:请问以下两次检测对象constructor是否拥有属性名1的结果分别是什么?
1 in Object(1.0).constructor; Number[1] = 123; 1 in Object(1.0).constructor;
A:false、true
解析:
首先可以认识一下 "in" :用于检查对象(数组)及其原型链中是否含有某键值,如
const arr = [0,2] 1 in arr // true
这里表示的是arr这个数组中存在 1 这个键值,即arr[1]
回归正题:Object(1.0) 相当于 new Number(1.0) ,new Number(1.0).constructor就拿到了Number构造函数,此时Number上还没有 1 这个属性,所以使用 in 返回false,而下一步中增加了这个属性,所以返回true
Q2:下面这段程序的显示结果是?
var x = new Boolean(false); if (x) { alert('hi'); } var y = Boolean(0); if (y) { alert('hello'); }
A:hi
解析:
Boolean(0)返回一个布尔类型的false
而new Boolean(false)返回一个布尔对象
Q3:以下哪些对象是Javascript内置的可迭代对象?
Array Map String Object
A:Array Map String
解析:
ES6中对可迭代的定义:一种数据结构存在Symbol.iterator属性,就表示可迭代
在阮一峰的博客中还提到了以下可迭代的数据类型
- Array
- Map
- Set
- String
- TypedArray
- 函数的 arguments 对象
- NodeList 对象
Q4:以下代码执行后,输出结果为
let x = 10; let foo = () => { console.log(x); let x = 20; x++; } foo();
A:抛出ReferenceError
解析:使用let定义变量会形成 "暂时死区" 即foo作用域内获取的x就是等于20的那个,然而使用let去定义变量,变量不会得到提升,所以在定义前获取x会报错
Q5:请问以下JS代码会输出什么
var a = 10; (function a() { a = 20; console.log(a); })()
A:输出函数a的内容
解析: 立即执行函数中的a访问到的是函数a,当函数为IIFE的函数表达式时,与使用const类似(即无法修改这个值),与函数声明( function fun(){} )不相同,当执行a=20时,无法修改a的值
Q6:以下代码执行后,输出结果为
var a = 10; function a(){} console.log(typeof a)
A:输出 "number"
解析:我的理解是var会将声明提升到作用域的最高点,function会在js编译时加载在内存中
可以简单理解为:函数声明大于变量声明大于变量赋值,所以原式转换成了下面这段代码
function a(){} var a; a = 10; console.log(typeof a)
验证方式:
console.log(typeof a)// function var a = 10; console.log(typeof a)// number function a(){} console.log(typeof a)// number
Q7:以下JavaScript代码,在浏览器中运行的结果是
var foo = {n:1}; (function(foo){ console.log(foo.n); foo.n = 3; var foo = {n:2}; console.log(foo.n); })(foo); console.log(foo.n);
A:1 2 3
解析:根据var的变量提升,可以得到以下代码;
var foo = {n:1}; (function(foo){ var foo = foo // 这一步是参数(形参)的声明,赋值 var foo // 此操作优先级没有参数高,所以不生效 console.log(foo.n);// 此时foo指代参数,n为1 foo.n = 3; // 参数引用了全局的foo,所以全局的foo此时已经是3了 foo = {n:2}; // 给参数重新赋值,相当于指向了一块新内存 console.log(foo.n); // 打印新内存的值:2 })(foo); console.log(foo.n);
Q8:请问以下JS代码的输出结果以及变量i的值是?
var i = 100; function foo() { bbb: try { console.log("position1"); return i++; } finally { break bbb; } console.log("position2"); return i; } foo();
A:position1、position2、101
解析:在try中执行return不会打断finally的执行,但是没有打断i++执行,因此执行完try中的语句后,通过break bbb跳出到foo中的bbb级代码块,继续打印下面的position2
Q9:请问以下JS代码输出的结果是什么?
let obj = { num1: 117 } let res = obj;// -----------1 obj.child = obj = { num2: 935 };// -----------2 var x = y = res.child.num2;// ----------3 console.log(obj.child); console.log(res.num1); console.log(y);
A:undefined、117、935
解析:这道题主要考察两点:
1.引用类型赋值
2.连续赋值机制
下面我对1-3步做一个分析:
第1步在栈中新建res变量使其引用地址指向obj,即与其共用一个堆地址(这一步可以看做是题目中对第二步的一个伏笔);
第2步是一个连续复制,赋值从右往左看,理解为以下代码,因为在赋值时obj指向了新的堆地址,obj.child中的obj已经不是赋值后的obj了,可以把它暂时看作是res
obj = { num2: 935 }; res.child = obj;
那么此时 res 就是{ child: {num2: 935}, num1: 117 },obj 就是{ num2: 935 };
第3步,连续复制,理解为以下代码
window.y = res.child.num2; var x = window.y
此时res.child.num2是935,所以x和y都是935
Q10:在浏览器控制台中执行以下代码,输出的结果是
function test() { var n = 4399; function add(){ n++; console.log(n); } return {n:n,add:add} } var result = test();// ------1 var result2 = test();// ------2 result.add();// ------3 result.add();// ------4 console.log(result.n);// ------5 result2.add();// ------6
A:4400 4401 4399 4400
解析:这道题考察闭包和函数作用域,首先咱得理解的是通过步骤1和2,产生的result和result2是两个不同对象(两个对象指向的堆地址不是一个,虽然内容一样),其次要了解的是test函数和add函数之间产生了闭包关系,所以执行3和4步骤后,n发生了变化,然而值得注意的是,此时的result.n却没有发生变化,这是因为在test函数作用域中,n作为一个基本类型的值随着函数执行完后随着return返回出来了,此时在add中修改的n与result.n已没有关联了。为了更好理解上述说法,我修改了代码,得出以下代码:
function test() { var n = {num:4399}; function add(){ n.num++; console.log(n); } return {n:n,add:add} } var result = test(); var result2 = test(); result.add() // {num: 4400} result.add() // {num: 4401} console.log(result.n) // {num: 4401}
这样写应该会有助于理解
言归正传,执行第5步时就是打印最早传入的n(4399),第六步由于上面解析中提到的步骤1和2,产生的result和result2是两个不同对象,所以,可以理解为result2也来了一次步骤3