19
头图

Preface

" public account. Maybe you haven't met before, but it's very likely that you are too late to meet.

I met my classmates from elementary school yesterday. I didn’t expect it to be so bad--- I only put a dollar into my bowl o(╥﹏╥)o

Life is so boring, I have to tease myself, I will tell you jokes often in the future, are you willing to listen to O(∩_∩)O haha~

A few days ago I wrote an article advanced front-end] essential, 30+ high-frequency handwritten questions and detailed answers (10 thousand characters long), see how "you" stumped me summarized the realization of 30+ common handwritten questions, vast Brothers and sisters pointed out many of these problems, and some people suggested that there is no realization of anti-shake and throttling, and the fat head fish does not eat or sleep and does 12+ handwritten questions (close to 42+). Let's take a look.

## Through train

Click to view the source address of Rigong One Question (Currently, 62+ handwritten questions have been implemented)

手写实现题.jpg

1. Anti-shake

// 防抖:可以和你的电脑设定了10分钟睡眠时间的场景结合起来理解
// 如果你一直在用电脑,那么电脑就不会睡眠(频繁的把前一个定时器关掉,开启新的定时器)
// 当你最后一次没操作电脑10分钟之后,电脑陷入睡眠
const debounce = function (func, delay) {
  let timer = null

  return function (...args) {
    clearTimeout(timer)

    timer = setTimeout(() => {
      func.apply(this, args)
    }, delay)
  }
}

// 测试
// html部分
<input type="text" id="input"/>
// js部分
const showName = debounce(function (name) {
  console.log($input.value, this, name)
}, 500)


$input.addEventListener('input', (e) => {
  // 500ms内停止输入才会输出
  showName.call({ name: '前端胖头鱼' }, '前端胖头鱼')
})

2. Throttling

Throttling: No matter how you trigger it, it will only be triggered once in the specified time interval

Based on timestamp (method 1)

const throttle = function (func, delay) {
  let startTime = Date.now()

  return function (...args) {
    let lastTime = Date.now()

    if (lastTime - startTime > delay) {
      func.apply(this, args)
      startTime = Date.now()
    }
  }
}

// 测试
let t1 = Date.now()

const showName = throttle(function (name) {
  const t2 = Date.now()
  console.log(this, name, t2 - t1)
  t1 = Date.now()
}, 1000)
// 虽然设置了每隔10毫秒就会执行一次showName函数, 但是实际还是会每隔1秒才输出
setInterval(() => {
  showName.call({ name: '前端胖头鱼' }, '前端胖头鱼')
}, 10)

// { name: '前端胖头鱼' } '前端胖头鱼' 1013
// { name: '前端胖头鱼' } '前端胖头鱼' 1001
// { name: '前端胖头鱼' } '前端胖头鱼' 1006
// { name: '前端胖头鱼' } '前端胖头鱼' 1006
// { name: '前端胖头鱼' } '前端胖头鱼' 1005

Based on setTimeout (Method 2)

const throttle2 = function (func, delay) {
  let timer = null

  return function (...args) {
    if (!timer) {
      timer = setTimeout(() => {
        func.apply(this, args)
        timer = null
      }, delay) 
    }
  }
}
// 测试
let t1 = Date.now()

const showName = throttle2(function (name) {
  const t2 = Date.now()
  console.log(this, name, t2 - t1)
  t1 = Date.now()
}, 1000)

setInterval(() => {
  showName.call({ name: '前端胖头鱼' }, '前端胖头鱼')
}, 10)

// { name: '前端胖头鱼' } '前端胖头鱼' 1014
// { name: '前端胖头鱼' } '前端胖头鱼' 1001
// { name: '前端胖头鱼' } '前端胖头鱼' 1007
// { name: '前端胖头鱼' } '前端胖头鱼' 1011
// { name: '前端胖头鱼' } '前端胖头鱼' 1009
// { name: '前端胖头鱼' } '前端胖头鱼' 1008

3. Function Currying

