金九银十过了大半,笔者最近也面了一些公司,现将一些自己遇到的和收集的基础题目整理出来,后续会整理分享一些其他的信息,希望对你能有所帮助

前端面试必须掌握的手写题:基础篇

前端面试必须掌握的手写题:场景篇

前端面试必须掌握的手写题:进阶篇

闲言少叙,看正文

实现Object.create

创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。 —MDN

 1function create(obj) {
 2  function Fun(){
 3    
 4  }
 5  Func.prototype = obj;
 6  Func.prototype.constructor = Func;
 7  return new Fun();
 8}

实现instanceof方法

instanceof 运算符用于判断构造函数的 prototype 属性是否出现在对象的原型链中的任何位置。

 1function myInstanceof(obj, ctor) {
 2  let proto = Object.getPrototypeOf(obj);
 3  let prototype = ctor.prototype;
 4  where(true) {
 5    if(!proto) return false;
 6    if(proto === prototype) return true;
 7    proto = Object.getPrototypeOf(proto);
 8  }
 9}

实现new关键字

在调用new之后会发生这几个步骤

  1. 创建一个空对象

  2. 设置原型:将空白对象的原型设置为函数的prototype对象

  3. 让函数的this指向这个对象,执行构造函数的代码(为空白对象添加属性)

  4. 判断函数的返回值

    4.1. 如果是引用类型,直接返回,比如构造函数主动返回了一个对象:function T(){return {x: 1}}

    4.2. 如果不是引用类型,返回空白对象; 比如构造函数返回一个数字:function T(){return 1}

 1
 2function objectFactory() {
 3  let newObject = null;
 4  let constructor = Array.prototype.shift.call(arguments);
 5  let result = null;
 6  if (typeof constructor !== 'function') {
 7    console.error('type error');
 8    return
 9  }
10  newObject = Object.create(constructor.prototype);
11  result = constructor.apply(newObject, arguments);
12  let flag = result && (typeof result === 'function' || typeof result === 'object');
13  return flag ? result : newObject;
14}

拦截构造函数调用

 1function Person(name) {
 2  if (new.target !== undefined) {
 3    this.name = name;
 4  } else {
 5    throw new Error('必须使用 new 命令生成实例');
 6  }
 7}
 8
 9
10function Person(name) {
11  if (new.target === Person) {
12    this.name = name;
13  } else {
14    throw new Error('必须使用 new 命令生成实例');
15  }
16}
17
18var person = new Person('张三'); 
19var notAPerson = Person.call(person, '张三');  

实现继承

组合式继承

 1
 2function Father(name, age) {
 3  this.name = name
 4  this.age = age
 5  this.hobby = ['敲代码', '解Bug', '睡觉']
 6}
 7
 8Father.prototype.sayName = function () {
 9  console.log(this.name, 666)
10}
11Father.prototype.x = 1
12
13function Child(name, age) {
14  Father.call(this, name, age) 
15  this.a = 1
16}
17Child.prototype = Object.create(Father.prototype)
18
19
20function Super(foo) {
21  this.foo = foo
22}
23Super.prototype.printFoo = function() {
24  console.log(this.foo)
25}
26function Sub(bar) {
27  this.bar = bar
28  Super.call(this)
29}
30Sub.prototype = Object.create(Super.prototype)
31Sub.prototype.constructor = Sub

ES6版本继承

 1class Super {
 2  constructor(foo) {
 3    this.foo = foo
 4  }
 5  printFoo() {
 6    console.log(this.foo)
 7  }
 8}
 9class Sub extends Super {
10  constructor(foo, bar) {
11    super(foo)
12    this.bar = bar
13  }
14}

简单实现Promise

