春招面试笔试记录(一)

255 阅读5分钟

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. 普通函数和箭头函数的区别

  1. 它俩长得不一样:
    • 箭头函数:
    let fun = () => {
        // 函数体代码
    }
    
    • 普通函数:
    function fun() {
        // 函数体代码
    }
    
  2. 箭头函数是匿名函数,不能作为构造函数,不能使用new
  3. 箭头函数不绑定 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]
  1. 箭头函数不绑定this,会捕获其所在上下文的this值,作为自己的this
  2. 箭头函数通过call()apply()方法调用一个函数时,只是传入了一个参数,对this的值并没有影响
  3. 箭头函数没有原型属性
  4. 箭头函数不能当作生成器函数,不能使用yield关键字

5. 未完待续...