手撕前端面试十几种常考的手写题(一篇就够)

废话不多说,直接上代码

防抖

function JLDebounce(fn, deplay){
  let timer = null;
  const _debounce = function(...args){
    if(timer) clearInterval(timer);
    timer = setTimeout(() => {
      fn.apply(this, args);
    }, deplay);
  }
  return _debounce;
}

节流

function JLThrottle(fn, interval){
  // 记录上一次的实践
  let lastTime = 0;
  // 事件触发真正的函数
  const _throttle = function(...args){
    // 记录当前时间
    let nowTime = new Date().getTime();
    // 计算剩余时间
    let remainTime = interval - (nowTime - lastTime);
    // 判断剩余时间小于等于0则执行函数
    if(remainTime <= 0){
      fn.apply(this, args);
      lastTime = nowTime;
    }
  }
  return _throttle;
}

柯里化

function TestCurring(fn){
    function curried(...args1){
        if(args1.length >= fn.length){
            return fn.apply(this, args1);
        }else{
            return function(...args2){
                return curried.apply(this, args1.concat(args2));
            }
        }
    }
    return curried;
}

深拷贝

//工具函数
function isObject(value){
  const TYValue = typeof value;
  return (value !== null) && (TYValue === "object" || TYValue === "function")
}

function JLDeepClone(originValue, map = new WeakMap()){
  // 判断是否为Set类型
  if(originValue instanceof Set){
    return new Set([...originValue]);
  }

  // 判断是否为Map类型
  if(originValue instanceof Map){
    return new Map([...originValue])
  }

  // 判断是否为函数类型
  if(typeof originValue === "function"){
    return originValue;
  }

  // 判断是否为symbol类型
  if(typeof originValue === "symbol"){
    return Symbol(originValue.description);
  }

  //判断是否为数组类型
  const newObj = Array.isArray(originValue) ? [] : {};
  map.set(originValue, newObj);
  for(const key in originValue){
    newObj[key] = JLDeepClone(originValue[key])
  }

  // 判断是否为对象类型
  if(!isObject(originValue)){
    return originValue
  }

  // 循环引用
  if(map.has(originValue)){
    return map.get(originValue)
  }

  // 将symbol的key特殊处理
  const symbolKeys = Object.getOwnPropertySymbols(originValue);
  for(const sKey of symbolKeys){
    newObj[sKey] = JLDeepClone(originValue[sKey], map);
  }
  return newObj;
}

数组扁平化

function JLFlat1(arr){
  let res = [];
  for(let i = 0; i < arr.length; i++){
    if(Array.isArray(arr[i])){
      res = res.concat(JLFlat(arr[i]));
    }else{
      res.push(arr[i]);
    }
  }
  return res;
}

function JLFlat2(arr, depth = 1) {
  const res = [];
  arr.forEach(item => {
    if (Array.isArray(item) && depth > 1) {
      res.push(...JLFlat2(item, depth - 1));
    } else {
      res.push(item);
    }
  });

  return res;
}

对象扁平化

function JLObjectFlat(obj = ''){
  const res = {};

  function flat(item , preKey = ''){
    Object.entries(item).forEach(([key,value]) => {
      let newKey = key;
      if (Array.isArray(item)){
        newKey = preKey ? `${preKey}[${key}]` : key;
      }else{
        newKey = preKey ? `${preKey}.${key}` : key;
      }
      if (value && typeof value === 'object'){
        flat(value , newKey)
      }else{
        res[newKey] = value;
      }
    })
  }

  flat(obj);
  return res;
}

map函数

Array.prototype.JLMap = function(fn, thisValue){
  let res = [];
  thisValue = thisValue || [];
  for(let key in thisValue){
    res.push(fn(thisValue[key]));
  }
  return res;
}

ajax

function JLAjax({
  url,
  method = "get",
  data = {},
  timeout = 10000,
  headers = {}, 
  success,
  failure
} = {}) {
  const xhr = new XMLHttpRequest()
  
  xhr.onload = function() {
    if (xhr.status >= 200 && xhr.status < 300) {
      success && success(xhr.response)
    } else {
      failure && failure({ status: xhr.status, message: xhr.statusText })
    }
  }

  xhr.responseType = "json"
  xhr.timeout = timeout

  if (method.toUpperCase() === "GET") {
    const queryStrings = []
    for (const key in data) {
      queryStrings.push(`${key}=${data[key]}`)
    }
    url = url + "?" + queryStrings.join("&")
    xhr.open(method, url)
    xhr.send()
  } else {
    xhr.open(method, url)
    xhr.setRequestHeader("Content-type", "application/json")
    xhr.send(JSON.stringify(data))
  }

  return xhr
}

