前言:
最近看到了一篇大厂面试题集锦,在这里摘出来一些比较有意思的面试题跟大家分享,通过这些试题的分析,加深大家对js的理解,夯实基础知识。
1.输出以下代码的执行结果
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);
考察点1:
对push的理解,push
是根据length
来决定从哪里开始插入给定的值。
我们来去除其他代码,仅保留push
来看看控制台输出
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);
输出:
{2: 1, 3: 2, length: 4, push: ƒ}
可以看到因为length
为2,所以push
是从下标为2的地方开始插入,下标为2跟3的值被push
进去的值覆盖。
接下来我们修改length
var obj = {
'2': 3,
'3': 4,
'length': 0,
// 'splice': Array.prototype.splice,
'push': Array.prototype.push
}
obj.push(1);
obj.push(2);
console.log(obj);
输出:
{0: 1, 1: 2, 2: 3, 3: 4, length: 2, push: ƒ}
可以看到如果length
改为0,那么push是从0开始插入
考察点2:
当一个对象拥有splice
函数作为属性时,控制台会以数组形式输出
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);
输出:
[empty × 2, 1, 2, splice: ƒ, push: ƒ]
虽然控制台输出的是数组形式,但是obj依然是对象类型
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);
console.log(obj instanceof Array)
console.log(obj instanceof Object)
输出:
[empty × 2, 1, 2, splice: ƒ, push: ƒ]
false
true
结合两个考察点,我们可以分析出最终结果
[empty × 2, 1, 2, splice: ƒ, push: ƒ]
2.输出以下代码的执行结果
var a = {n:1};
var b = a;
a.x = a = {n:2};
console.log(a.x);
console.log(b.x);
考察点1:
连续赋值,顺序从右向左。
考察点2:
运算符优先级,点的优先级要高于等号。更多运算符优先级请参阅运算符优先级
考察点3
引用数据类型的值是指向堆的指针。
我们先看输出
undefined
{n: 2}
解析:
var a = {n:1};
var b = a; //将b指向{n:1}
a.x = a = {n:2};
// 1. a.x 会在{n:1,x:undefined}
// 2. 将a重新指向{n:2}
// 3. 将{n:1,x:undefined}中的x指向{n:2}
// 4. 此时 b: {n:1,x:{n:2}}, a: {n:2}
console.log(a.x);
console.log(b.x);
3.输出以下代码的执行结果
var a = 10;
(function(){
console.log(a);
a=5;
console.log(window.a);
var a = 20;
console.log(a)
})()
考察点1:
变量提升,es6之前只存在全局作用域跟函数作用域,变量提升就是将变量定义提升到当前作用域的开始。
考察点2:
作用域,es6之前只存在全局作用域跟函数作用域,当赋值变量或者查找变量时,会现在当前作用域查找,如果没有找到会向上级作用域查找,直到找到全局作用域。
解析:
var a = 10;
(function(){
console.log(a);
a=5;
console.log(window.a);
var a = 20;
console.log(a)
})()
结果:
undefined 10 20
// 1. 立即执行函数内的 var a = 20; 会先进行变量提升。 var a = undefined;会提升到函数作用域顶部
// 2. 这时候console.log(a) 的a为undefined
// 3. console.log(window.a),这时候取的是window的a变量,这时候全局对象下的a变量为10
// 4. 运行到var a = 20; 这时候会在当前作用域查找a变量,找到了以后赋值为20
// 5. 这时候输出console.log(a) 这时候a的值为20
4.下面代码中a在什么情况下会打印1?
var a = ?;
if(a == 1 && a == 2 && a == 3){
console.log(1);
}
考察点1:
隐式类型转换,当进行±等运算或者==时,会进行隐式类型转换。
考察点2:
对象转为原始类型时会先调用自身valueOf方法,如果没有返回原始值,会继续调用自身的toString方法,如果还是没有返回原始类型,则抛出异常。
var a = {
i:1,
toString:function(){
console.log('toString',this.i)
return this.i++
},
valueOf:function(){
console.log('valueOf',this.i)
return this.i++
}
};
if(a == 1 && a == 2 && a == 3){
console.log(1);
}
输出:
valueOf 1
valueOf 2
valueOf 3
1
// 1. a为一个对象,在转换为数字时会调用valueOf
// 2. valueOf执行完毕以后再次转换时会继续调用valueOf
// 3. 当()内的表达式执行完毕后a内的i变为4
5.输出以下代码的执行结果
var b = 10;
(function b(){
b=20;
console.log(b)
})()
console.log(b)
考察点1:
立即执行表达式会生成一个局部作用域,起到隔离参数的作用。
考察点2:
函数提升要高于变量提升
例子:
var a = 1;
function a(){
}
console.log(a)
输出:
1
由此可见,函数的优先级要高于var变量定义。
考察点3:
具名函数表达式只能由内部访问,并且具名函数表达式的name无法重新赋值
举个例子:
var a = function b(){
console.log(b);
b=1;
console.log();
}
输出:
ƒ b(){
console.log(b);
b=1;
console.log(b)
}
ƒ b(){
console.log(b);
b=1;
console.log(b)
}
解析:
var b = 10;
(function b(){
b=20;
console.log(b)
})()
console.log(b)
输出:
ƒ b(){
b=20;
console.log(b)
}
10
// 1. 立即执行函数有两种定义方式
(1). (function(){})()
(2). (function(){}())
(3). !、,、+、-等符号 !function(){}()
// 2. 当函数被()包裹以后,这个函数就不是函数声明了,就变成了一个函数表达式
// 3. 具名函数表达式内部的b无法重新定义,所以输出的是b函数
// 4. b=20 赋值查找当前作用域,查到了具名函数表达式的name,但是无法重新赋值,查找到了不再继续向上查找
// 4. 最后的console输出的是全局变量b,所以是10
如果想获取更多内容,可以扫描下方二维码,一起学习,一起进步。