这次出来面试感觉比上次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

如果有理解不到位的,请大家鞭策。


浮生如斯
20 声望2 粉丝

缘来天注定,缘去人自夺。种如是因,收如是果,一切唯心造。笑言面对,不去埋怨。悠然、随心、随性、随缘。