JS进阶篇(前端面试题整合)(三)
最近刷面试题遇到了一些比较有意思的题目,做个记录(题目均来源于牛客网,解析均是本人的理解,有任何问题欢迎在评论区指出)
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)
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