ajax-promise实现

function AJAXPromise({
  url,
  method = 'get',
  data = {},
  timeout = 1000
} = {}) {
  const xhr = new XMLHttpRequest();

  const promise = new Promise((resolve, reject) => {
    xhr.onload = function () {
      if (xhr.status >= 200 && xhr.status < 300) {
        resolve(xhr.response);
      } else {
        reject({status: xhr.status, message: xhr.responseText});
      }
    }

    xhr.timeout = timeout;
    xhr.responseType = "json";

    if (method.toUpperCase() === 'GET') {
      const queryStrings = [];
      for (let ket in data) {
        queryStrings.push(`${key}=${data[key]}`);
      }
      url = url + "?" + queryStrings.join('&');
      xhr.open(method, url);
      xhr.send();
    } else {
      xhr.open(method, url);
      xhr.setRequestHeader("Content-type", "application/json");
      xhr.send(data);
    }
  })

  promise.xhr = xhr;

  return promise;
}

apply

Function.prototype.TestApply = function(thisArg, argArr){
    let fn = this;
    thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) :window;
    thisArg.fn = fn;
    argArr = argArr || [];
    let result = thisArg.fn(...argArr);
    delete thisArg.fn;
    return result;
}

call

Function.prototype.TestCall = function(thisArg, argArr){
    let fn = this;
    thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window;
    thisArg.fn = fn;
    let result = thisArg.fn(...argArr);
    delete thisArg.fn;
    return result;
}

bind

Function.prototype.TestBind = function(thisArg, argArr){
    let fn = this;
    thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window;
    return function (...args){
        let finalyArr = [...argArr, ...args];
        thisArg.fn = fn;
        let result = thisArg.fn(...finalyArr);
        delete thisArg.fn;
        return result;
    }
}

new

function JLNew(context, ...args){
  let obj = {};
  obj.__proto__ = context.prototype;
  let res = context.apply(obj, args);
  return res instanceof Object ? res : obj;
}

instanceof

function JLInstanceof(target, origin){
  while(target){
    if(target.__proto__ === origin.prototype){
      return true;
    }
    target = target.__proto__;
  }
  return false;
}

事件总线

class JLEventBus{
  constructor(){
    this.eventBus = {}
  }

  on(eventName, eventCallback, thisArg){
    let handlers = this.eventBus[eventName];
    if(!handlers){
      handlers = [];
      this.eventBus[eventName] = handlers;
    }
    handlers.push({
      eventCallback,
      thisArg
    })
  }

  off(eventName, eventCallback){
    const handlers = this.eventBus[eventName];
    if(!handlers) return
    const newHandlers = [...handlers];
    for(let i = 0; i < newHandlers.length; i++){
      const handler = newHandlers[i];
      if(handler.eventCallback === eventCallback){
        const index = handlers.indexOf(handler)
        handlers.splice(index, 1)
      }
    }
  }

  emit(eventName, ...payload){
    const handlers = this.eventBus[eventName];
    if(!handlers) return 
    handlers.forEach(handler => {
      handler.eventCallback.apply(handler.thisArg, payload)
    })
  }
}

响应式

class Depend {
  constructor() {
    this.reactiveFns = new Set();
  }

  addDepend(fn) {
    this.reactiveFns.add(fn);
  }

  depend() {
    if (reactiveFn) {
      this.reactiveFns.add(reactiveFn);
    }
  }

  notify() {
    this.reactiveFns.forEach(fn => {
      fn();
    })
  }
}

let reactiveFn = null;
function watchFn(fn) {
  reactiveFn = fn;
  fn();
  reactiveFn = null;
}

const objMap = new WeakMap();
function getDepend(obj, key) {
  let map = objMap.get(obj);
  if (!map) {
    map = new Map();
    objMap.set(obj, map);
  }

  let dep = map.get(key);
  if (!dep) {
    dep = new Depend();
    map.set(key, dep);
  }

  return dep;
}

//vue2
function reactiveVue2(obj) {
  Object.key(obj).forEach(key => {
    let value = obj[key];

    Object.defineProperty(obj, key, {
      set(newValue) {
        value = newValue;
        const dep = getDepend(obj, key);
        dep.notify();
      },
      get() {
        const dep = getDepend(obj, key);
        dep.depend();
        return value;
      }
    })
  })

  return obj;
}

