写他妈的

数组扁平化

let flat = arr => arr.reduce((list, v) => list.concat(Array.isArray(v) ? flat(v) : v), [])

v2

function flat(arr) {
    let res = []
    for (let item of arr) {
        if (Array.isArray(item)) {
            res = res.concat(flat(item))
        } else {
            res.push(item)
        }
    }
    return res
}

类型

加法操作符的基本规则

  • 两者都为数字 进行普通的数字相加
  • 一方为字符串 则转换另一方为字符串后进行字符串拼接
  • 一方为对象类型 转换为字符串后 继续应用上一条规则
  • null + 1 = 1 原因是null被转换成了数字 这和红宝书上有冲突,应该null undefined会和另外一项进行自适应 如果是数字 自己也变数字,如果是字符 自己也变字符

相等操作符的运算规则

image

[] == ![] -> [] == false -> [] == 0 -> [].valueOf() == 0 -> [].toString() == 0 -> ‘’ == 0 -> 0 == 0 -> true

继承

寄生组合式继承

function Person(name) {
  this.name = name
}

Person.prototype.sayName = function() {
  console.log(this.name)
}

function Boy(name) {
  this.sex = 'box'
  Person.call(this, name)
}

let prototype = Object.create(Person.prototype)
prototype.constructor = Boy
Boy.prototype = prototype

let boy1 = new Boy("TOM")
boy1.sayName()

特点 继承的只是方法,父类实例不共享变量,并且不需要New一个多余的父类变量,只需要继承父类的原型。这里面的构造函数没有被覆盖,只是建立再一个新的对象上。

用Class

class Person {
  constructor(name) {
    this.name = name
  }
  sayName() {
    console.log(this.name)
  }
}

class Boy extends Person {
  constructor(name) {
    super(name)
  }
}

let boy1 = new Boy("Tom")
boy1.sayName()

算法

快速排序

function quick_sort1(arr) {
  if (!arr || arr.length < 2) return arr
  const pivot = arr.pop()
  const left = arr.filter(v => v <= pivot)
  const right = arr.filter(v => v > pivot)
  return quick_sort1(left).concat([pivot], quick_sort1(right))
}
function quickSort2(arr) {
  if (arr.length <= 1) {
    return arr
  }
  let pi = Math.floor(arr.length / 2)
  let p = arr.splice(pi, 1)[0]

  let left = []
  let right = []
  let i = arr.length
  while (i--) {
    let cur = arr[i]
    if (cur < p) {
      left.push(cur)
    } else {
      right.push(cur)
    }
  }

  return quickSort2(left).concat([p], quickSort2(right))
}

function quick_sort3(arr, start, end) {
  let mid = arr[start],
    p1 = start,
    p2 = end

  while (p1 < p2) {
    swap(arr, p1, p1 + 1)
    while (compare(arr[p1], mid) >= 0 && p1 < p2) {
      swap(arr, p1, p2--)
    }
    p1++
  }

  if (start < p1 - 1) quick_sort3(arr, start, p1 - 1)
  if (p1 < end) quick_sort3(arr, p1, end)
}

异步

简单的Promise(无法.then连续返回新Promise 不符合A+规范)

const PromiseStateMap = {
  pending: 'pending',
  resolved: 'resolved',
  rejected: 'rejected'
}
class Promise {
  static resolve(val) {
    if (val instanceof Promise) {
      return val
    }
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(val)
      })
    })
  }
  static reject(val) {
    if (val instanceof Promise) {
      return val
    }
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        reject(val)
      })
    })
  }
  constructor(fn) {
    this.val = null
    this.state = PromiseStateMap.pending
    this.resolvedCbs = []
    this.rejectedCbs = []
    fn(
      res => {
        this.val = res
        this.state = PromiseStateMap.resolved
        for (let fn of this.resolvedCbs) {
          fn(this.val)
        }
      },
      res => {
        this.val = res
        this.state = PromiseStateMap.rejected
        for (let fn of this.rejectedCbs) {
          fn(this.val)
        }
      }
    )
  }
  then(onResolved, onRejected) {
    if (typeof onResolved === 'function') {
      this.resolvedCbs.push(onResolved)
    }
    if (typeof onRejected === 'function') {
      this.rejectedCbs.push(onRejected)
    }
  }
}

