FrankKai / FrankKai.github.io

FE blog
https://frankkai.github.io/
363 stars 39 forks source link

一些特别棒的面试题[2] #66

Open FrankKai opened 6 years ago

FrankKai commented 6 years ago

由于种种原因,开始了新一轮的面试,这一轮面试可谓收获颇丰。 与各种各样的面试官交流下来,除了收获到一些疏漏的知识点外,发现面试其实非常考验面试官的水平。揣摩出一些如何成为一名合格的前端面试官方法。

以及很重要的老哥的经验:进大厂前必须要做的准备,提前一个月刷题。

FrankKai commented 6 years ago

如何成为一名合格的面试官?

FrankKai commented 6 years ago

setTimeout与函数调用栈

console.log(1);
setTimeout(function(){
    console.log(2);
},0);
console.log(3);

输出:1 3 2 原因:Call Stack会最后调用setTimeout的callback,setTimeout中的callback是一个异步函数。 函数调用栈的部分可以参考这里:https://blog.risingstack.com/node-js-at-scale-understanding-node-js-event-loop/

FrankKai commented 6 years ago

如何解决callback hell问题

https://blog.risingstack.com/node-js-async-best-practices-avoiding-callback-hell-node-js-at-scale/

FrankKai commented 6 years ago

function foo 与 var foo的提升优先级

console.log(typeof foo);
var foo = "foo";
function foo(){}

输出:function

console.log(typeof foo);
function foo(){}
var foo = "foo";

输出:function

function优先级比var高,无论在其前后,都会覆盖掉同名的var声明。

FrankKai commented 6 years ago

let 块作用域 与 setTimeout

for(let i=0;i<6;i++){
    setTimeout(function(){
        console.log(i)
    },0)
}
console.log(i)

输出:

0
Uncaught ReferenceError: i is not defined
1
2
3
4
5

为什么在debug的过程中,打印顺序是混乱的? (等把规范的timers章节翻译完,再来解决) breakpoint打在console.log(i)上。

Uncaught ReferenceError: i is not defined
0 
2
5
4
3
1

如果将let替换成var呢?

for(var i=0;i<6;i++){
    setTimeout(function(){
        console.log(i)
    },0)
}
console.log(i)

输出: 6个6 原因:

FrankKai commented 6 years ago

深入理解Object.prototype.toString.call()

为什么Object.toString.call([1,2,3])返回[object Array]?可以直接[].toString()返回[object Array]吗? 难道真的像自己理解的那样,是通过call将[1,2,3]作为Object.toString的实参传递了进去吗?不是。 直接Object.toString([1,2,3])不能实现同样的功能吗?不能。 而实际上也有Array.proto.toString()这种形式,所以是可以直接调用arr.toString()的,这样能检测出吗?不行。

那到底是什么原因? 先来肝一个表格。


数据类型 例子 return
字符串 "foo".toString() "foo"
数字 1.toString() Uncaught SyntaxError: Invalid or unexpected token
布尔值 false.toString() "false"
undefined undefined.toString() Uncaught TypeError: Cannot read property 'toString' of undefined
null null.toString() Uncaught TypeError: Cannot read property 'toString' of null
String String.toString() "function String() { [native code] }"
Number Number.toString() "function Number() { [native code] }"
Boolean Boolean.toString() "function Boolean() { [native code] }"
Array Array.toString() "function Array() { [native code] }"
Function Function.toString() "function Function() { [native code] }"
Date Date.toString() "function Date() { [native code] }"
RegExp RegExp.toString() "function RegExp() { [native code] }"
Error Error.toString() "function Error() { [native code] }"
Promise Promise.toString() "function Promise() { [native code] }"
Obejct Object.toString() "function Object() { [native code] }"
Math Math.toString() "[object Math]"

为什么会出现下面的情况?

Object.toString.call(Array)//"function Array() { [native code] }"
Object.prototype.toString.call(Array)//"[object Function]"

答案在这里!

Object.toString()//"function Object() { [native code] }"
Object.prototype.toString()//"[object Object]"

Object对象和它的原型链上各自有一个toString()方法,第一个返回的是一个函数,第二个返回的是值类型。

既然知道了不同,现在我们再来分析下Object.prototype.toString.call(Array)//"[object Function]"。 Array对象本身返回一个构造函数,Array//ƒ Array() { [native code] },而Object.prototype.toString()返回的是//"[object Type]"的形式,通过call将Array的this上下文切换到Object,从而调用了Object.prototype.toString(),因此返回[object Function]

需要注意的是:Math.toString()直接返回"[object Math]"。

实际开发中,我们用到最多的可能是:Object.prototype.toString.call([1,2,3])//"[object Array]"这种。

总结:

那么不可以直接Array.prototype.toString.call([1,3,4])吗? 不行! 因为Array,Function,Date虽然是基于Object进行创建的,但是他们继承的是Object.toString(),而不是Object.prototype.toString()。 再加深一遍印象:

Object.toString()//"function Object() { [native code] }"
Object.prototype.toString()//"[object Object]"

所以这就是必须用Object.prototype.toString()去检测类型的原因。

至于Object.prototype.toString()内部是怎么实现的,等到时机成熟再去深入。

FrankKai commented 6 years ago

综合考察bind,call和apply的面试题

var obj = {
    a: 1,
    name: 'world',
    objSayName: function (fn) {
        fn();
    }
}
function sayName () {
    return console.log(this.name);
}
obj.objSayName(sayName);
// 输出:undefined

为什么? 在obj的objSayName内部,没有修改this指向到当前调用对象。

题目一:对象内部方法,调用全局的函数 适用:多个对象(局部方法)复用同一全局函数 精简:局部(方法)复用全局函数 方法:修改this指向,通过Function.prototype.bind()去显式修改this指向到当前调用对象。 原因:Calling f.bind(someObject) creates a new function with the same body and scope as f, but where this occurs in the original function,in the new function it is permanently bound to the first argument of bind, regardless of how the function is being used.bind only works once!

var obj = {
    name: '1',
    objSayName: function (f) {
      var g = f.bind(this);
      console.log(g());
    }
};
function sayName(){
    return this.name;
}
obj.objSayName(sayName);

输出:'1'

拓展: 题目二:如果全局方法想输出对象的局部属性,该怎么办? 适用:同一全局函数输出多个对象(内部变量) 精简:全局函数输出局部(变量) 方法:使用apply或者call修改this指向到被调用对象 原因:An object can be passed as the first argument to call or apply and this will be bound to it.

var obj = {
    name: '1',
    say: function (fn) {
        fn();
    }
};
function sayName(){
    return this.name;
}
sayName.apply(obj);
sayName.call(obj);
// 输出:'1'

参考:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this