//vue3
function reactiveVue3(obj) {
  return new Proxy(obj, {
    set(target, key, newValue, receiver) {
      Reflect.set(target, key, newValue, receiver);
      const dep = getDepend(target, key);
      dep.notify();
    },
    get(target, key, receiver) {
      const dep = getDepend(target, key);
      dep.depend();
      return Reflect.get(target, key, receiver);
    }
  })
}


简易版响应式

function reactive(obj){
  if(typeof obj !== 'object' && obj !== null){
    return obj;
  }

  return new Proxy(obj, {
    get(target, key, receiver){
      return Reflect.get(target, key, receiver);
    },
    set(target, key, value, receiver){
      return Reflect.set(target, key, value, receiver);
    },
    deleteProperty(target, key){
      return Reflect.deleteProperty(target, key);
    }
  })
}

Promise

const PROMISE_STATUS_PENDING = 'pending';
const PROMISE_STATUS_FULFILLED = 'fulfilled';
const PROMISE_STATUS_REJECTED = 'rejected';

//工具函数
function exectorWithCatchError(execfn, value, resolve, reject){
    try{
        const result = execfn(value);
        resolve(result);
    }catch(err){
        reject(err);
    }
}

class TestPromise {
    constructor(executor){
        this.status = PROMISE_STATUS_PENDING;
        this.value = undefined;
        this.reason = undefined;
        this.onFuifilledFns = [];
        this.onRejectedFns = [];

        //实现resolve方法
        const resolve = () => {
            if(this.status === PROMISE_STATUS_PENDING){
                queueMicrotask(() => {
                    if(this.status !== PROMISE_STATUS_PENDING) return ;
                    this.status = PROMISE_STATUS_FULFILLED;
                    this.value = value;
                    this.onFuifilledFns.forEach(fn => {
                        fn(this.value);
                    })
                })
            }
        }

        //实现reject方法
        const reject = () => {
            if(this.status === PROMISE_STATUS_PENDING){
                queueMicrotask(() => {
                    if(this.sttus !== PROMISE_STATUS_PENDING) return ;
                    this.reason = reason;
                    this.status = PROMISE_STATUS_REJECTED;
                    this.onRejectedFns.forEach(fn => {
                        fn(this.reason);
                    })
                })
            }
        }

        try{
            executor(resolve, reject);
        }catch(err){
            reject(err);
        }
    }
        //实现then方法
        then(onFulfilled, onRejected){
            const defaultOnRejected = err => { throw err };
            onRejected = onRejected || defaultOnRejected;

            const defaultOnFulfilled = value => { return value };
            onFulfilled = onFulfilled || defaultOnFulfilled;

            return new TestPromise((resolve, reject) => {
                if(this.status === PROMISE_STATUS_FULFILLED && onFulfilled){
                    exectorWithCatchError(onFulfilled, this.value, resolve, reject);
                }
                if(this.status === PROMISE_STATUS_REJECTED && onRejected){
                    exectorWithCatchError(onRejected, this.reason, resolve, reject);
                }

                //将成功的回调或者失败的回调存在数组中
                if(this.status === PROMISE_STATUS_PENDING){
                    if(this.onFulfilled) this.onFuifilledFns.push(() => {
                        exectorWithCatchError(onFulfilled, this.value, resolve, reject);
                    })
                    if(this.onRejected) this.onRejectedFns.push(() => {
                        exectorWithCatchError(onRejected, this.reason, resolve, reject);
                    })
                }
            })
        }
    catch(onRejected){
        return this.then(undefined, reject);
    }
    
    finaly(onFinaly){
        this.then(() => {
            onFinaly();
        },() => {
            onFinaly();
        })
    }

    static resolve(value){
        return new TestPromise((resolve) => resolve(value));
    }

    static reject(reason){
        return new TestPromise((resolva, reject) => reject(reason));
    }

    static all(promises){
        return new TestPromise((resolve, reject) => {
            const values = [];
            promises.forEach(promise => {
                promise.then(res => {
                    values.push(res);
                    if(values.length === promises.length){
                        resolve(values);
                    }
                },err => {
                    reject(err);
                })
            })
        })
    }