这里简单实现一下,可以参考一下其他的Promise A+规范的实现,主要包含then,all,race

  • then:
 1const PENDING = 'pending';
 2const RESOLVED = 'resolved';
 3const REJECTED = 'rejected';
 4
 5function MyPromise(fn) {
 6  const self = this;
 7  this.state = PENDING;
 8  this.value = null;
 9  this.reason = null;
10  this.resolvedCallbacks = [];
11  this.rejectedCallbacks = [];
12
13  function resolve(value) {
14    if (value instanceof MyPromise) {
15      value.then(resolve, reject)
16    }
17    
18    setTimeout(() => {
19      if (self.state === PENDING) {
20        self.state = RESOLVED;
21        self.value = value;
22        self.resolvedCallbacks.forEach(cb => cb(value));
23      }
24    }, 0)
25  }
26
27  function reject(reason) {
28    setTimeout(() => {
29      if (self.state === PENDING) {
30        self.state = REJECTED;
31        self.reason = reason;
32        self.rejectedCallbacks.forEach(cb => cb(reason));
33      }
34    }, 0)
35  }
36  try {
37    fn(resolve, reject);
38  } catch (e) {
39    reject(e);
40  }
41}
42
43MyPromise.prototype.then = function (onFulfilled, onReject) {
44  const self = this;
45  return new MyPromise((resolve, reject) => {
46    let fulfilled = () => {
47      try {
48        const result = onFulfilled(self.value);
49        return result instanceof MyPromise ? result.then(result) : resolve(result);
50      } catch (e) {
51        reject(e);
52      }
53    };
54    let rejected = () => {
55      try {
56        const result = onReject(self.reason);
57        return result instanceof MyPromise ? result.then(resolve, reject) : reject(result);
58      } catch (e) {
59        reject(e);
60      }
61    }
62    switch (self.state) {
63      case PENDING:
64      case RESOLVED:
65      case RESOLVED:
66
67    }
68  })
69}
70
71MyPromise.all = (promises) => {
72  return new MyPromise((resolve, reject) => {
73    if (!Array.isArray(promises)) {
74      throw new TypeError('arguments must be array');
75    }
76    let resolvedCounter = 0;
77    let promiseNum = promises.length;
78    let resolvedResult = [];
79
80    for (let i = 0; i < promises.length; i++) {
81      MyPromise.resolve(promises[i]).then(value => {
82        resolvedCounter++;
83        resolvedResult[i] = value;
84        if (resolvedCounter === promiseNum) {
85          return resolve(resolvedResult);
86        }
87      }, error => {
88        return reject(error);
89      })
90    }
91  })
92}
93MyPromise.race = function(args) {
94  return new Promise((resolve, reject) => {
95    for(let i = 0; len = args.length; i++) {
96      args[i].then(resolve, reject);
97    }
98  })
99}

防抖函数

防抖是n秒内会重新计时

 1function debounce(fn, wait) {
 2  let timer = null;
 3  return function() {
 4    if(timer) {
 5      clearTimeout(timer);
 6      timer = null;
 7    }
 8    timer = setTimeout(() => {
 9      fn.apply(this, arguments);
10    }, wait);
11  }
12}

节流函数

n秒内不重新计时

 1function throttle(fn, delay) {
 2  let timer = null;
 3  return function () {
 4    if (timer) return;
 5    timer = setTimeout(() => {
 6      timer = null;
 7      return fn.apply(this, arguments);
 8    }, delay)
 9  }
10}

实现类型判断函数

 1function getType(value) {
 2  if (value === null) {
 3    return value + '';
 4  }
 5  if(typeof value === 'object') {
 6    return Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
 7  } else {
 8    return typeof value;
 9  }
10}

实现call函数

执行步骤:

  • 判断call的调用者是否为函数,不是函数需要抛出错误,call调用者就是上下文this,也就是需要被调用的函数
  • 判断需要被调用的函数的的上下文对象是否传入,不存在就设置为window
  • 处理传入的参数,截取第一个参数后的所有参数,作为被调用函数
  • 将需要被调用的函数,绑在传入的上下文上,作为一个属性
  • 使用传入的上下文调用这个函数,并返回结果
  • 删除绑定的属性
  • 返回结果
 1
 2Function.prototype.myCall = function(context) {
 3  if(typeof this !== 'function') {
 4    throw new TypeError('need function');
 5  }
 6  let args = arguments.slice(1);
 7  let result = null;
 8
 9  context = context || window;
10
11  context.fn = this;
12  result = context.fn(...args);
13
14  delete context.fn;
15  return result;
16}

实现apply函数

唯一的不同就是最后参数的获取方式

 1Function.prototype.myApply = function(context) {
 2  if(typeof this !== 'function') {
 3    throw new TypeError('need function');
 4  }
 5  let args = arguments[1];
 6  let result = null;
 7
 8  context = context || window;
 9
10  context.fn = this;
11  result = context.fn(...args);
12
13  delete context.fn;
14  return result;
15}

实现bind

  • 先判断调用者是否为函数

  • 缓存当前需要bind的函数,就是上面的调用者,也是是bind函数的上下文

  • 返回一个函数,利用闭包原理实现对this的保存

  • 函数内部用apply函数来处理函数调用

    • 需要判断函数作为构造函数的情况,这个时候的this就是当前调用这个闭包函数的this
    • 作为普通函数,直接使用传入的上下文就好了
 1Function.prototype.myBind = function(context) {
 2  if(typeof this !== 'function') {
 3    throw new TypeError('need function');
 4  }
 5  let args = [...arguments].slice(1);
 6  let fn = this;
 7
 8  return function F() {
 9    return fn.apply(
10      this instanceof F ? this : context,
11      args.concat(...arguments)
12    )
13  }
14}

浅拷贝

 1
 2Object.assign(target, source1, source2);
 3
 4
 5{...obj1, ...obj2}
 6
 7
 8Array.prototype.slice
 9Array.prototype.concat
