大厂手写前端JS汇总【字节/快手】持续更新

347 阅读3分钟

前言

虽然仍然处于疫情中,但为了自己技术职业规划,还是选择去尝试心目中的大厂。以下为真实的大厂面试记录,希望起到抛砖引玉的作用

1. 函数柯里化(字节三面)【压场面】

//给定一个计算体积的函数
function cube(l, w, h){
    return l * w * h
}
function currying(){
    //TODO:传入函数可以随意调用
}

//实现如下多种方式调用
let func = currying(cube)
func(1)(2)(3)
func(1,2,3)
func(1,2)(3)

第一道题个人觉得,此前没有接触过函数柯里化,急的满头大汉也是很正常的。函数柯里化

这里贴一下我的手写代码(面试后写出)

function currying (fn){
    let Myargs = []   //存放陆续传入的参数
    return function f() {
            if(Myargs.length + arguments.length >= 3){
                Myargs = Myargs.concat(Array.from(arguments));
                return fn( ...Myargs)    //返回传入的 fn的调用结果,并将保存下来的参数传入
            }else{   //未到达指定的参数个数,将参数存起来
                Myargs = Myargs.concat(Array.from(arguments))
            }
            return f
    }
}

2. 实现一个简单的bind方法(字节一面)

少废话,简单题直接上代码

Function.prototype.myBind = function(context){
    //闭包this,这里指向调用者,也就是this指向的是sayName函数
    return () => {
        /**
         *这里的this由于是箭头函数,故而指向上面的闭包this
         *调用函数的时候传入obj被context接收,‘1’被arguments接收(这里arguments也是闭包的arguments,箭头函数无arguments)
         *使用[...arguments]将类数组arguments转为数组,使用slice(1)将0下标以外的都截取下来
        **/
        this.call(context, ...[...arguments].slice(1))
    }
}

let obj = {name: 'hxb'}
function sayName (){
    console.log(this.name)
}
const say = sayName.myBind(obj, '1')
say()  //'hxb'

3. 构造函数理解(字节二面)

function Foo() {

     getName = function(){ alert(1); };

     return this;

}

Foo.getName = function() { alert(2); };

Foo.prototype.getName = function(){ alert(3); };

var getName = function() { alert(4); };

function getName(){ alert(5); }

//请解释下方各自输出
Foo.getName();       // 2   Foo为复杂数据类型,所以属于直接调用函数对象的方法

getName();           // 4  最后一行getName函数声明和getName变量都会发生变量提升,但是getName函数声明会提升到 var getName前面,输出4

Foo().getName();     // 1  普通函数调用,返回this为window,调用时对getName进行了覆盖

getName();           // 1  上一步的调用对getName进行了覆盖

new Foo.getName();   // 2  将Foo.getName当作一个构造函数来调用,执行函数内部代码输出2

new Foo().getName(); // 3 实例化了一个对象,此时实例化的这个对象,自身没有getName方法,将访问Foo.prototype上面的getName方法

4. 事件循环的应用(字节/快手)

事件循环机制,相比大家都很熟悉了,读完下面的几种例子,说不定你还需要重新学习一下Promise

// 字节二面
new Promise((res, rej) => {
  console.log(1);
  throw new Error('abc');  //抛出错误,下面的代码不执行
  res(2);
  console.log(3);
}).catch((e) => {

  console.log('catch');

}).then((t) => {

  console.log(t);

});
// 输出 1 --> catch --> undefined

稍稍变形一下

new Promise((res, rej) => {

  console.log(1);
  
  res(2);   //状态发生转变后,后续的错误也无法捕获
  
  throw new Error('abc');

  console.log(3);


}).catch((e) => {

  console.log('catch');

}).then((t) => {

  console.log(t);

});
// 输出 1 --> 2

看看快手的事件循环怎么考察

//快手一面
let a;
const b = new Promise((resolve, reject) => {
  console.log('promise1');
  resolve();
}).then(() => {
  console.log('promise2');
}).then(() => {
  console.log('promise3');
}).then(() => {
  console.log('promise4');
});

a = new Promise(async (resolve, reject) => {
  console.log(a);
  await b;
  console.log(a);  //输出一个Promise
  console.log('after1');
  await a     //TODO:此处求大佬指点迷津
  resolve(true);
  console.log('after2');
}).then(data => {console.info(data)})

console.log('end');
// 输出 promise1 --> undefined --> end --> promise2 --> promise3 --> promise4 --> Promise{<pending>} --> after1

自己也稍稍变形一下加深理解

let a;
const b = new Promise((resolve, reject) => {
  console.log('promise1');
  resolve();
}).then(() => {
  console.log('promise2');
}).then(() => {
  console.log('promise3');
}).then(() => {
  console.log('promise4');
});

a = new Promise(async (resolve, reject) => {
  console.log(a);
  await b;
  console.log(a);  //输出一个Promise
  console.log('after1');
  await Promise.resolve(1)    //TODO:此处求大佬指点迷津
  resolve(true);
  console.log('after2');
}).then(data => {console.info(data)})

//输出: promise1 --> undefined --> end --> promise2 --> promise3 --> promise4 --> Promise{<pending>} --> after1 --> after2 --> true

这此变形估计是最好理解的

let a;
const b = new Promise((resolve, reject) => {
  console.log('promise1');
  resolve();
}).then(() => {
  console.log('promise2');
}).then(() => {
  console.log('promise3');
}).then(() => {
  console.log('promise4');
});

a = new Promise(async (resolve, reject) => {
  console.log(a);
  await Promise.resolve(1);
  console.log(a);  //输出一个Promise
  console.log('after1');
  await Promise.resolve(1)    //TODO:此处求大佬指点迷津
  resolve(true);
  console.log('after2');
}).then(data => {console.info(data)})
//输出: promise1 --> undefined --> end --> promise2 --> Promise{<pending>} --> after1 --> promise3 --> after2 --> promise4 --> true

个人觉得Promise这个ES6的特性还需要大家好好研究一下,由于时间有限暂时先放这四个题。

如果大家感觉不错的话点个赞,鼓励一下,我将持续更新。