面试手写第一期

一. 实现call, apply, bind方法

相同点:它们的共同点是都可以修改函数
不同点:this 指向第一个是传参方式不同: call 和 bind 是列表传参,apply是数组或伪数组传参 第二个是执行机制不同:call 和 apply 是立即执行,bind 不会立即执行而是生成一个修改 this之后的新函数

Function.prototype.myCall = function(context) {
    
    
  if(typeof this !== 'function') {
    
    
    throw new Error('type error')
  }
  let args = [...arguments].slice(1)
  context = context || window
  context.fn = this
  let result = context.fn(...args)
  delete context.fn
  return result
}

Function.prototype.myApply = function(context) {
    
    
  if(typeof this !== 'function') {
    
    
    throw new Error('type error')
  }
  let arrParams = arguments[1] || []
  context = context || window
  context.fn = this
  let result = context.fn(...arrParams)
  delete context.fn
  return result
}

Function.prototype.myBind = function(context) {
    
    
  if(typeof this !== 'function') {
    
    
    throw new Error('Error')
  }
  let args = [...arguments].slice(1),
    fn = this
  return function Fn() {
    
    
    return fn.apply(this instanceof Fn ? this : context, args.concat(...arguments))
  }
}

二. 防抖节流函数

2.1 概念

防抖:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。
节流:规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。

2.2 使用场景

在这里插入图片描述

2.3 实现

// 基础版
function debbounce(fn, delay) {
    
    
  let timer
  return function(...args) {
    
    
    if(timer) clearTimeout(timer)
    timer = setTimeout(() => {
    
    
      fn.apply(this, args)
    }, delay)
  }
}

// 防抖函数第一次点击直接执行
function debounce(fn, delay, immediate) {
    
    
  let timer
  return function(...args) {
    
    
    if(timer) clearTimeout(timer)
    if(immediate) {
    
    
      let firstRun = !timer
      if(firstRun) {
    
    
        fn.apply(this, args)
      }
      timer = setTimeout(() => {
    
    
        timer = null
      }, delay)
    } else {
    
    
      timer = setTimeout(() => {
    
    
        fn.apply(this, args)
      }, delay)
    }
  }
}

// 时间戳写法
function throller(fn, delay) {
    
    
  let time
  return function() {
    
    
    let now = Date.now()
    if(now - time > delay) {
    
    
      fn.call(this, arguments)
      time = now
    }
  }
}

// 定时器写法
function throller(fn, delay) {
    
    
  let timer = null
  return function(...args) {
    
    
    if(!timer) {
    
    
      timer = setTimeout(() => {
    
    
        fn.apply(this, args)
        timer = null
      })
    }
  }
}

三. lodash.get的实现

function lodashGet(object, path, defaultValue) {
    
    
  let obj = object
  if(typeof path === 'string') {
    
    
    const reg = /[^\[\]""''.]+/g
    path = path.match(reg)
  }
  for(let key of path) {
    
    
    if(!obj) {
    
    
      return defaultValue
    }
    obj = obj[key]
  }
  return obj
}
const object = {
    
     a: [{
    
     b: {
    
     c: 3 } }] }
console.log(lodashGet(object, 'a[0]["b"]["c"]'))
//=> 3
console.log(lodashGet(object, "a[0].b.c"))
//=> 3
console.log(lodashGet(object, 'a[0]["b"]["c"]'))
//=> 10086
console.log(lodashGet(object, "a[100].b.c", 10086))

四. 深浅拷贝的实现

4.1 概念

浅拷贝:
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存(分支)
浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。
如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。
深拷贝:
深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象,是“值”而不是“引用”(不是分支), 拷贝第一层级的对象属性或数组元素,
递归拷贝所有层级的对象属性和数组元素
深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。

4.2 实现方法

浅拷贝的实现方法:

  • Object.assign
  • 扩展运算符
  • Array.prototype.slice
  • Array.prototype.concat
  • lodash的clone方法
  • 手写实现

深拷贝的实现方法:

  • JSON.parse(JSON.stringfy())
  • lodash实现
  • 手写实现

4.3 手写实现

浅拷贝:

