一文解决前端面试④—-必备的前端手写

写在前面

在前端的学习中,不仅要学会用,一些基础功能的手写也非常重要

食用对象:初级前端
美味指数:?????

1. 实现可拖拽DIV

<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style type="text/css">
        #box {
            width: 100px;
            height: 100px;
            background: #07d268;
            position: absolute;
            left: 100px;
            top: 100px;
        }
    </style>
</head>
<body>
<div id="box">1</div>
</body>
<script>
  const box = document.querySelector('#box');
  let drag = false;
  let position = [];
  box.addEventListener('mousedown', (e) => {
    drag = true
    position = [e.clientX, e.clientY]
  })
  document.addEventListener('mousemove', (e) => {
    if (drag !== true) {
      return
    }
    const x = e.clientX;
    const y = e.clientY;
    const left = parseInt(getComputedStyle(box).left || 0);    //js获取head里css需要用getComputedStyle
    const top = parseInt(getComputedStyle(box).top || 0);
    box.style.left = left + x - position[0] + 'px'
    box.style.top = top + y - position[1] + 'px'
    position = [x, y]
  })
  document.addEventListener('mouseup', () => {
    drag = false
  })
</script>
复制代码

2. 双飞翼布局

    <style>
        body {
            min-width: 550px
        }

        .column {
            float: left;
        }

        #center {
            width: 100%;
            height:200px;
            background-color: #cccccc;
        }

        #main {
            margin: 0 190px 0 190px;
        }

        #left {
            background-color: #ffff00;
            width: 190px;
            height:200px;
            margin-left: -100%;
        }
        #right {
            background-color: red;
            width: 190px;
            height:200px;
            margin-left: -190px;
        }
    </style>

    <div id="center" class="column">
        <div id="main">
            this is center
        </div>
    </div>
    <div id="left" class="column">this is left</div>
    <div id="right" class="column">this is right</div>
复制代码

3. 圣杯布局

    <style>
        body {
            min-width: 550px
        }

        #header {
            text-align: center;
        }

        #container {
            padding-left: 200px;
            padding-right: 150px;
        }

        #container .column {
            float: left
        }

        #center {
            background-color: #ccc;
            width: 100%
        }

        #left {
            position: relative;
            background-color: yellow;
            width: 200px;
            margin-left: -100%;
            right: 200px
        }

        #right {
            background-color: red;
            width: 150px;
            margin-right: -150px;
        }

        #footer {
            clear: both;
            text-align: center;
            background-color: #f1f1f1
        }
    </style>

    <div id="container">
        <div id="center" class="column">this is center</div>
        <div id="left" class="column">this is left</div>
        <div id="right" class="column">this is right</div>
    </div>
复制代码

4. 两栏布局

  • 绝对定位方式
    <style>
        .wrap {
            position: relative;
            width: 100%;
            height: 200px;
        }
        .box1 {
            position: absolute;
            width: 150px;
            height: 200px;
            background-color: #d43131;
        }
        .box2 {
            position: absolute;
            left: 150px;
            right: 0;
            height: 200px;
            background-color: #07d268;
        }
    </style>
    <div class="wrap">
        <div class="box1">1</div>
        <div class="box2">2</div>
    </div>
复制代码
  • 浮动方式
    <style>
        .box1 {
            float: left;
            width: 150px;
            height: 200px;
            background-color: #d43131;
        }
        .box2 {
            margin-left: 150px;
            height: 200px;
            background-color: #07d268;
        }
    </style>
    <div class="wrap">
        <div class="box1">1</div>
        <div class="box2">2</div>
    </div>
复制代码
  • flex方式
    <style>
        .wrap {
            display: flex;
            height: 200px;
        }
        .box1 {
            width: 150px;
            height: 200px;
            background-color: #d43131;
        }
        .box2 {
            flex: 1;
            height: 200px;
            background-color: #07d268;
        }
    </style>
    <div class="wrap">
        <div class="box1">1</div>
        <div class="box2">2</div>
    </div>
复制代码

5. 画三角形或梯形⭐⭐⭐⭐

  • CSS方法:
<style>
    .box {
        width: 0;
        height: 0;
        border-right: 100px solid transparent;
        border-left: 100px solid transparent;
        border-top: 100px solid #d43131;
        border-bottom: 100px solid transparent;
    }
</style>
    <div class="box"></div>

复制代码

image.png
如果需要画梯形就改变width的值即可

  • Canvas方法:
<style>
    #box2 {
        width: 300px;
        height: 300px;
    }
</style>
    <canvas id="box2"></canvas>
    const canvas = document.querySelector('#box2')
    const ctx = canvas.getContext('2d')
    ctx.beginPath()
    ctx.moveTo(0, 0);
    ctx.lineTo(100, 0);
    ctx.lineTo(0, 50);
    ctx.closePath(); 
    ctx.fillStyle="#8989e7";
    ctx.fill();
