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关键字