function shallowClone(source) {
    
    
  let target = {
    
    };
  for(let i in source) {
    
    
    if (source.hasOwnProperty(i)) {
    
    
      target[i] = source[i];
    }
  }
  return target;
}

深拷贝:

const obj = {
    
    
  re: /hello/,
  f() {
    
    },
  date: new Date(),
  map: new Map(),
  list: [1, 2, 3],
  a: 3,
  b: 4,
};

function deepClone(source, cache = new WeakMap()) {
    
    
  if(typeof source !== 'object') {
    
    
    return source
  }
  if(cache.has(source)) {
    
    
    return cache.get(source)
  }
  let res = new source.constructor()
  cache.set(source, res)

  if(source instanceof Array) {
    
    
    source.forEach((v) => {
    
    
      res.push(deepClone(v, cache))
    })
  } else if (source instanceof Map) {
    
    
    for(const [k, v] of source) {
    
    
      res.set(k, deepClone(v, cache))
    }
  } else if (source instanceof Set) {
    
    
    for(const v of source) {
    
    
      res.add(deepClone(v, cache))
    }
  } else if (Object.prototype.toString.call(source) == '[object Object]') {
    
    
    for(const key in source) {
    
    
      res[key] = deepClone(source[key], cache)
    }
  } else {
    
    
    res = new source.constructor(source)
  }
  return res
}
const newObj = deepClone(obj);
console.log(newObj);

五. flat拍平实现

function flatten(arr, depth) {
    
    
  if(!depth) return arr
  let res = []
  for(let i = 0; i < arr.length; i++) {
    
    
    if(Array.isArray(arr[i])) {
    
    
      res = res.concat(flatten(arr[i], --depth))
    } else {
    
    
      res.push(arr[i])
    }
  }
  return res
}

console.log(flatten([1, 2, 3, [4, [5, 6]]], 1));
function flatten(arr, depth) {
    
    
  while(arr.some(item => Array.isArray(item) && depth)) {
    
    
    arr = [].concat(...arr)
    depth--
  }
  return arr
}
console.log(flatten([1, 2, 3, [4, [5, 6]]], 1));
function flatten(arr, depth = 1) {
    
    
  if (depth === 0) return arr;
  return arr.reduce(
    (a, b) => a.concat(Array.isArray(b) ? flatten(b, depth - 1) : b),
    [],
  );
}
console.log(flatten([1, 2, 3, [4, [5, 6]]], 1));
function flatten(arr) {
    
    
  return arr.toString().split(',').map(item => parseInt(item))
}
console.log(flatten([1, 2, 3, [4, [5, 6]]]));

六. 手写实现LRU算法

class LRUCache {
    
    
  constructor(capacity) {
    
    
    this.capacity = capacity;
    this.cache = new Map();
  }
  get(key) {
    
    
    if (!this.cache.has(key)) {
    
    
      return -1;
    }
    const temp = this.cache.get(key);
    this.cache.delete(key);
    this.cache.set(key, temp);
    return temp;
  }
  put(key, value) {
    
    
    if (this.cache.has(key)) {
    
    
      this.cache.delete(key);
    } else if (this.cache.size >= this.capacity) {
    
    
      this.cache.delete(this.cache.keys().next().value);
    }
    this.cache.set(key, value);
  }
}

七. 实现once方法,只执行一次

function once(f) {
    
    
  let result;
  let revoked = false;
  return (...args) => {
    
    
    if (revoked) return result;
    const r = f(...args);
    revoked = true;
    result = r;
    return r;
  };
}

八. 对字符串进行压缩编码

//=> a4b3c2
encode("aaaabbbcc");
 
//=> a4b3a4
encode("aaaabbbaaaa");
 
//=> a2b2c2
encode("aabbcc");

function encode(str) {
    
    
  let count = 1
  prev = ''
  res = ''
  for(const v of str) {
    
    
    if(v == prev) {
    
    
      count++
    } else {
    
    
      res += `${
      
      count}${
      
      prev}`
      count = 1
    }
    prev = v
  }
  res +=`${
      
      count}${
      
      prev}`
  return res
}

console.log(encode('aaabbc'));

猜你喜欢

转载自blog.csdn.net/qq_63299825/article/details/135911628