    static allSetted(promises){
        return new TestPromise((resolve) => {
            const results = [];
            promises.forEach(promise => {
                promise.then(res => {
                    results.push({ status : PROMISE_STATUS_FULFILLED , value : res })
                    if(results.length === promises.lrngth){
                        resolve(results);
                    }
                }, err => {
                    results.push({ status : PROMISE_STATUS_REJECTED , value : err })
                    if(results.length === promises.length){
                        resolve(results);
                    }
                })
            })
        })
    }

    static race(peomises){
        return new TestPromise((resolve, reject) => {
            promises.forEach(promise => {
                promise.then(resolve, reject)
            })
        })
    }

    static any(promise){
        const results = [];
        return new TestPromise((resolve, reject) => {
            promises.forEach(promise => {
                promise.then(resolve, err => {
                    results.push(err);
                    if(results.length === promises.length){
                        reject(new AggregateError(results));
                    }
                })
            })
        })
    }
}

Promise.all

Promise.JLAll = function(promiseArr){
  return new Promise((resolve, reject) => {
    const result = [];
    let index = 0;
    for(let i =0; i < promiseArr.length; i++){
      promiseArr[i]
        .then(res => {
          result[i] = res;
          index++;
          if(index === promiseArr.length){
            resolve(result);
          }
        })
        .catch(err => reject(err));
    }
  })
}

Promise.race

Promise.JLRace = function(promiseArr) {
  return new Promise((resolve, reject) => {
    promiseArr.forEach(p => {
      Promise.resolve(p).then(
        res => resolve(res),
        err => reject(err)
      )
    })
  })
}

手写自定义类的迭代

class Person1 {
  constructor(name, age, height, friends) {
    this.name = name;
    this.age = age;
    this.height = height;
    this.friends = friends;
  }

  [Symbol.iterator]() {
    let index = 0;
    const iterator = {
      next: () => {
        if (index < this.friends.length) {
          return {done: false, value: this.friends[index++]};
        } else {
          return {done: true};
        }
      }
    }

    return iterator;
  }
}

const p1 = new Person1('JL', 18, 1.88, ["curry", 'kobe']);

for (let item of p1) {
  console.log(item);
}

//yield改进版
class Person2 {
  constructor(name, age, friends) {
    this.name = name;
    this.age = age;
    this.friends = friends;
  }

  *[Symbol.iterator]() {
    yield* this.friends;
  }
}

const p2 = new Person2('JL', 18, ['curry', 'kobe']);

for (let item of p) {
  console.log(item);
}

插入排序

function insertSort(arr){
  for(let i = 0; i < arr.length; i++){
    let j = i;
    let target = arr[j];
    while(j > 0 && arr[j -1] > target){
      arr[j] = arr[j - 1];
      j--;
    }
    arr[j] = target;
  }
  return arr;
}

冒泡排序

function bubblesort(arr){
  for(let i = 0; i < arr.length; i++){
    for(let j = 0; j < arr.length - 1; j++){
      if(arr[j] > arr[j + 1]){
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
      }
    }
  }
  return arr;
}

选择排序

function selectSort(arr){
  let min;
  for(let i = 0; i < arr.length - 1; i++){
    min = i;
    for(let j = 0; j < arr.length; j++){
      if(arr[j] < arr[min]){
        min = j;
      }
    }
    if(min !== i){
      [arr[i], arr[min]] = [arr[min], arr[i]];
    }
  }
  return arr;
}

快排

//阮一峰老师版本
function quickSortRuan(arr){
  if(arr.length === 1) return arr;
  let pivotIndex = Math.floor(arr.length / 2);
  let pivot = arr.splice(pivotIndex, 1)[0];
  let left = [];
  let right = [];

  for(let i = 0; i < arr.length; i++){
    if(arr[i] < pivot){
      left.push(arr[i]);
    }else{
      right.push(arr[i]);
    }
  }

  return quickSortRuan(left).concat([pivot], quickSortRuan(right));
}

//改进版
function quickSort(arr){
  if(arr.length <= 1){
    return arr;
  }

  let left = [], right = [], container = arr.splice(0, 1);

  for(let i = 0; i < arr.length; i++){
    if(arr[i] < container){
      left.push(arr[i]);
    }else{
      right.push(arr[i]);
    }
  }

  return quickSort(left).concat(container, quickSort(right));
}

console.log(quickSort([8, 2, 5, 1, 4, 9, 3, 7, 6])); //[1, 2, 3, 4, 5, 6, 7, 8, 9]

JSONP