进程与线程

进程是 CPU 资源分配的最小单位;线程是 CPU 调度的最小单位

可以认为一个进程就是一个正在运行中的程序,而一个线程是这个程序中的执行流,现在的操作系统都是多进程的,可以同时运行多个程序。

手写call,apply,bind

Function.prototype.mycall = function(ctx, ...args) {
  if (!ctx || typeof ctx !== 'object') {
    console.error('ctx must be a object!')
  }
  let fn = this
  let key = Symbol()
  ctx[key] = fn
  let res = ctx[key](...args)
  return res
}

Function.prototype.myapply = function(ctx, args) {
  if (!ctx || typeof ctx !== 'object') {
    console.error('ctx must be a object!')
  }
  let fn = this
  let key = Symbol()
  ctx[key] = fn
  let res = ctx[key](...args)
  return res
}

Function.prototype.mybind = function(ctx) {
  if (!ctx || typeof ctx !== 'object') {
    console.error('ctx must be a object!')
  }
  let fn = this
  return function(...args) {
    let key = Symbol()
    ctx[key] = fn
    let res = ctx[key](...args)
    return res
  }
}

实现instanceOf

instanceOf的原理是沿着left对象的原型链进行检查 看是否和right构造函数的原型对象相等

function myInstanceOf(left, right) {
  let targetProto = right.prototype
  let curProto = Object.getPrototypeOf(left)
  while (curProto) {
    if (curProto === targetProto) {
      return true
    } else {
      curProto = Object.getPrototypeOf(curProto)
    }
  }
  return false
}

VDOM

VDOM是一种技术,一种理念,他把真实的DOM与JS中的对象进行一种映射,也就是说在JS这一端与真实DOM端建立了一层抽象

他的好处有如下几点

  • 增强Diff的性能 可以通过对比前后VnodeTree来找到更新的节点,进行局部更新。
  • 建立抽象层,方便移植到多平台,进行SSR

打开浏览器 发生

大纲

DNS解析 多级缓存
获取IP 进行TCP链接 三次握手 SYN SYN_ACK ACK
如果是https 进行TLS加密
获取到过程底层其实是按包来的
获取HTML文件 中间加载各种资源 几乎并发 最多6个套接字同时进行 资源的缓存机制强缓存与协商缓存 资源可能会gzip
合并两棵树 DOM 和 CSSDOM树 进行合并
进行首次绘制 用户看到内容

算法部分

按位实现加法

function sum(a, b) {
  if (a == 0) return b
  if (b == 0) return a
  let newA = a ^ b
  let newB = (a & b) << 1
  return sum(newA, newB)
}

所有的排序

function swap(arr, a, b) {
  let temp = arr[a]
  arr[a] = arr[b]
  arr[b] = temp
}
let testArr = [10, 3, 50, 11, 88, 2390, 1, 2, 11]
// 冒泡排序
function bsort(arr) {
  let len = arr.length
  arr = arr.slice()
  for (let i = 0; i < len; i++) {
    for (let j = 0; j < len - i; j++) {
      if (arr[j] > arr[j + 1]) {
        swap(arr, j, j + 1)
      }
    }
  }
  return arr
}
// 插入排序
function iSort(arr) {
  let len = arr.length
  for (let i = 1; i < len; i++) {
    let cur = arr[i]
    let j = i - 1
    while (j >= 0 && arr[j] > cur) {
      arr[j + 1] = arr[j]
      j--
    }
    arr[j + 1] = cur
  }
  return arr
}
// 快速排序
function qsort(arr) {
  let len = arr.length
  if (len <= 1) {
    return arr
  }
  let pivotIndex = Math.floor(len / 2)
  let pivot = arr.splice(pivotIndex, 1)[0]

  let left = []
  let right = []

  for (item of arr) {
    if (item < pivot) {
      left.push(item)
    } else {
      right.push(item)
    }
  }
  return qsort(left).concat([pivot], qsort(right))
}
// 选择排序
function ssort(arr) {
  let len = arr.length
  arr = arr.slice()
  for (let i = 0; i < len; i++) {
    let index = i
    for (let j = i; j < len; j++) {
      if (arr[j] < arr[index]) {
        index = j
      }
    }
    swap(arr, i, index)
  }
  return arr
}
// 归并排序
function msort(arr) {
  let len = arr.length
  if (len <= 1) {
    return arr
  }

  let middle = Math.floor(len / 2)
  let left = msort(arr.slice(0, middle))
  let right = msort(arr.slice(middle, len))

  let start1 = 0,
    start2 = 0,
    end1 = middle,
    end2 = len - middle
  let res = []
  while (start1 < end1 && start2 < end2) {
    left[start1] < right[start2]
      ? res.push(left[start1++])
      : res.push(right[start2++])
  }
  while (start1 < end1) {
    res.push(left[start1++])
  }
  while (start2 < end2) {
    res.push(right[start2++])
  }
  return res
}