复制代码

image.png

6. reduce模拟map

Array.prototype._map = function(fn, callbackThis) {
  // 最终返回的新数组
  let res = [];
  // 定义回调函数的执行环境
  // call第一个参数传入null,则 this指向全局对象,同 map的规则
  let CBThis = callbackThis || null;
  this.reduce((brfore, after, idx, arr) => {
    // 传入map回调函数拥有的参数
    // 把每一项的执行结果push进res中
    res.push(fn.call(CBThis, after, idx, arr));
  }, null);
  return res;
};
复制代码

7. 手写flatter考虑多层级

function flat(arr) {
  const isDeep = arr.some(item => item instanceof Array)
  if (!isDeep) {
    return arr  //已经是flatern
  }
  const res = [].concat(...arr)   //arr里的每个元素都会通过concat连接
  return flat(res)
}
复制代码

8. 手写new

// 手写一个new
function myNew(fn, ...args) {
  // 创建一个空对象
  let obj = {}
  // 使空对象的隐式原型指向原函数的显式原型
  obj.__proto__ = fn.prototype
  // this指向obj
  let result = fn.apply(obj, args)
  // 返回
  return result instanceof Object ? result : obj
}
复制代码

9. 手写trim

String.prototype.trim = function () {
  return this.replace(/^\s+/, '').replace(/\s+$/, '')
}
复制代码

10. 手写柯里化

//经典面试题:实现add(1)(2)(3)(4)=10;add(1)(1,2,3)(2)=9
//指的是将一个接受多个参数的函数,变为接受一个参数返回一个函数的固定形式
//这样便于再次调用,例如f(1)(2)
function add() {
  let args = Array.from(arguments)
  let adder = function() {
    args.push(...arguments)
    return adder
  }
  adder.toString = function() {
    return args.reduce(function(a, b){
      return a + b
    }, 0)
  }
  return adder
}
let a = add(1,2,3)
let b = add(1)(2)(3)
console.log(a)
console.log(b)
console.log(a==6)
console.log(b==6)
复制代码

11. 手写深度比较

//判断是否是对象或数组
function isObject(obj) {
  return typeof obj === 'object' && obj !== 'null'
}

function isEqual(obj1, obj2) {
  if (!isObject(obj1) || !isObject(obj2)) {
    //值类型(注意,参与equal的一般不会是函数)
    return obj1 === obj2
  }
  //两个都是对象或数组,而且不相等
  //1.先取出obj1和obj2的keys,比较个数
  const obj1Keys = Object.keys(obj1)
  const obj2Keys = Object.keys(obj2)
  if (obj1Keys.length !== obj2Keys.length) {
    return false
  }
  //2.以obj1为基准,和obj2一次递归比较
  for (let key in obj1) {
    if (!isEqual(obj1[key], obj2[key])) {
      return false
    }
  }
  return true
}
复制代码

12. 手写深拷贝

function deepClone(obj = {}) {
  if(typeof obj !== 'object' || obj == null) {
    //obj是null,或者不是对象或数组,直接返回
    return obj;
  }
  //初始化返回结果
  let result;
  if (obj instanceof Array) {
    result = [];
  } else {
    result = {};
  }

  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      //递归调用
      result[key] = deepClone(obj[key]);
    }
  }
  return result;
}
复制代码

13. 手写apply

Function.prototype.myApply = function(context) {
  const ctx = context || window
  //最重要的一步,1.myApply内部的this是指向调用者fn函数的。2.ctx.func就是fn函数,ctx调用了fn函数,因此fn函数内部的this指向ctx
  ctx.func = this
  let result = arguments[1] ? ctx.func([...arguments[1]]) : ctx.func()
  delete ctx.func
  return result
}

obj = {c: 2}

function a(x, y) {
  console.log(this, x, y)
}

a.apply(obj, [1, 2])//{c:2} 1 2
a.myApply(obj, [1, 2])//{c:2,func:[function a]} 1 2
复制代码

14. 手写bind

//模拟bind
Function.prototype.bind1 = function () {
  //将参数拆解为数组
  const args = Array.prototype.slice.call(arguments);

  //获取this(数组第一项)
  const t = args.shift();

  //fn1.bind(...)中的fn1
  const self = this

  //返回一个函数
  return function () {
    return self.apply(t, args)
  }
}
复制代码

15. 手写发布订阅模式