const jsonp = ({url, params, callbackName}) => {
 const getUrl = () => {
   let dataSrc = '';
   for(let key in params){
     if(params.hasOwnProperty(key)){
       dataSrc += `${key}=${params[key]}&`;
     }
   }
   dataSrc += `callback=${callbackName}`;
   return `${url}?${dataSrc}`;
 }

 return new Promise((resolve, reject) => {
   const scriptEle = document.createElement('script');
   scriptEle.src = getUrl();
   document.body.appendChild(scriptEle);
   window[callback] = data => {
     resolve(data);
     document.removeChild(scriptEle);
   }
 })
}

继承

原型链继承
function Animal(){
  this.colors = ['black', 'white'];
}

Animal.prototype.getColor = function (){
  return this.colors;
}

function Dog(){}
Dog.prototype = new Animal();

let dog1 = new Dog();
dog1.colors.push('red');
console.log(dog1);

let dog2 = new Dog();
console.log(dog2);
构造函数继承
function Animal(name){
  this.name = name;
  this.getName = function(){
    return this.name;
  }
}

function Dog(name){
  Animal.call(this, name);
}

Dog.prototype = new Animal();

组合继承
function Animal(name){
  this.name = name;
  this.colors = ['black', 'white'];
}

Animal.prototype.getName = function(){
  return this.name;
}

function Dog(name, age){
  Animal.call(this, name);
  this.age = age;
}

Dog.prototype = new Animal();
Dog.prototype.constructor = Dog;

class继承
class Animal{
  constructor(name){
    this.name = name;
  }

  getName (){
    return this.name;
  }
}

class Dog extends Animal{
  constructor(name, age){
    super(name);
    this.age = age;
  }
}

圣杯布局

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>圣杯布局</title>
  <style>
    *{
      margin: 0;
      padding: 0;
      text-align: center;
    }
    .header,.footer{
      height: 30px;
      width: 100%;
      background: red;
    }
    .center{
      display: flex;
    }
    .left{
      width: 200px;
      background-color: pink;
    }
    .right{
      width: 200px;
      background-color: blue;
    }
    .middle{
      flex: 1;
      background-color: yellow;
    }
  </style>
</head>
<body>
  <div class="header">头部</div>
  <div class="center">
    <div class="left">左边</div>
    <div class="middle">中间</div>
    <div class="right">右边</div>
  </div>
  <div class="footer">底部</div>
</body>
</html>

双飞翼布局

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>双飞翼布局</title>
  <style>
    *{
      margin: 0;
      padding: 0;
    }
    .container{
      min-width: 600px;
    }
    .left{
      float: left;
      width: 200px;
      height: 400px;
      background:red;
      margin-left: -100%;
    }
    .center{
      float: left;
      width: 100%;
      height: 500px;
      background: yellow;
    }
    .center .inner{
      margin: 0 200px;
    }
    .right{
      float: left;
      width: 200px;
      height: 400px;
      background: blue;
      margin-left: -200px;
    }
  </style>
</head>
<body>
  <div class="container">
    <div class="center">
      <div class="inner">双飞翼布局</div>
    </div>
    <div class="left"></div>
    <div class="right"></div>
  </div>
</body>
</html>

水平垂直居中

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>居中1</title>
  <style>
    .parent{
      width: 200px;
      height: 200px;
      border: 1px solid red;
      position: relative;
    }
    .children{
      position: absolute;
      width: 100px;
      height: 100px;
      background-color: yellow;
      top: 50%;
      left: 50%;
      margin-top: -50px;
      margin-left: -50px;
    }
  </style>
</head>
<body>
  <div class="parent">
    <div class="children"></div>
  </div>
</body>
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>居中2</title>
  <style>
    .parent{
      width: 200px;
      height: 200px;
      border: 1px solid red;
      position: relative;
    }
    .children{
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      width: 100px;
      height: 100px;
      background-color: yellow;
      margin: auto;
    }
  </style>
</head>
<body>
  <div class="parent">
    <div class="children"></div>
  </div>
</body>
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>居中3</title>
  <style>
    .parent{
      width: 200px;
      height: 200px;
      border: 1px solid red;
      position: relative;
    }
    .children{
      position: absolute;
      width: 100px;
      height: 100px;
      background-color: yellow;
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%);
    }
  </style>
</head>
<body>
  <div class="parent">
    <div class="children"></div>
  </div>
</body>
  • 2
    点赞
  • 5
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:我行我“速” 设计师:Amelia_0503 返回首页
评论

打赏作者

Noria 107

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值