前端面试题系列文章:
- 【1】备战前端实习面试之HTML篇
- 【2】备战前端实习面试之CSS篇
- 【3】备战前端实习面试之JavaScript篇
- 【4】备战前端实习面试之React篇
- 【5】备战前端实习面试之Vue篇
- 【6】备战前端实习面试之Node.js篇
- 【7】备战前端实习面试之浏览器篇
- 【8】备战前端实习面试之性能优化篇
- 【9】备战前端实习面试之计算机网络篇
- 【10】备战前端实习面试之手写代码篇
- 【11】备战前端实习面试之代码输出结果篇
输出结果
变量提升
题一
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();
分析函数表达式和函数声明的提升层级:
getName() //oaoafly
var getName = function() {
console.log('wscat')
}
getName() //wscat
function getName() {
console.log('oaoafly')
}
getName() //wscat
这道题涉及到变量提升的机制。函数声明和函数表达式都会被提升,那么是函数声明覆盖函数表达式?还是函数表达式覆盖函数声明?很多人知道是函数表达式覆盖函数声明,但其实这里是有一个条件的:当 js 代码执行到函数表达式时,它才能覆盖函数声明,否则只会提升函数声明(没有函数表达式什么事了)。
现在再来看这道题,一开始会提升函数声明,上面的代码可以看成(由于未执行到函数表达式,因此不能提升):
function getName() {
console.log('oaoafly')
}
getName() //oaoafly
var getName = function() {
console.log('wscat')
}
getName() //wscat
getName() //wscat
所以第一个 getName()
输出 oaoafly
。当执行到函数表达式后,覆盖函数声明,于是变成:
var getName = function() {
console.log('wscat')
}
getName() //oaoafly
getName() //wscat
getName() //wscat
因此后两个 getName()
都是输出 wscat
。
题二
下面继续看这道题,也是同样的道理:
var getName //变量被提升,此时为undefined
getName() //oaoafly 函数被提升 这里受函数声明的影响,虽然函数声明在最后可以被提升到最前面了
var getName = function() {
console.log('wscat')
} //函数表达式此时才开始覆盖函数声明的定义
getName() //wscat
function getName() {
console.log('oaoafly')
}
getName() //wscat 这里就执行了函数表达式的值
题三
难度升级:
function Foo() {
this.getName = function() {
console.log(3);
return {
getName: getName //这个就是第六问中涉及的构造函数的返回值问题
}
}; //这个就是第六问中涉及到的,JS构造函数公有方法和原型链方法的优先级
getName = function() {
console.log(1);
};
return this
}
Foo.getName = function() {
console.log(2);
};
Foo.prototype.getName = function() {
console.log(6);
};
var getName = function() {
console.log(4);
};
function getName() {
console.log(5);
} //答案:
Foo.getName(); //2
getName(); //4
console.log(Foo())
Foo().getName(); //1
getName(); //1
new Foo.getName(); //2
new Foo().getName(); //3
//多了一问
new Foo().getName().getName(); //3 1
new new Foo().getName(); //3
变量提升
var x = 10;
if (true) {
x = 20;
console.log(x) // error
let x;
}
变量赋值
let a = {a: 10};
let b = {b: 10};
let obj = {
a: 10
};
obj[b] = 20;
console.log(obj[a]); // 20
这道题主要考察对JS数据类型的熟练度以及对ES6中属性名表达式的理解。在倒数第二行中 obj[b] = 20
的赋值操作后,obj 其实已经变成了 {a: 10, [object Object]: 20}
,这是因为如果属性名表达式是一个对象的话,那么默认情况下会自动将对象转为字符串 [object Object]
,最后一步获取 obj[a]
时,a 本身也是一个对象,所以会被转换为获取 obj['[object Object]']
也就是说要查找 obj 中键为 [object Object]
的值,答案为上一步赋值的 20。
预编译/变量提升
题一
function test() {
console.log(b);
if (a) {
var b = 100;
}
console.log(b);
c = 234;
console.log(c);
}
var a;
test();
a = 10;
console.log(c);
题二
var foo = 1;
function bar() {
console.log(foo);
if (!foo) {
var foo = 10;
}
console.log(foo);
}
bar();
答案
undefined
10
题三
var a = 1;
function b() {
console.log(a);
a = 10;
return;
function a() { }
}
b();
console.log(a);
答案
function a() { }
1
题四
console.log(foo);
var foo = "A";
console.log(foo)
var foo = function () {
console.log("B");
}
console.log(foo);
foo();
function foo(){
console.log("C");
}
console.log(foo)
foo();
答案
ƒ foo(){
console.log("C");
}
A
ƒ () {
console.log("B");
}
B
ƒ () {
console.log("B");
}
B
题五
var foo = 1;
function bar(a) {
var a1 = a;
var a = foo;
function a() {
console.log(a);
}
a1();
}
bar(3);
答案
1
总结
预编译的题目多数情况下就可以采用以下原则:
- 函数声明,整体提升
- 变量声明,声明提升
Promise
console.log('script start')
let promise1 = new Promise(function (resolve) {
console.log('promise1')
resolve()
console.log('promise1 end')
}).then(function () {
console.log('promise2')
})
setTimeout(function(){
console.log('settimeout')
})
console.log('script end')
答案
script start
promise1
promise1 end
script end
promise2
settimeout
async/await
async function async1(){
console.log('async1 start');
await async2();
console.log('async1 end')
}
async function async2(){
console.log('async2')
}
console.log('script start');
async1();
console.log('script end')
答案
script start
async1 start
async2
script end
async1 end
事件循环
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
console.log('script start')
setTimeout(function () {
console.log('setTimeout0')
}, 0)
setTimeout(function () {
console.log('setTimeout2')
}, 300)
setImmediate(() => console.log('setImmediate'));
process.nextTick(() => console.log('nextTick1'));
async1();
process.nextTick(() => console.log('nextTick2'));
new Promise(function (resolve) {
console.log('promise1')
resolve();
console.log('promise2')
}).then(function () {
console.log('promise3')
})
console.log('script end')
- 先找到同步任务,输出
script start
- 遇到第一个
setTimeout
,将里面的回调函数放到 timer 队列中 - 遇到第二个
setTimeout
,传入的延迟时间为 300 ms,在定时器过程中只有在时间到达时才能将里面的回调函数放到 timer 队列中 - 遇到第一个
setImmediate
,它会在下一个循环迭代中触发,即把回调函数放到 check 队列中 - 遇到第一个
process.nextTick
,将其里面的回调函数放到本轮同步任务执行完毕后执行 - 执行
async1()
,输出async1 start
- 执行
async2()
,输出async2
- 遇到第二个,将其里面的回调函数放到本轮同步任务执行完毕后执行
- 遇到
new Promise
,执行里面的立即执行函数,输出promise1
、promise2
- then 里面的回调函数进入微任务队列
- 遇到同步任务,输出
script end
- 执行下一轮回到函数,先依次输出
nextTick
的函数,分别是nextTick1
、nextTick2
- 然后执行微任务队列,依次输出
async1 end
、promise3
- 执行 timer 队列,依次输出
setTimeout0
- 接着执行 check 队列,依次输出
setImmediate
- 300ms 后,timer 队列存在任务,执行输出
setTimeout2
答案:
script start
async1 start
async2
promise1
promise2
script end
nextTick1
nextTick2
async1 end
promise3
setTimeout0
setImmediate
setTimeout2
函数柯里化
var MAP = {
onclick: function () {
},
curry: function (val) {
return function (z) {
return val++ + z
}
}
}
var getInfo = function (val) {
return MAP[val]
}
var fn = getInfo('curry')
var a = fn(100)
console.log(a(200)) // 300
console.log(a(300)) // 401
console.log(fn(100)(200)) // 300
console.log(getInfo('curry')(100)(300)) // 400
深浅拷贝
var obj = {
name: 'baidu',
arr: ['a', 'b', 'c']
}
var obj2 = obj
var arr = obj.arr
obj2.arr = ['a', 'b', 'c', 'd']
obj2.name = 'inke'
console.log(arr) // ['a', 'b', 'c']
console.log(obj.name) // 'inke'
console.log(obj === obj2) // true
console.log(obj.arr === obj2.arr) // true
console.log(obj.arr === arr) //false