// 发布订阅模式
class EventEmitter {
  constructor() {
    // 事件对象,存放订阅的名字和事件
    this.events = {};
  }
  // 订阅事件的方法
  on(eventName,callback) {
    if (!this.events[eventName]) {
      // 注意时数据,一个名字可以订阅多个事件函数
      this.events[eventName] = [callback]
    } else {
      // 存在则push到指定数组的尾部保存
      this.events[eventName].push(callback)
    }
  }
  // 触发事件的方法
  emit(eventName) {
    // 遍历执行所有订阅的事件
    this.events[eventName] && this.events[eventName].forEach(cb => cb());
  }
  // 移除订阅事件
  removeListener(eventName, callback) {
    if (this.events[eventName]) {
      this.events[eventName] = this.events[eventName].filter(cb => cb !== callback)
    }
  }
  // 只执行一次订阅的事件,然后移除
  once(eventName,callback) {
    // 绑定的时fn, 执行的时候会触发fn函数
    let fn = () => {
      callback(); // fn函数中调用原有的callback
      this.removeListener(eventName,fn); // 删除fn, 再次执行的时候之后执行一次
    }
    this.on(eventName,fn)
  }
}
复制代码

16. 手写promise

class MyPromise2 {
  constructor(executor) {
    // 规定状态
    this.state = "pending"
    // 保存 `resolve(res)` 的res值
    this.value = undefined
    // 保存 `reject(err)` 的err值
    this.reason = undefined
    // 成功存放的数组
    this.successCB = []
    // 失败存放的数组
    this.failCB = []


    let resolve = (value) => {
      if (this.state === "pending") {
        this.state = "fulfilled"
        this.value = value
        this.successCB.forEach(f => f())
      }
    }
    let reject = (reason) => {
      if (this.state === "pending") {
        this.state = "rejected"
        this.reason = reason
        this.failCB.forEach(f => f())
      }
    }

    try {
      // 执行
      executor(resolve, reject)
    } catch (error) {
      // 若出错,直接调用reject
      reject(error)
    }
  }
  then(onFulfilled, onRejected) {
    if (this.state === "fulfilled") {
      onFulfilled(this.value)
    }
    if (this.state === "rejected") {
      onRejected(this.reason)
    }
    if (this.state === "pending") {
      this.successCB.push(() => { onFulfilled(this.value) })
      this.failCB.push(() => { onRejected(this.reason) })
    }
  }
}
复制代码

17. 手写promise.all

Promise.all = function (promises) {
  let list = []
  let count = 0
  return new Promise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) {
      Promise.resolve(promises[i]).then(res => {
        list[i] = res
        count ++
        if (count === promises.length) {
          resolve(list)
        }
      }, err => reject(err))
    }
  })
}
复制代码

18. 大数相加

function add(a, b) {
  var maxLength = Math.max(a.length, b.length)
  a = a.padStart(maxLength, 0)
  b = b.padStart(maxLength, 0)
  var t = 0
  var f = 0
  var sum = ''
  for(let i = a.length - 1; i >= 0; i--) {
    t = parseInt(a[i]) + parseInt(b[i]) + f
    f = Math.floor(t / 10)
    sum = t % 10 + sum
    console.log(sum)
  }
  if(f === 1){
    sum = '1' + sum
  }
  return sum
}
复制代码

19. 手写防抖函数

function debounce(fn, delay = 500) {
  let timer = null
  return function () {
    if (timer) {
      clearTimeout(timer)
    }
    timer = setTimeout(() => {
      //为了使函数的参数和this正确
      fn.apply(this, arguments)
      timer = null
    }, delay)
  }
}
复制代码

20. 手写节流函数

function throttle(fn, delay = 100) {
  let timer = null
  return function () {
    if (timer) {
      return
    }
    timer = setTimeout(() => {
      fn.apply(this, arguments)
      timer = null
    }, delay)
  }
}
复制代码

21. 手写快排

Array.prototype.quickSort = function() {
  const rec = (arr) => {
    if (arr.length <= 1) { return arr }
    const left = []
    const right = []
    const mid = arr[0]
    for (let i = 1; i < arr.length; i++) {
      if (arr[i] < mid) {
        left.push(arr[i])
      } else {
        right.push(arr[i])
      }
    }
    return [...rec(left), mid, ...rec(right)]
  }
  const res = rec(this)
  res.forEach((n, i) => this[i] = n)
}
复制代码

22. 手写二分搜索

Array.prototype.binarySearch = function (item) {
  if (typeof item === 'undefined') return -1
  let low = 0
  let high = this.length -1
  while (low <= high) {
    const mid = Math.floor((low + high) / 2)
    const element = this[mid]
    if (element < item) {
      low = mid + 1
    } else if (element > item) {
      high = mid - 1
     } else {
      return mid
    }
  }
  return -1
}
复制代码

23. 手写instanceof

//遍历A的原型链,如果找到B.prototype,返回true,否则返回false

const _instanceof = (A, B) => {
  let p = A
  while(p) {
    if(p === B.prototype) {
      return true
    }
    p = p.__proto__
  }
  return false
}
复制代码

感谢阅读

非常感谢您到阅读到最后,如果有错误希望您能够指出,以免误导其他人,如果您觉得对您有帮助的话,希望能够点个赞,加个关注,有任何问题都可以联系我,希望能够一起进步。

最后祝您前程似锦,我们各自攀登,高处相见?!

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享