颜色排序算法

function tSort(arr) {
  let len = arr.length
  let left = -1
  let right = len

  for (let i = 0; i < right; i++) {
    if (arr[i] === 0) {
      swap(arr, i, ++left)
    }
    if (arr[i] === 2) {
      swap(arr, i--, --right)
    }
  }
  return arr
}

线性统计 获取第K大的值

小心splice[]

注意 p = left.length + 1

function select(nums, i) {
  let len = nums.length
  if (len <= 1) {
    return nums[0]
  }

  let pivotIndex = Math.floor(len / 2)
  let pivot = nums.splice(pivotIndex, 1)[0]

  let left = []
  let right = []

  for (num of nums) {
    if (num < pivot) {
      left.push(num)
    } else {
      right.push(num)
    }
  }

  let p = left.length + 1
  if (p === i) {
    return pivot
  }
  if (i < p) {
    return select(left, i)
  } else {
    return select(right, i - p)
  }
}

堆排序 最大最小堆

建堆的时候小心顺序 i = floor(len / 2) i >=0 i --

一定要从后往前建

小心heapsize--的顺序

let array = [5, 2, 6, 1, 6, 8, 2, 39, 2, 6, 89, 5, 6, 4, 7]

const swap = (arr, a, b) => {
  let temp = arr[a]
  arr[a] = arr[b]
  arr[b] = temp
}

const left = i => i * 2
const right = i => i * 2 + 1

function HEAPIFY(arr, i) {
  let l = left(i)
  let r = right(i)

  let largest = i

  if (l < arr.heapsize && arr[l] > arr[largest]) {
    largest = l
  }
  if (r < arr.heapsize && arr[r] > arr[largest]) {
    largest = r
  }
  if (largest !== i) {
    swap(arr, i, largest)
    HEAPIFY(arr, largest)
  }
}

function BUILD_HEAP(arr) {
  arr.heapsize = arr.length
  for (let i = Math.floor(arr.length / 2); i >= 0; i--) {
    HEAPIFY(arr, i)
  }
  return arr
}

function HEAP_SORT(arr) {
  BUILD_HEAP(arr)
  for (let i = arr.length - 1; i >= 0; i--) {
    swap(arr, 0, i)
    arr.heapsize--
    HEAPIFY(arr, 0)
  }
  return arr
}

单向链表 以及 反转功能

// 单项链表

class ListNode {
  constructor(val, next) {
    this.val = val
    this.next = next
  }
}

class LinkList {
  constructor() {
    this.head = null
    this.tail = null
  }
  add(val) {
    if (!this.head || !this.tail) {
      this.head = this.tail = new ListNode(val, null)
    } else {
      this.tail.next = new ListNode(val, null)
      this.tail = this.tail.next
    }
  }
  reverse() {
    if (!this.head | !this.tail) return
    let pre = null
    let current = this.head
    let next = null

    while (current) {
      next = current.next
      current.next = pre
      pre = current
      current = next
    }

    let temp = this.head
    this.head = this.tail
    this.tail = temp
  }
}

let linklist = new LinkList()
linklist.add('a')
linklist.add('b')
linklist.add('c')
linklist.add('d')
linklist.reverse()

