常见应用问题积累

1. 一次性向页面插入大量 DOM 元素怎么做比较好?

在循环体内使用 appendChild 会发生多次重渲染的操作。

可以使用 DocumentFragment 文档碎片来代替,与 document 相比,最大的区别是 DocumentFragment 不是真实 DOM 树的一部分,它的变化不会触发 DOM 树的重新渲染,且不会导致性能等问题。

所有的节点会被一次插入到文档中,而这个操作仅发生一个重渲染的操作,而不是每个节点分别被插入到文档中。

<ul id="root"></ul>
<script>
  var root = document.getElementById('root')
  var fragment = document.createDocumentFragment()
  for (let i = 0; i < 1000; i++) {
    let li = document.createElement('li')
    li.innerHTML = '我是li标签'
    fragment.appendChild(li)
  }
  root.appendChild(fragment)
</script>

2. 如何用 5L 和 6L 的杯子,得到 3L 的水?

先用 5L 的杯子倒满水,然后把满杯的水倒进 6L 的杯子里,

再用 5L 的杯子继续装满水,然后再次倒进 6L 的杯子里,最终 5L 的杯子里只剩下 4L 的水了,

这个时候,会把 6L 的杯子的水全部倒掉,

然后把 5L 的杯子里的 4L 水倒进 6L 的杯子里,

然后再倒满 5L 的杯子的水,

最后,再把 5L 的水倒满 6L 的水杯中,由于原本 6L 的水杯中就已经有了 4L 的水,所以只需要再倒满 2L 的水即可,最后 5L 的杯子里就只有 3L 水!

3. 12 个球,其中一个和其他的重量不一样,有一个天平,最多几次找出这个球?

将球编号,分成 3 组,[1、2、3、4] [5、6、7、8] [9、10、11、12]

  • 第一次将 [1、2、3、4] [5、6、7、8] 放在天平的两边
    • 如果平衡,则坏球在 [9、10、11、12] 里,第二次取 9、10 称
      • 如果平衡,则坏球在 11、12 里,将 10 换成 11,如果平衡,则坏球是 12,如果不平衡,则坏球是 11
      • 如果不平衡,则坏球在 9、10 里,将 10 换成 11,如果平衡,则坏球是 10,如果不平衡,则坏球是 9
    • 如果不平衡,则坏球在 [1、2、3、4] [5、6、7、8] 里,然后会有 [1、2、3、4] < [5、6、7、8] 或者 [1、2、3、4] > [5、6、7、8] 这两种情况,这里先假设 [1、2、3、4] > [5、6、7、8],第二次取 [4、5、6、7]、[8、9、10、11] 来称
      • 如果平衡,则坏球在 [1、2、3] 里,因为 [1、2、3、4] < [5、6、7、8],可得坏球比较轻,(反之可得比较重),取 [1、2] 称,如果平衡则坏球是 3,如果不平衡,坏球是 [1、2] 中轻的那个。
      • 如果不平衡,则坏球在 [4、5、6、7、8] 里
        • 如果 [4、5、6、7] > [8、9、10、11],则坏球在 [5、6、7] 里,且坏球比较重,取 5、6 称,如果平衡则坏球是 7,如果不平衡,坏球是 5、6 中重的那个。
        • 如果 [4、5、6、7] < [8、9、10、11],则坏球在 4,8 里,用 4 跟一个好球比,如果平衡,则坏球是 8,如果不平衡,则坏球是 4。

所以最多 3 次可以找出。

4. 吸顶效果

  1. 可以使用 position: sticky
.parent {
  position: relative;
  overflow: auto;
}
.child {
  position: -webkit-sticky;
  position: sticky;
  top: 20px;
}
  1. 通过监听滚动高度来进行 fixed 定位:

::::tabs :::tab CSS

.sticky {
  position: fixed;
  top: 0;
}

::: :::tab JavaScript

const h1 = document.querySelector('h1')
const offsetTop = h1.offsetTop
document.addEventListener('scroll', (e) => {
  const scrollTop = document.documentElement.scrollTop
  if (scrollTop >= offsetTop) {
    h1.className = 'sticky'
  } else {
    h1.className = ''
  }
})

::: ::::

5. 金条问题?

你让工人为你工作 7 天,给工人的回报是一根金条。金条平分成相连的 7 段,你必须在每天结束时给他们一段金条,如果只许你两次把金条弄断,你如何给你的工人付费?

两次弄断,将金条分为 [1,2], [3], [4,5,6,7] 三份

第一天:给工人 [3] 第二天:给工人 [1,2],换回 [3] 第三天:给工人 [3] 第四天:给工人 [4,5,6,7],换回 [1,2], [3] 第五天:给工人 [3] 第六天:给工人 [1,2],换回 [3] 第七天:给工人 [3]

6. 怎么监听 localStorage、sessionStorage 变化?

// 用闭包实现局部对象storage(注意Storage的方法都重写一遍,不然调用其对象原型方法会报错。)
var localStorageMock = (function (win) {
  var storage = win.localStorage
  return {
    setItem: function (key, value) {
      var setItemEvent = new Event('setItemEvent')
      var oldValue = storage[key]
      setItemEvent.key = key
      // 新旧值深度判断,派发监听事件
      if (oldValue !== value) {
        setItemEvent.newValue = value
        setItemEvent.oldValue = oldValue
        win.dispatchEvent(setItemEvent)
        storage[key] = value
        return true
      }
      return false
    },
    getItem: function (key) {
      return storage[key]
    },
    removeItem: function (key) {
      storage[key] = null
      return true
    },
    clear: function () {
      storage.clear()
      return true
    },
    key: function (index) {
      return storage.key(index)
    },
  }
})(window)

Object.defineProperty(window, 'localStorage', { value: localStorageMock, writable: true })

window.addEventListener("setItemEvent", (e) => {
    console.log("localStorage值发生变化后触发:", e.newValue);
});