0. 前言
转眼间度过了两年的研究生生涯,到了该找工作的时候。在这次春招里,小编投了腾讯,字节,网易,拼多多,美团等公司的前端岗位。目前,除了网易还没有开始之外,其余的基本上全部以失败告终。有点儿沮丧,但是不能因此丧失斗志,还是要收拾行囊,继续准备前行。
在这篇文章,包括接下来的一两篇文章中,小编会通过现有的面试经验,将一些在面试和笔试中容易考到的知识点整理一下。
1. Promise 的重试机制
- 题目描述:实现
promise
的重试机制。输入一个返回值是Promise
对象的函数以及重试的次数,返回一个Promise
对象,返回的Promise
对象要实现的功能是:执行输入的函数,直到promise决议被执行,或者达到重试次数时终止。 - 例子:
// 输入函数:返回值是一个Promise
function retryDemo(){
return new Promise((resolve, reject)=>{
let r = Math.random()
setTimeout(()=>{
console.log(r)
if(r>0.9){
resolve(r)
}else{
reject('error:'+r)
}
}, 1000)
})
}
// 我们需要实现的函数
retry(retryDemo, 5).then(res=>{
console.log('成功:'+ res)
}).catch(err=>{
console.log(err)
})
// 要实现的输出结果:最多输出 5 个 error,或者在输出 '成功' 截止
- 分析:在问题的描述中,我们已经知道了函数的返回值是一个
Promise
对象,因此直接写就可以了。对于Promise
对象而言,我们需要关注的是什么之后调用resolve
将决议执行,什么时候需要把决议拒绝掉。显然从问题的描述中,我们可以获得这个答案: 当传入的fn
返回的Promise
决议被执行的时候,调用resolve
执行决议;但是,传入的fn
返回的Promise
决议被拒绝的时候,不能直接调用reject
,而是需要继续执行fn()
函数,等待返回的promise
进行决议。当时,我的思路是通过一个while
循环来判断,在决议中更改当前的状态,如果状态更改,直接将循环break
掉。这样做的问题是,promise是异步进行决议的,并不能直接知道决议成功与否。关于这个问题,这里提供的一个思路是在retry
函数最终返回的promise
中,封装一个函数,在这个函数的内部去执行fn
函数,当fn
返回的promise
被拒绝掉,发生 error 的时候,调用封装好的函数进行重试。 - 代码:
function retry(fn, times) {
return new Promise((resolve, reject) => {
function run() {
fn().then(resolve).catch(err => {
if(times--) {
console.log(`还有${times}次尝试`);
run();
} else {
reject(err);
}
})
}
run();
});
}
2. bind 的实现
- 题目描述:实现函数的
bind
方法 - 分析:函数的
bind
方法返回一个函数,并且将函数的this
绑定到该方法传入的对象上。想要实现这个功能,需要做的是利用函数的apply
方法进行绑定。 - 第一版代码,用函数的
apply
模拟bind
:
Function.prototype.myBind = function(context) {
var self = this;
var argArray = Array.prototype.slice.call(arguments);
return function () {
return self.apply(context, argArray.slice(1));
}
}
- 第二版代码,对于返回函数进行柯里化处理:
Function.prototype.myBind = function(context) {
var self = this;
var args = Array.prototype.slice.call(arguments, 1);
return function() {
var innerArgs = Array.prototype.slice.call(arguments);
var finalArgs = args.concat(innerArgs);
return self.apply(context, finalArgs);
}
}
- 第三版代码,我们应该注意到当
bind
返回的函数作为构造函数的时候,bind
时指定的this
值会失效,但传入的参数依然有效. 这一版代码,兼容这种情况:
Function.prototype.myBind = function(context) {
var self = this;
var args = Array.prototype.slice.call(arguments, 1);
var F = function () {};
F.prototype = this.prototype;
var bound = function() {
var innerArgs = Array.prototype.clice.call(arguments);
var finalArgs = args.concat(innerArgs);
return self.apply(this instanceof F ? this : context || this, finalArgs);
}
return bound;
}
- 最终版代码,考虑调用该方法的不是函数的情况:
Function.prototype.myBind = function(context) {
if(typeof this !== "function") {
throw new Error("Function.prototype.bind - what is trying to be bound is not callable");
}
var self = this;
var args = Array.prototype.slice.call(arguments, 1);
var F = function () {};
F.prototype = this.prototype;
var bound = function() {
var innerArgs = Array.prototype.clice.call(arguments);
var finalArgs = args.concat(innerArgs);
return self.apply(this instanceof F ? this : context || this, finalArgs);
}
return bound;
}
3. apply 实现
- 题目描述:实现函数的
apply
方法 - 分析:函数的
apply
方法实现的功能是,执行该函数,并且将this
绑定到传入的object
对象中。在这里,我们采用this
的隐式绑定规则来实现。具体使用到的是:当函数作为某个对象的属性调用的时候,该函数的this
会绑定到外层的object
对象上 - 代码:
Function.prototype.myApply = function(context, arr) {
if(typeof arr === 'undefined' || arr === null) {
arr = [];
}
if(typeof context === 'undefined' || context === null) {
context = window;
}
context = new Object(context);
context.fn = this;
const result = context.fn(...arr);
delete context.fn;
return result;
}
4. 普通函数和箭头函数的区别
- 它俩长得不一样:
- 箭头函数:
let fun = () => { // 函数体代码 }
- 普通函数:
function fun() { // 函数体代码 }
- 箭头函数是匿名函数,不能作为构造函数,不能使用
new
- 箭头函数不绑定
arguments
,取而代之用...arr
来解决
function A(a){
console.log(arguments);
}
A(1,2,3,4,5,8); // [1, 2, 3, 4, 5, 8, callee: ƒ, Symbol(Symbol.iterator): ƒ]
let B = (b)=>{
console.log(arguments);
}
B(2,92,32,32); // Uncaught ReferenceError: arguments is not defined
let C = (...c) => {
console.log(c);
}
C(3,82,32,11323); // [3, 82, 32, 11323]
- 箭头函数不绑定
this
,会捕获其所在上下文的this
值,作为自己的this
值 - 箭头函数通过
call()
或apply()
方法调用一个函数时,只是传入了一个参数,对this
的值并没有影响 - 箭头函数没有原型属性
- 箭头函数不能当作生成器函数,不能使用
yield
关键字