树的遍历 递归和迭代实现

function TreeNode(val) {
  this.val = val
  this.left = this.right = null
}

function traverse(root) {
  if (root) {
    console.log(root)
    if (root.left) {
      traverse(root)
    }
    if (root.right) {
      traverse(root)
    }
  }
}

function pre(root) {
  if (root) {
    let stack = []
    stack.push(root)
    while (stack.length > 0) {
      let item = stack.pop()
      console.log(item)
      if (item.right) {
        stack.push(right)
      }
      if (item.left) {
        stack.push(left)
      }
    }
  }
}

function mid(root) {
  if (root) {
    let stack = []
    stack.push(root)
    while (stack.length >= 1) {
      if (root) {
        stack.push(root)
        root = root.left
      } else {
        let item = stack.pop()
        console.log(item)
        root = item.right
      }
    }
  }
}

function pos(root) {
  if (root) {
    let stack1 = []
    let stack2 = []

    stack1.push(root)
    while (stack1.length >= 1) {
      let item = stack1.pop()
      stack2.push(item)
      if (item.right) {
        stack1.push(item.right)
      }
      if (item.left) {
        stack1.push(item.left)
      }
    }
    while (stack2.length >= 1) {
      console.log(stack2.pop())
    }
  }
}

前驱节点 后驱节点

前驱先看Left 后续全部right
后续先看right 后续全部left

function successor(node) {
  if (node.right) {
    return findLeft(node.right)
  } else {
    let parent = node.parent
    while (parent && parent.left === node) {
      node = parent
      parent = node.parent
    }
    return parent
  }
}

function findLeft(node) {
  while (node) {
    if (!node.left) {
      return node
    }
    node = node.left
  }
}

function predecessor(node) {
  if (node.left) {
    return getRight(node.left)
  } else {
    let parent = node.parent
    while (parent && parent.right === node) {
      node = parent
      parent = node.parent
    }
    return parent
  }
}

function getRight(node) {
  while (node) {
    if (!node.right) {
      return node
    }
    node = node.right
  }
}

获取树的最大深度

function MAX_DEPTH(root) {
  if (!root) {
    return 0;
  }
  return Math.max(MAX_DEPTH(root.left), MAX_DEPTH(root.right)) + 1;
}

帅的不行Fib

const fib = n =>
  Array(n)
    .fill(1)
    .reduce(nums => [nums[1], nums[0] + nums[1]], [0, 1])[0]

最小硬币算法

/**
 * @param {*} coins 硬币数组
 * @param {*} m 目标金额
 * @returns {number} 如果拥有解则为一个整数 如果没有则为正无限
 */
function min_coins(coins, m) {
  let table = [0]
  let i = 1
  while (i <= m) {
    table[i] = Infinity
    for (coin of coins) {
      if (i >= coin) {
        table[i] = Math.min(table[i], table[i - coin] + 1)
      }
    }
    i++
  }
  return table[m]
}

console.log(min_coins([3, 6, 8], 16))

01背包问题

想象出一个表格 行是物品 列是当前容量

外循环是物品 里面是金额

注意哦 一层是物品 二层是空间

空间要从0开始 一层直接开始

/**
 * @param {*} w 物品重量
 * @param {*} v 物品价值
 * @param {*} C 总容量
 * @returns
 */
function knapsack(w, v, C) {
  let len = w.length

  let table = new Array(len).fill(new Array(C + 1).fill(0))

  for (let j = 0; j <= C; j++) {
    table[0][j] = j >= w[0] ? v[0] : 0
  }

  for (let i = 1; i < len; i++) {
    let cw = w[i]
    for (let j = 0; j <= C; j++) {
      table[i][j] = table[i - 1][j]
      if (j >= cw) {
        table[i][j] = Math.max(table[i][j], v[i] + table[i - 1][j - cw])
      }
    }
  }
  return table[len - 1][C]
}

console.log(knapsack([1, 2, 3], [3, 7, 12], 5))

最长递增子序列

要注意最后的Max哦
第一层循环从1开始

