前端面试刷题日记(三)

June 26, 2020

  1. css 垂直水平居中

  2. 闭包

  3. 浏览器缓存相关

  4. 性能优化


css 垂直水平居中

第一种 position+transform

 <style>
      #box {
        width: 800px;
        height: 800px;
        background-color: gray;
        
        position: relative;
      }
      #cbox {
        width: 300px;
        height: 300px;
        background-color: orange;
        
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
      }
</style>
<body>
  <div id="box">
    <div id="cbox"></div>
  </div>
</body>

第二种 flex

  <style>
    #box {
      width: 800px;
      height: 800px;
      background-color: gray;

      display: flex;
      align-items: center;
      justify-content: center;
    }
    #cbox {
      width: 300px;
      height: 300px;
      background-color: orange;
    }
  </style>
  <body>
    <div id="box">
      <div id="cbox"></div>
    </div>
  </body>

第三种 padding + margin

  <style>
    #box {
      width: 800px;
      height: 800px;
      background-color: gray;

      box-sizing: border-box;
      padding: 250px 0;
    }
    #cbox {
      width: 300px;
      height: 300px;
      background-color: orange;

      margin: 0 auto;
    }
  </style>
  <body>
    <div id="box">
      <div id="cbox"></div>
    </div>
  </body>

以上三种代码显示效果均为

image-20200626093651504

闭包

闭包可以从内部函数访问到外部函数作用域。

  • 模拟私有方法

    Java有私有属性,private,而 js 也可以通过模拟来实现私有变量和方法

    var makeCounter = function() {
      var privateCounter = 0;
      function changeBy(val) {
        privateCounter += val;
      }
      return {
        increment: function() {
          changeBy(1);
        },
        decrement: function() {
          changeBy(-1);
        },
        value: function() {
          return privateCounter;
        }
      }  
    };
    
    var Counter1 = makeCounter();
    var Counter2 = makeCounter();
    console.log(Counter1.value()); /* logs 0 */
    Counter1.increment();
    Counter1.increment();
    console.log(Counter1.value()); /* logs 2 */
    Counter1.decrement();
    console.log(Counter1.value()); /* logs 1 */
    console.log(Counter2.value()); /* logs 0 */
  • 经典闭包问题

    <button>按钮1</button>
    <button>按钮2</button>
    <button>按钮3</button>
    <button>按钮4</button>
    <button>按钮5</button>
    
    <script>
      var btns = document.getElementsByTagName('button')
      for (var i = 0; i < btns.length; i++) {
        var btn= btns[i]
        btn.onclick= function () {
          console.log('点击了第' + i + '个按钮')
        }
      }
    </script>
    

    运行上面代码,点击会发现,输出的均为

    image-20200626101322509

    这是因为虽形成了三个闭包作用域,但他们共享了一个词法作用域,在这个作用域中又只有一个变量 ivar 的作用域数函数作用域,但定义 i 外并未有函数,所以这里的 i 是挂载在全局的。在循环后执行点击时间前,i 的值都已经指向了最后一个数 5了。

    解决方法(修改下列任何一种都可以)

    • var i=0; 换为 let i=0; ,这种也是最简单的,let 是 ES6 修复 var 的设计缺陷而出现的

    • 使用更多的闭包

      <button>按钮1</button>
      <button>按钮2</button>
      <button>按钮3</button>
      <button>按钮4</button>
      <button>按钮5</button>
      
      <script>
        function handleClick(i) {
          return function() {
            console.log('点击了第' + i + '个按钮')
          }
        }
        var btns = document.getElementsByTagName('button')
        for (var i = 0; i < btns.length; i++) {
          var btn= btns[i]
          btn.onclick= handleClick(i)
        }
      </script>
      
    • 匿名闭包

      <button>按钮1</button>
      <button>按钮2</button>
      <button>按钮3</button>
      <button>按钮4</button>
      <button>按钮5</button>
      
      <script>
        var btns = document.getElementsByTagName('button')
        for (var i = 0; i < btns.length; i++) {
          ;(function (i) {
            var btn= btns[i]
            btn.onclick= function () {
              console.log('点击了第' + i + '个按钮')
            }
          })(i)
        }
      </script>
      
  • 性能

    在处理速度和内存消耗方面都是有负面影响,所以原则是少用,别再不必要的地方使用。