const curry = (func, ...args) => {
  // 获取函数的参数个数
  const fnLen = func.length

  return function (...innerArgs) {
    innerArgs = args.concat(innerArgs)
    // 参数未搜集足的话,继续递归搜集
    if (innerArgs.length < fnLen) {
      return curry.call(this, func, ...innerArgs)
    } else {
      // 否则拿着搜集的参数调用func
      func.apply(this, innerArgs)
    }
  }
}
// 测试
const add = curry((num1, num2, num3) => {
  console.log(num1, num2, num3, num1 + num2 + num3)
})

add(1)(2)(3) // 1 2 3 6
add(1, 2)(3) // 1 2 3 6
add(1, 2, 3) // 1 2 3 6
add(1)(2, 3) // 1 2 3 6

4. bind

bind() method creates a new function, in bind() is called, this new function this is designated as bind() the first parameter, and the remaining arguments as parameters of the new function for use during calls. MDN

sister article call realization

sister articles apply to achieve

Function.prototype.bind2 = function (context, ...args) {
  if (typeof this !== 'function') {
    throw new TypeError('Bind must be called on a function')
  }

  const executeBound = function(sourceFunc, boundFunc, context, callingContext, args) {
    if (!(callingContext instanceof boundFunc)) {
      // 如果调用方式不是new func的形式就直接调用sourceFunc,并且给到对应的参数即可
      return sourceFunc.apply(context, args)
    } else {
      // 类似于执行new的几个过程
      const self = Object.create(sourceFunc.prototype) // 处理new调用的形式
      const result = sourceFunc.apply(self, args)
      // 判断函数执行后的返回结果 非对象函数,则返回self
      if (result && typeof result === 'object' || typeof result === 'function') {
        return result
      } else {
        return self
      }
    }
  }
  const func = this
  
  const bound = function (...innerArgs) {
    return executeBound(func, bound, context, this, args.concat(innerArgs))
  }

  return bound
}

// 测试
// 1. 普通调用
const showName = function (sex, age) {
  console.log(this, sex, age)
}

showName.bind2({ name: '前端胖头鱼' }, 'boy')(100) // { name: '前端胖头鱼' } 'boy' 100

// 2. new 调用
const Person = function (name) {
  this.name = name
}

Person.prototype.showName = function (age) {
  console.log(this, this.name, age)
}

const bindPerson = Person.bind(null, 'boy')
const p1 = new bindPerson('前端胖头鱼')

p1.showName(100) // Person { name: 'boy' } 'boy' 100

5. Implement a simple template engine

In the jQuery era, template engines are still used a lot. It can be understood that it is such a function. After a period of black box operation through template + data, the page that needs to be displayed is finally obtained.
const render = (template, data) => {
  // \s*?是为了兼容{{name}} {{ name }}这种写法
  return template.replace(/{{\s*?(\w+)\s*?}}/g, (match, key) => {
    // 匹配中了则读取替换,否则替换为空字符串
    return key && data.hasOwnProperty(key) ? data[ key ] : ''
  })
}
const data = {
  name: '前端胖头鱼',
  age: 100
}
const template = `
  我是: {{ name }}
  年龄是: {{age}}
`
console.log(render(template, data))
/*
我是: 前端胖头鱼
年龄是: 100
*/

6. 4 ways to convert a class array to an array

// 类数组转化为数组
const arrayLikeObj = {
  0: '前端胖头鱼',
  1: 100,
  length: 2
}

// 1. [].slice
console.log([].slice.call(arrayLikeObj))
// 2. Array.from
console.log(Array.from(arrayLikeObj))
// 3. Array.apply
console.log(Array.apply(null, arrayLikeObj))
// 4. [].concat
console.log([].concat.apply([], arrayLikeObj))

7. Please implement a function of DOM2JSON that can output a DOM node in the format of JSON

Appeared in a byte interview
const dom2json = (rootDom) => {
  if (!rootDom) {
    return 
  }

  let rootObj = {
    tagName: rootDom.tagName,
    children: []
  }

  const children = rootDom.children
  // 读取子节点(元素节点)
  if (children && children.length) {
    Array.from(children).forEach((ele, i) => {
      // 递归处理
      rootObj.children[ i ] = dom2json(ele)
    })
  }

  return rootObj
}

test