10
11
12function shallowCopy(object) {
13  if(!object || typeof object !== 'object') return;
14  let newObj = Array.isArray(object);
15
16  for(let key in object) {
17    if(object.hasOwnProperty(key)) {
18      newObj[key] = object(key);
19    }
20  }
21  return newObj;
22}

深拷贝deepclone

可能的问题:

  • json方法出现函数或symbol类型的值的时候,会失效
  • 处理循环引用问题
  • 处理可迭代类型的数据
  • 处理包装类型
  • 处理普通类型

简单版本参考vue版本:

  • 判断类型是否为原始类型,如果是,无需拷贝,直接返回
  • 为避免出现循环引用,拷贝对象时先判断存储空间中是否存在当前对象,如果有就直接返回
  • 开辟一个存储空间,来存储当前对象和拷贝对象的对应关系
  • 对引用类型递归拷贝直到属性为原始类型
 1const deepClone = (target, cache = new WeakMap()) => {
 2    if(target === null || typeof target !== 'object') {
 3        return target
 4    }
 5    if(cache.get(target)) {
 6        return target
 7    }
 8    const copy = Array.isArray(target) ? [] : {}
 9    cache.set(target, copy)
10    Object.keys(target).forEach(key => copy[key] = deepClone(obj[key], cache))
11    return copy
12}

实现Object.assign

就是实现一个浅拷贝

 1
 2Object.myAssign = function (target, ...source) {
 3  if (target === null) {
 4    throw new TypeError('can not be null');
 5  }
 6  let ret = Object(target);
 7  source.forEach(obj => {
 8    if (!obj !== null) {
 9      for (let key in obj) {
10        if (obj.hasOwnProperty(key)) {
11          ret[key] = obj[key];
12        }
13      }
14    }
15  });
16  return ret;
17}

简单实现async/await中的async函数

async/await语法糖就是使用Generator函数+自动执行器来运作的,注意只要要实现async函数就是实现一个generate函数+执行器的语法糖

 1
 2function getNum(num){
 3    return new Promise((resolve, reject) => {
 4        setTimeout(() => {
 5            resolve(num+1)
 6        }, 1000)
 7    })
 8}
 9
10
11function asyncFun(func){
12  var gen = func();
13
14  function next(data){
15    var result = gen.next(data);
16    if (result.done) return result.value;
17    result.value.then(function(data){
18      next(data);
19    });
20  }
21
22  next();
23}
24
25
26var func = function* (){
27  var f1 = yield getNum(1);
28  var f2 = yield getNum(f1);
29  console.log(f2) ;
30};
31asyncFun(func);

实现一个Object.freeze

锁定对象的方法

  • Object.preventExtensions()

no new properties or methods can be added to the project 对象不可扩展, 即不可以新增属性或方法, 但可以修改/删除

  • Object.seal()

same as prevent extension, plus prevents existing properties and methods from being deleted 在上面的基础上,对象属性不可删除, 但可以修改

  • Object.freeze()

same as seal, plus prevent existing properties and methods from being modified 在上面的基础上,对象所有属性只读, 不可修改

以上三个方法分别可用Object.isExtensible(), Object.isSealed(), Object.isFrozen()来检测

 1var deepFreeze =function (obj) {
 2	var allProps = Object.getOwnPropertyNames(obj);
 3	
 4	allProps.forEach(item => {
 5		if (typeof obj[item] === 'object') {
 6			deepFreeze(obj[item]);
 7		}
 8	});
 9	return Object.freeze(obj);
10}

模拟实现一个Object.freeze,使用了Object.seal

 1 function myFreeze(obj) {
 2    if (obj instanceof Object) {
 3      Object.seal(obj);
 4      let p;
 5      for (p in obj) {
 6        if (obj.hasOwnProperty(p)) {
 7          Object.defineProperty(obj, p, {
 8            writable: false
 9          });
10         myFreeze(obj[p]);
11       }
12     }
13   }
14 }

用ES5实现一下map和reduce函数

 1Array.prototype.myMap = (fn, context) => {
 2  var arr = Array.prototype.slice.call(this);
 3  var mapArray = [];
 4  for (let i = 0; i < arr.length; i++) {
 5    mapArray.push(fn.call(context, arr[i], i, this));
 6  }
 7  return mapArray;
 8}
 9
10Array.prototype.myReduce = (fn, initialValue) => {
11  var arr = Array.prototype.slice.call(this);
12  var res, startIndex;
13  res = initialValue ? initialValue : arr[0];
14  startIndex = initialValue ? 0 : 1;
15  for(let i = startIndex; i< arr.length; i++) {
16    res = fn.call(null, res, arr[i], i, this);
17  }
18  return res;
19}
个人笔记记录 2021 ~ 2025