这次出来面试感觉比上次16年的时候难了很多,从真正面试的题看来需要真材实料,不像之前凭半瓶醋的前端知识就可以过去拿到Offer。现在更需要了解为什么这样做,这样做有什么好处,原理等。好好学学,补充下基础、算法还是有机会的,很多的知识点可能大概了解过,但要说怎么一回事,为什么他会这样,可能就讲不太出来,这样就还需要补充学习。
面试题
今天面试时候碰到了两道笔试题,第一道凑合着答对了,但是你要说让我当时再来一次,也不一定能够答对。而且如果讲到做题的思路,很难清楚的给表达出来。
先来看下面试题:
console.log(a);
a();
var a = 3;
function a() {
console.log(10);
}
console.log(a);
a = 6;
a();
第二道面试题:
function Foo() {
getName = function() {
alert(1);
}
return this;
}
Foo.getName = function() {
alert(2);
}
Foo.prototype.getName = function() {
alert(3);
}
getName = function() {
alert(4);
}
function getName() {
alert(5);
}
Foo.getName();
getName();
Foo().getName();
getName();
Foo.getName();
new Foo().getName();
我当时的答案:2 5 3 4 2 1。
这样错误的结果不但是对new关键字记得有点混,对变量和函数重名之后的处理也是不太清晰。
解析
我们先来看第一个,
console.log(a);
a();
var a = 3;
function a() {
console.log(10);
}
console.log(a);
a = 6;
a();
首先我们可能知道var和function都会变量提升,提升到最前面,有的可能会觉得第一个console是undefind,但其实是下面的步骤。
function a() {
console.log(10);
}
var a;
// 输出function函数体
console.log(a);
// 函数执行,输出10
a();
a = 3;
console.log(a);
a = 6;
console.log(a);
a();
是不是按照这个顺序就很容易的理解了响应的输出了。第一步输出function
函数体,第二步a()输出10,第三步a=3输出3
,第四个6
,最后a()
因为a为6,要运行的就肯定报错了,a is not a function
。
第二道题
function Foo() {
getName = function() {
alert(1);
}
return this;
}
Foo.getName = function() {
alert(2);
}
Foo.prototype.getName = function() {
alert(3);
}
getName = function() {
alert(4);
}
function getName() {
alert(5);
}
Foo.getName();
getName();
Foo().getName();
getName();
Foo.getName();
new Foo().getName();
当时回答错误,第二个只知道function给提升了,但是对function 和 var的赋值顺讯没有搞好,所以以为是function getName
输出的5。第三个以为Foo()也可以实例化对象,这个就错的离谱了。后面就不说了,思路已经全乱了,再去想已经没有意义了。
我们按照顺序把上面的输出理解一下。
// Foo的静态方法,这次的应该都理解,输出2
Foo.getName();
// 这一点的易错点就是function和变量的提升赋值操作
// function getName提升在前,getName = function赋值在后,所以输出4
getName();
// Foo()返回的当前this为Window对象,而且在函数里面对getName又做了一次赋值
// 因为在函数里没有找到getName,所以是对window.getName赋值,覆盖了之前函数和变量赋值提升之后的值
// window.getName()输出1
Foo().getName();
// 因为getName已经改了,同上一步一样为1,
getName();
// 没有对静态方法做过重新赋值,所以还是2
Foo.getName();
// new Foo()实例化Foo,所以getName需要在原型上找,是3,没问题了
new Foo().getName();
看到别的还加了new Foo的操作,可以猜猜下面会输出什么?
new Foo.getName();
var foo = new Foo
foo.getName()
new 有两种用法。
第一种是普通的用法,就和上面代码 new Foo 一样。这种时候它的优先级是仅次于括号(点和括号是同级别的)。但是 new 还有另一个种用法,那就是输入构造参数的情况。比如上面代码第二个代码 new Foo(),即使构造函数的参数为空,这种情况下 new 就有括号的性质,前括号的 new 关键字,后括号是用来输入构造参数的小括号。
在 JavaScript 中,如果构造函数不带参数的话,new 的时候可以省略括号。
// 这两种写法是等价的
var f = new Foo;
var f = new Foo();
// 但是下面这两种是不同的,不能混淆了:
var f = new Foo.getName(); //new Foo.getName;
var f = new Foo().getName(); //new Foo().getName;
// 根据上面,new操作符优先级低于点操作符,而且Foo后面没有跟括号
// 先执行Foo.getName, 而Foo.getName= function() {alert(2);}
// 所以等于new function() {alert(2);}(),输出2
new Foo.getName();
// 这个正常实例化对象,就输出3
var foo = new Foo
foo.getName()
自己测试理解的时候,可以修改输出,更直观的看一下结果,方便理解。
function Foo() {
getName = function() {
// alert(1);
console.log("Foo 内部 getName =function");
}
return this;
}
Foo.getName = function() {
// alert(2);
console.log("Foo.getName");
}
Foo.prototype.getName = function() {
// alert(3);
console.log("Foo.prototype.getName");
}
getName = function() {
// alert(4);
console.log("getName = function");
}
function getName() {
// alert(5);
console.log("function getName");
}
另外大家可以试试另一道面试题,看看跟自己想的是否一样,如果不一样的话,可以看JS中变量名与函数名重名的问题的解释。
var a = 1;
function b(){
a = 10;
return;
function a(){
console.log(a);
}
}
b();
console.log(a);
关于new Foo参考了JS实例化一个对象有括号和没括号有什么区别吗?。
实际开发中我们可能不会这么写,如果你看到同事这么写一堆考验你的理解力,相信你一定会到他面前跟他掰扯两句。
在ES6中, 可能类似的面试题就会少一些了
// 在ES6中,使用let禁止重命名,会提示已经定义了
// Uncaught SyntaxError: Identifier 'a' has already been declared
console.log(a);let a = 3; function a() {console.log(5);}
// 在ES6中, let与const拥有类似的特性,阻止了变量提升
// Uncaught ReferenceError: a is not defined
console.log(a);let a = 3; function b() {console.log(5);}
let还有另外一个作用,解决经典的需要闭包解决的问题
for (let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 100);
}
// 0 1 2 3 4
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 100);
}
// 全是5
如果有理解不到位的,请大家鞭策。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。