function lis(n) {
  let len = n.length
  let array = new Array(len).fill(1)

  for (let i = 1; i < len; i++) {
    for (let j = 0; j < i; j++) {
      if (n[i] > n[j]) {
        array[i] = Math.max(array[i], array[j] + 1)
      }
    }
  }
  return Math.max.apply(Math, array)
}

console.log(lis([0, 3, 4, 17, 2, 8, 6, 10, 11]))

你在工作中遇到的兼容性问题

  1. URLSearchParams 兼容性问题 在IE和Edge下不可用 解决 使用polyfill
  2. 微信IOS下不会主动加载音频文件 解决 先放置一个空的mp3文件 然后首次加载直接load方法+永远不摧毁这个audio
  3. 微信的登录跳转缓存问题 解决 将初始化函数传入到一个全局变量中 登录完成后手动触发所有回调

转美式3个数字一个,

function commafy(num) {
  num = num.toString().split('.')
  let head = num[0]
  let tail = num[1]
  head = head
    .split('')
    .reverse()
    .map((v, i) => (i && i % 3 === 0 ? v + ',' : v))
    .reverse()
    .join('')
  return head + '.' + tail
}

随机化数组

注意,Math.random() 是无法获取一个等于1的数字的 排除1

function shuffle(arr) {
  return arr.sort(() => Math.random() - 0.5)
}

function shuffle(arr) {
  arr = arr.slice()
  let len = arr.length

  for (let i = 0; i < len; i++) {
    let swapIndex = getRandomInt(0, i)
    let temp = arr[i]
    arr[i] = arr[swapIndex]
    arr[swapIndex] = temp
  }
  return arr
}

function getRandomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1) + min)
}

严格模式

使用"use strict"可以开启严格模式,在严格模式下将会开启更加严格的代码错误检查,很多在非严格模式下允许的操作会在严格模式下被禁止

列如

  1. 阻止那些意外被创建的全局变量,message = 1 如果该变量没有被声明,则不会帮你自动创建
  2. 不允许重复的对象key
    3 不允许重复的参数名
    4 evel将剥脱在外部作用域创建变量的能力

闭包

闭包是指一个有权访问另外一个函数作用域中的变量的函数

创建闭包最简单的方法就是从一个函数里面返回另一个匿名函数

闭包是本质是其内部的函数引用了外部函数的活动对象。只要内部函数的引用不释放,这个活动对象不会被清除引用和回收。内部函数将会一直有能力访问其外部引用的变量。

闭包的真正用途是为了保存状态,让变量不被回收。

这里可以谈一下著名的SICP 里面用闭包实现了序对,某种意义上说,他和OOP的对象很类似,都能存放状态。

函数节流与函数防抖

function debounce(fn, dealy) {
  let timer = null
  return function(...args) {
    const ctx = this
    if (timer) {
      clearTimeout(timer)
    }
    timer = setTimeout(() => {
      fn.apply(ctx, args)
    }, dealy)
  }
}

function throttle(fn, interval) {
  let last = 0
  return function(...args) {
    const now = Date.now()
    const ctx = this
    if (now - last > interval) {
      last = now
      fn.apply(ctx, args)
    }
  }
}

function compose(fn, dealy, interval) {
  let d = debounce(fn, dealy)
  let t = throttle(fn, interval)
  return function(...args) {
    let ctx = this
    d.apply(ctx, args)
    t.apply(ctx, args)
  }
}

function compose(fn, dealy, interval) {
  let timer = null
  let last = 0
  return function(...args) {
    const ctx = this
    const now = Date.now()
    const run = () => {
      fn.apply(ctx, args)
    }
    if (now - last > interval) {
      run()
      last = now
      return
    }
    if (timer) {
      clearTimeout(timer)
    }
    timer = setTimeout(run, dealy)
  }
}

谈谈什么算全栈工程师

一 基本掌握前后端开发的某种技术,如一门后端语言+前端框架
二 在思维方式上 要做到没有局限,不会只注意某种环节或者技术,可以从更高的层次观察整个产品,学历能力要非常出色,出现什么问题能够迅速找到相关技术学习并解决问题

你可能感兴趣的:(写他妈的)