0
点赞
收藏
分享

微信扫一扫

手写常见面试题

时光已翩然轻擦 2022-02-14 阅读 43
javascript

手写常见面试题

防抖

防抖函数原理:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。

有两种情况:

  • 点击之后立即执行
  • 点击之后非立即执行
// 非立即执行
const debounce1 = (fn, delay) => {
  let timer = null;
  return (...args) => {
    if(timer) clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, args);
    }, delay)
  }
}

// 立即执行
const debounce2 = (fn, delay) => {
  let timer = null;
  let emitNow = true;
  return (...args) => {
    if(timer) clearTimeout(timer);
    if(emitNow) {
      fn.apply(this, args);
      emitNow = false;
    } else {
      timer = setTimeout(() => {
        fn.apply(this, args);
        emitNow = true;
      }, delay)
    }
  }
}

// 通过参数控制是否立即执行
const debounce3 = (fn, delay, isImmediate) => {
  let timer = null;
  let emitNow = true;
  return (...args) => {
    if(timer) clearTimeout(timer);
    
    if(isImmediate) {
      if(emitNow) {
        fn.apply(this, args);
        emitNow = false;
      } else {
        timer = setTimeout(() => {
          fn.apply(this, args);
          emitNow = true;
        }, delay)
      }
    } else {
      timer = setTimeout(() => {
        fn.apply(this, args);
      }, delay)
    }
  }
}

节流

防抖函数原理:规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。

有两种情况:

  • 点击之后立即执行
  • 点击之后非立即执行
// 非立即执行
const throttle1 = (fn, delay) => {
  let isEmit = false;
  return (...args) => {
    if(isEmit) return;
    isEmit = true;
    
    setTimeout(() => {
      fn.apply(this, args);
      isEmit = false;
    }, delay);
  }
}

// 立即执行
const throttle2 = (fn, delay) => {
  let isEmit = false;
  return (...args) => {
    if(isEmit) return;
    isEmit = true;

    fn.apply(this,args);
    setTimeout(() => {
      isEmit = false;
    },delay);
  }
}

// 通过参数控制是否立即执行
const throttle3 = (fn, delay, isImmediate) => {
  let isEmit = false;
  return (...args) => {
    if(isEmit) return;
    isEmit = true;
    
    if(isImmediate) {
      fn.apply(this, args);
      setTimeout(() => {
        isEmit = false;
      },delay);
    } else {
      setTimeout(() => {
        fn.apply(this, args);
        isEmit = false;
      }, delay);
    }
  }
}

深克隆

我们最常用的是JSON.parse(JSON.stringify(obj))这样的方式来实现克隆,但是这个方式其实存在一些局限。

比如:

  • 无法实现对函数 、RegExp等特殊对象的克隆
  • 会抛弃原始对象的构造函数, 并指向Object
  • 当对象循环引用时,会报错

instanceOf

根据原型链的知识,我们能很快能知道根据对象的__proto__属性就能找到其构造函数。

const instanceOf = function(object, target) {
  // 取目标的原型对象
  const instance = target.prototype;
  // 取待检验的对象的隐式原型
  object = object.__proto__;
  while(true) {
    if(!object) return false;

    if(object === instance) return true;

    object = object.__proto__;
  }
}

new 操作符

new的作用:

  • 创建一个新对象
  • this执行创建的新对象
  • 创建的新对象会被链接到该函数的prototype对象上(新对象的__proto__属性指向函数的prototype);
  • 利用函数的call方法,将原本指向window的绑定对象this指向了obj。(这样一来,当我们向函数中再传递实参时,对象的属性就会被挂载到obj上。)
function createObject() {
  // 创建一个新对象
  const obj = {};
  // 获取构造函数,采用call方法使得arguments能够使用shift方法将第一个参数(构造函数)拿出来
  const Constructor = [].shift.call(arguments);
  // 将对象__proto__属性链接到构造函数的prototype属性中
  obj.__proto__ = Constructor.prototype;
  // 将构造函数中的this指向对象并传递参数
  const result = Constructor.apply(obj, arguments);
  // 确保返回值是一个对象
  return typeof ret === "object" ? result : obj;
}

实现call方法

我们都很清楚call这个方法就是用于修改this指向,但是有些同学可能不太懂其原理,我们来手写一个call方法帮助深入了解其原理。

Function.prototype.mycall = function(context) {
  // 默认上下文为window
  context = context || window;
  // 添加一个属性用于保存当前调用call的函数
  context.fn = this;
  // 将arguments转变成数组并移除第一个参数(上下文)
  const args = [...arguments].slice(1);
  // 这样调用函数时该函数内部的this就指向调用者(context);
  const result = context.fn(...args);
  delete context.fn;
  return result;
}

实现apply方法

apply原理与call很相似,唯一不同就是传参问题,apply方法的第二个参数是所有参数组合成的数组,而call方法除了第一个参数是context外,其他都是传入的参数。

Function.prototype.myapply = function(context, arr) {
  // 默认上下文为window
  context = context || window;
  // 添加一个属性用于保存当前调用call的函数
  context.fn = this;
  // 将arguments转变成数组并移除第一个参数(上下文)
  let result;
  if(!arr) {
    result = context.fn();
  } else {
    result = context.fn(arr);
  }
  delete context.fn;
  return result;
}

实现bind方法

相对于callapply而言,bind方法的返回值是一个改变了this的函数(即非立即调用)。当返回的函数被当作构造函数使用时,this失效,但是传入的参数依旧有效。

Function.prototype.mybind = function(context) {
  if(typeof this !== 'function') {
    throw new Error('Uncaught TypeError: not a function')
  }

  const args = [...arguments].slice(1);
  // 用于记录当前传入的函数的prototype;
  let Transit = function() {};
  const _ = this;
  const FunctionToBind = function() {
    const bindArgs = [...arguments];
    return _.apply(this instanceof Transit ? this : context, args.concat(bindArgs));
  }
  // 记录当前传入的函数的prototype;
  Transit.prototype = this.prototype;
  FunctionToBind.prototype = new Transit();
  return FunctionToBind;
}

代码地址:https://github.com/leopord-lau/EasyPresentation

举报

相关推荐

0 条评论