缓存相关

缓存

浏览器缓存机制

强缓存

浏览器加载资源前,会根据请求头 expirescache-control 判断是否命中强缓存,如果命中,则不会发送请求到服务器。

Expires

http1.0 提出,表示资源过期时间的 header,描述为一个绝对时间,服务器返回。(如果修改本地时间,可能会造成缓存失效。)

expires: Tue, 14 Jul 2020 13:13:53 GMT

Cache-Control

http1.1 提出,优先级高于 Expires ,表示相对时间

缓存请求指令

Cache-Control: max-age=<seconds>
Cache-Control: max-stale[=<seconds>]	// 表示客户端接受一个已过期的资源
Cache-Control: min-fresh=<seconds>		// 表示客户端获取一个在指定的秒数内保持最新状态的响应
Cache-control: no-cache 							// 在发布缓存前,强制要求把缓存提交给服务器进行验证(协商缓存验证)
Cache-control: no-store								// 不存储和不使用任何缓存

缓存响应指令

Cache-control: must-revalidate
Cache-control: no-cache					// 如缓存请求指令
Cache-control: no-store					// 如缓存请求指令
Cache-control: public						// 表示响应可以为任何对象(客户端、代理服务器等,包括通常不可缓存的
Cache-control: private					// 只能被单个用户缓存(私有缓存)
Cache-Control: max-age=<seconds>	// 缓存存储最大周期,单位秒
Cache-control: s-maxage=<seconds>	// 覆盖 max-age或 expires 头,仅在共享缓存中有效

协商缓存

如果没有命中强缓存,浏览器会发送一个请求到服务器,通过 last-modifiedetag 验证资源是否命中协商缓存,如果命中,会将这个请求返回,但不返回资源的数据,依旧从缓存中读取资源。

Last-Modified 和 If-Modified-Since

http1.0 引入,Last-Modified 是响应头中的属性,表示资源的修改时间。浏览器请求时会在请求头上加上 If-Modified-Since (上次返回的Last-Modified的值),询问是否资源有更新,如有立刻将新的资源返回。

准确性不如 Etag,所以这个为备用机制。

ETag 和 If-None-Match

http1.1 引入,Etag就像一个指纹,资源变化都会导致ETag变化,跟最后修改时间没有关系,ETag可以保证每一个资源是唯一的。优先级高于 Last-Modified

If-None-Match的header会将上次返回的Etag发送给服务器,询问该资源的Etag是否有更新,有变动就会发送新的资源回来

异同点总结

  • 相同点,都是从客户端缓存中加载资源,而不是从服务器加载资源。
  • 不同点,强缓存不发送请求到服务器,协商缓存会发送请求到服务器。

缓存优先级顺序(由低到高)

强缓存

  • Expire
  • Cache-Control

协商缓存

  • Last-Modified 和 If-Modified-Since
  • ETag 和 If-None-Match

用户操作和行为

img

  • 普通F5刷新,强缓存失效,只进行协商缓存
  • Ctrl+F5刷新,强缓存和协商缓存均失效

性能优化

图片优化,可以减少 http 请求数量

  • 雪碧图、精灵图
  • Base64
  • 字体图标

压缩资源大小

  • HTML压缩
  • css 压缩
  • js 压缩与混淆
  • 图片压缩,(可以尝试webp格式)
  • 开启 gzip

缓存

  • DNS 缓存
  • http 缓存
  • 浏览器缓存

其他优化

  • 数据校验,避免无用数据重复发送

  • 不使用 css @import

  • 比如你使用空的 src 和 href(会重定向到当前页面地址)

  • 使用CDN

  • 减少重绘回流

  • webpack 打包使用优化插件

参考

闭包

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures

缓存

https://github.com/amandakelake/blog/issues/41

https://segmentfault.com/a/1190000021248694

性能优化

https://www.cnblogs.com/xiaohuochai/p/9178390.html