<!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>dom2json</title>
</head>
<body>
  <div class="box">
    <p class="p">hello world</p>
    <div class="person">
      <span class="name">前端胖头鱼</span>
      <span class="age">100</span>
    </div>
  </div>
  <script>
    const dom2json = (rootDom) => {
      if (!rootDom) {
        return 
      }

      let rootObj = {
        tagName: rootDom.tagName,
        children: []
      }

      const children = rootDom.children

      if (children && children.length) {
        Array.from(children).forEach((ele, i) => {
          rootObj.children[ i ] = dom2json(ele)
        })
      }

      return rootObj
    }

    const json = dom2json(document.querySelector('.box'))

    console.log(json)
  </script>
</body>
</html>

image.png

8. List to tree structure

I believe you have encountered similar problems in your work. The front-end needs tree-structured data, but the back-end returns a list. We need to convert the list into a tree structure (of course, you can also put your back Duan classmates do not give me the data I want).
const arrayToTree = (array) => {
  const hashMap = {}
  let result = []

  array.forEach((it) => {
    const { id, pid } = it

    // 不存在时,先声明children树形
    // 这一步也有可能在下面出现
    if (!hashMap[id]) {
      hashMap[id] = {
        children: []
      }
    }

    hashMap[id] = {
      ...it,
      children: hashMap[id].children
    }
    // 处理当前的item
    const treeIt = hashMap[id]

    // 根节点,直接push
    if (pid === 0) {
      result.push(treeIt)
    } else {
      // 也有可能当前节点的父父节点还没有加入hashMap,所以需要单独处理一下
      if (!hashMap[pid]) {
        hashMap[pid] = {
          children: []
        }
      }
      // 非根节点的话,找到父节点,把自己塞到父节点的children中即可
      hashMap[pid].children.push(treeIt)
    }
  })

  return result
}

// 测试
const data = [
  // 注意这里,专门把pid为1的元素放一个在前面
  { id: 2, name: '部门2', pid: 1 },
  { id: 1, name: '部门1', pid: 0 },
  { id: 3, name: '部门3', pid: 1 },
  { id: 4, name: '部门4', pid: 3 },
  { id: 5, name: '部门5', pid: 4 },
  { id: 7, name: '部门7', pid: 6 },
]

console.log(JSON.stringify(arrayToTree(data), null, 2))
/*
[
  {
    "id": 1,
    "name": "部门1",
    "pid": 0,
    "children": [
      {
        "id": 2,
        "name": "部门2",
        "pid": 1,
        "children": []
      },
      {
        "id": 3,
        "name": "部门3",
        "pid": 1,
        "children": [
          {
            "id": 4,
            "name": "部门4",
            "pid": 3,
            "children": [
              {
                "id": 5,
                "name": "部门5",
                "pid": 4,
                "children": []
              }
            ]
          }
        ]
      }
    ]
  }
]
*/

9. Tree structure to list

You can also try the other way around
const tree2list = (tree) => {
  let list = []
  let queue = [...tree]

  while (queue.length) {
    // 从前面开始取出节点
    const node = queue.shift()
    const children = node.children
    // 取出当前节点的子节点,放到队列中,等待下一次循环
    if (children.length) {
      queue.push(...children)
    }
    // 删除多余的children树形
    delete node.children
    // 放入列表
    list.push(node)
  }

  return list
}

// 测试
const data = [
  {
    "id": 1,
    "name": "部门1",
    "pid": 0,
    "children": [
      {
        "id": 2,
        "name": "部门2",
        "pid": 1,
        "children": []
      },
      {
        "id": 3,
        "name": "部门3",
        "pid": 1,
        "children": [
          {
            "id": 4,
            "name": "部门4",
            "pid": 3,
            "children": [
              {
                "id": 5,
                "name": "部门5",
                "pid": 4,
                "children": []
              }
            ]
          }
        ]
      }
    ]
  }
]

console.log(tree2list(data))
/*
[ 
  { id: 1, name: '部门1', pid: 0 },
  { id: 2, name: '部门2', pid: 1 },
  { id: 3, name: '部门3', pid: 1 },
  { id: 4, name: '部门4', pid: 3 },
  { id: 5, name: '部门5', pid: 4 } 
]
*/

10. sleep

Implement a function, execute the function func after n seconds
const sleep = (func, delay) => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(func())
    }, delay)
  })
}

