暑期实习前端面试题汇总

142 阅读1分钟

1.实现一个浅拷贝

  function clone(source) {
        if (typeof source === "object" && source !== null) {
          //注意识别target是数组还是对象
          const cloneTarget = Array.isArray(source) ? [] : {};
          //for in可以遍历对象的所有属性包括继承的
          for (let prop in source) {
            if (source.hasOwnProperty(prop)) {
              cloneTarget[prop] = source[prop];
            }
          }
        } else return source;
      }

2.实现一个深拷贝(递归拷贝newTarget的属性)

  if (typeof source === "object" && source !== null) {
          //注意识别target是数组还是对象
          const cloneTarget = Array.isArray(source) ? [] : {};
          //for in可以遍历对象的所有属性包括继承的
          for (let prop in source) {
            if (source.hasOwnProperty(prop)) {
            //和浅拷贝的实现唯一不同的地方,递归拷贝
              cloneTarget[prop] = deepclone(source[prop]);
            }
          }
        } else return source;

3.实现一个深拷贝(进阶版,考虑函数,正则,循环引用等特殊情况)

 var isObject = (source) => {
        return (
          source !== null &&
          (typeof source === "object" || typeof source === "function")
        );
      };
      function deepclone(source, map = new Map()) {
        // 返回已缓存的对象
        if (map.get(source)) return source;
        // 直接返回!isObject的对象
        if (!isObject(source)) return source;
        const constructor = source.constructor;
        // 检测当前对象source是否与正则,日期等对象匹配
        if (/^(RegExp|Date)$/i.test(constructor.name)) {
          // 返回一个新的(正则类/日期类)的对象
          return new constructor(source);
        }
        const cloneTarget = Array.isArray(source) ? [] : {};
        // 缓存对象,给对象作标记
        map.set(source, true);
        for (let prop in source) {
          if (source.hasOwnProperty(prop)) {
            // map为循环引用的对象做标记,防止无限递归
            cloneTarget[prop] = deepclone(source[prop], map);
          }
        }
        return cloneTarget;
      }

循环引用,简单来说,就是对象a的属性值指向对象b,对象b的属性值指向对象a。

4.实现instanceOf

 function instanceOf(obj, target) {
        target = target.prototype;
        obj = obj.__proto__; // 处理兼容性问题,可以使用Object.getPrototypeOf(),更推荐使用后者
        while (true) {
          if (obj === target) return true;
          if (obj === null) return false;
          obj = obj.__proto__;
        }
      }
      
      
 function instanceOf(obj, target) {
        target = target.prototype;
        obj = Object.getPrototypeOf(obj); 
        while (true) {
          if (obj === target) return true;
          if (obj === null) return false;
          obj = Object.getPrototypeOf(obj);
        }
      }

5.实现节流防抖函数

定义

  • 节流: n 秒内只运行一次,若在 n 秒内重复触发,只有一次生效
  • 防抖: n 秒后在执行该事件,若在 n 秒内被重复触发,则重新计时

一个经典的比喻:

想象每天上班大厦底下的电梯。把电梯完成一次运送,类比为一次函数的执行和响应

假设电梯有两种运行策略 debouncethrottle,超时设定为15秒,不考虑容量限制

电梯第一个人进来后,15秒后准时运送一次,这是节流

电梯第一个人进来后,等待15秒。如果过程中又有人进来,15秒等待重新计时,直到15秒后开始运送,这是防抖

(1)实现防抖函数

①触发防抖函数后,n秒后执行fn

 function debounce(fn, delay) {
        // 闭包,形成私有变量
        let timer = null; 
        return function () {
          // 清除定时器,避免多次触发fn
          clearTimeout(timer);
          timer = setTimeout(() => {
            fn.call(this, arguments);
          }, delay);
        };
      }

②触发防抖函数后,若是第一次触发,则立即执行fn,否则n秒后执行fn

 function debounce2(fn, delay, immediate) {
        let timer = null;
        return function () {
          // 第一次触发时timer为null
          let now = !timer;
          // immediate为true
          if (immediate) {
            if (now) {
              // timer为null,立即执行fn函数
              fn.call(this, arguments);
            }
            timer = setTimeout(() => {
              timer = null;
            }, delay);
          } 
          // else代码块的处理逻辑与①相同
          else {
            clearTimeout(timer);
            timer = setTimeout(() => {
              timer = null;
              fn.call(this, arguments);
            }, delay);
          }
        };
      }

(2)实现节流函数,n秒内只执行一次

 function throttle(fn, delay) {
        
        let preTime = Date.now();
        let timer = null;
        return function () {
          // 时间戳,记录当前的时间
          let nowTime = Date.now();
          // 从上一次到现在剩余的时间
          let leftTime = delay - (nowTime - preTime);
          clearTimeout(timer);
          if (leftTime < 0) {
            fn.call(this, arguments);
            preTime = Date.now();
          } else {
            timer = setTimeout(() => {
              preTime = Date.now();
              fn.call(this, arguments);
            }, leftTime);
          }
        };
      }

6.实现Object.create()函数

 Object.prototype.create = function (target) {
        if (target !== "object" || !target) throw Error("must be an object");
        let func = function () {};
        func.prototype = target;
        return new func();
      };

7.实现new函数

 function new(target, ...arguments) {
        let obj = Object.create(target.prototype);
        // obj.__proto__ === target.prototype
        let result = target.call(obj, ...arguments);
        // 需要考虑构造函数的返回值是函数或者对象
        if (result && /^(function|object)$/.test(result)) return result;
        return obj;
      }

8.实现call,apply,bind

(1)call

 /**
       * context: 要改变的函数中的this指向,写谁就是谁
       * args:传递给函数的实参信息
       * this:要处理的函数 fn
 */
Function.prototype.call = function () {
        // arguements类数组对象
        var context = Array.prototype.slice.call(arguments, 0)[0];
        // var context = Array.of(arguments)[0],等效于上一行代码
        context = context || window; // 兼容性处理
        var args = Array.prototype.slice.call(arguments, 1);
        context["fn"] = this; // context添加属性fn,fn指向要处理的函数this
        var res = context["fn"](...args); 
        delete context["fn"];
        return res;
      };

(2)apply

 Function.prototype.apply = function (context, args) {
        context = context || window;
        context.fn = this;
        var res = context.fn(...args);
        delete context.fn;
        return res;
      };

(3)bind

Function.prototype.bind = function (context, ...args) {
        context = context || window;
        var self = this;
        var args1 = Array.of(...args);
        function f1() {}
        function f2() {
          var args2 = Array.prototype.slice.call(arguments);
          return self.apply(
            this instanceof f2 ? this : context,
            args1.concat(args2)
          );
        }
        f1.prototype = self.prototype;
        f2.prototype = new f1();
        return f2;
      };

未完待续...