Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
FrankKai opened this issue May 22, 2018 · 7 comments
Open

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

FrankKai opened this issue May 22, 2018 · 7 comments

Comments

@FrankKai
Copy link
Owner

FrankKai commented May 22, 2018

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

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

@FrankKai
Copy link
Owner Author

FrankKai commented May 22, 2018

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

  • 学会倾听
    无论面试者有多少职场上的不如意想和你倾诉,都要耐心听他讲完,不要打断他
  • 学会定位
    根据面试者的工作经验和工作场景,对能力进行简单定位,虽然这偶尔会有所偏颇,但是对于后续提问会有很好的帮助
  • 学会理解
    理解面试者想要表达的对知识点的理解,即使他讲的很模糊,尽可能捕捉其理解的部分
  • 学会引导
    当面试者遇到一道棘手的题目时,如果对方逻辑不清楚,学会化繁为简,由浅入深去做引导;假设对方对当前的题目游刃有余,引导深入原理或者是扩展出应用场景
  • 学会挖掘
    学会挖掘出面试者身上的闪光点,聪明,执着,这些都是非常好的品质
  • 学会尊重
    当面试者急于想得知面试官的评价时,无论你对他是否满意,都采取"如果有新的进展,我们后续会再通知你"的方式
  • 学会沉默
    当面试者想得知你对问题的看法时,不要做太深层次的讲解,点到为止,因为有限的面试时间主要目的是为了考察面试者的能力

@FrankKai
Copy link
Owner Author

FrankKai commented May 22, 2018

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
Copy link
Owner Author

FrankKai commented May 22, 2018

@FrankKai
Copy link
Owner Author

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
Copy link
Owner Author

FrankKai commented May 22, 2018

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
  • Uncaught ReferenceError: i is not defined
    由此可见for语句的块作用域,不仅仅在花括号中生效,在圆括号中也生效。
  • 输出0 1 2 3 4 5 的原因
    setTimeout的callback是异步函数,for循环实质上是在做异步循环队列,setTimeout的callback会被调用5次,由于let会为每次的i分配独立的地址空间,因此每一次传不同的值进去。

为什么在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
原因:

  • 打印的是window.i
    每个传入的i指向相同的i,传入时依次window.i的值为1,2,3,4,5,6,但是都是同一个引用,当函数调用栈开始执行setTimeout的callback时,window.i已经变为了6
  • var 不会限制块作用域
    不会分配6个独立的地址空间给setTimeout的callback

@FrankKai
Copy link
Owner Author

FrankKai commented May 22, 2018

深入理解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]"这种。

总结:

  • 一般情况下,js中对象的toString(),返回字符串,内容与函数声明语法有关,例如[1,2,3].toString()//"1,2,3"
  • 大多数都返回函数的完整源码,Array.toString()//"function Array() { [native code] }"
  • 内置函数往往返回一个类似"[native code]"的函数体,需要配合call方法,比如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 FrankKai changed the title 一些特别棒的面试题[3] 一些特别棒的面试题[2] May 22, 2018
Repository owner locked and limited conversation to collaborators May 22, 2018
@FrankKai
Copy link
Owner Author

综合考察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

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

1 participant