const consoleStr = (str) => {
  return () => {
    console.log(str)
    return str
  }
}

const doFns = async () => {
  const name = await sleep(consoleStr('前端胖头鱼'), 1000)
  const sex = await sleep(consoleStr('boy'), 1000)
  const age = await sleep(consoleStr(100), 1000)

  console.log(name, sex, age)
}

doFns()
// 前端胖头鱼  1s later
// boy 2s later
// 100 3s later
// 前端胖头鱼 boy 100

11. Fibonacci sequence

斐波那契数,通常用 F(n) 表示,形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:

F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1
给你 n ,请计算 F(n) 。


示例 1:

输入:2
输出:1
解释:F(2) = F(1) + F(0) = 1 + 0 = 1
示例 2:

输入:3
输出:2
解释:F(3) = F(2) + F(1) = 1 + 1 = 2
示例 3:

输入:4
输出:3
解释:F(4) = F(3) + F(2) = 2 + 1 = 3

Violent realization

According to the meaning of the title, it is easy to write the following recursive violent code
const fib = (n) => {
  if (n === 0) {
    return 0
  }

  if (n === 1 || n === 2) {
    return 1
  }

  return fib(n -2) + fib(n - 1)
}

// 测试
console.log(fib(1)) // 1
console.log(fib(2)) // 1
// 试着统计一下计算时间
const t1 = Date.now()
console.log(fib(44)) // 701408733
console.log(Date.now() - t1) // 接近4393

Cache optimization

The above code can achieve the effect, but the performance is worrying, look at a process of fib(10)
// 计算10
10 => 9 + 8 // 需要计算9和8
9 => 8 + 7 // 需要计算8和7
8 => 7 + 6 // 需要计算7和6
7 => 6 + 5 // 需要计算6和5
6 => 5 + 4 // 需要计算5和4
5 => 4 + 3 // 需要计算4和3
4 => 3 + 2 // 需要计算3和2
2 => 1 + 0 // 需要计算1和0

In this process, if you follow the violent implementation of the code above, you will repeatedly calculate some calculated values, such as 8, 7, 6, 5... etc. This loss is not necessary, so we can calculate The result is cached, and the same value is requested next time, just return directly

const fib = (n) => {
  // 缓存过直接返回
  if (typeof fib[ n ] !== 'undefined') {
    return fib[ n ]
  }

  if (n === 0) {
    return 0
  }

  if (n === 1 || n === 2) {
    return 1
  }

  const res = fib(n -2) + fib(n - 1)
  // 缓存计算的结果
  fib[ n ] = res

  return res
}

console.log(fib(1)) // 1
console.log(fib(2)) // 1

const t1 = Date.now()
console.log(fib(44)) // 701408733
console.log(Date.now() - t1) // 1ms

12. Implement a function sum function

The realization of a function sum function satisfies the following rules
sum(1, 2, 3).valueOf() // 6 
sum(2, 3)(2).valueOf() // 7 
sum(1)(2)(3)(4).valueOf() // 10
sum(2)(4, 1)(2).valueOf() // 9

analysis

Carefully observe these call methods to get the following information

  1. The sum function can pass one or more parameters
  2. After the sum function is called, it returns a new function and one or more parameters can be passed
  3. The final calculation is done when .valueOf is called

it look like 16172056fe6b87 function currying? The previous function call just caches the parameters of each call, while the valueOf call is to take these parameters to perform a summation operation and return the result

const sum = (...args) => {
  // 声明add函数,其实主要是缓存参数的作用
  // 注意add调用完成还是会返回add函数本身,使其可以链式调用
  const add = (...args2) => {
    args = [ ...args, ...args2 ]
    return add
  }
  // 求和计算
  add.valueOf = () => args.reduce((ret, num) => ret + num, 0)

  return add
}

// 测试
console.log(sum(1, 2, 3).valueOf()) // 6
console.log(sum(2, 3)(2).valueOf()) // 7
console.log(sum(1)(2)(3)(4).valueOf()) // 10
console.log(sum(2)(4, 1)(2).valueOf()) // 9

前端胖头鱼
3.7k 声望6.2k 粉丝