Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

面试题 #80

Open
caoxinhui opened this issue Aug 4, 2020 · 0 comments
Open

面试题 #80

caoxinhui opened this issue Aug 4, 2020 · 0 comments

Comments

@caoxinhui
Copy link
Owner

caoxinhui commented Aug 4, 2020

十道大厂面试题(含答案)总结

又到了跳槽季啦,该刷题走起了。这里总结了一些被问到可能会懵逼的面试真题,有需要的可以看下~

1.不借助变量,交换两个数

1. 算术交换

针对的是Number,或者类型可以转换成数字的变量类型

function swap(a, b) {
   a = a + b;
   b = a - b;
   a = a - b;
}

通过算术运算过程中的技巧,可以巧妙地将两个值进行互换。但是,有个缺点就是变量数据溢出。因为JavaScript能存储数字的精度范围是 -2^53 到 2^53。所以,加法运算,会存在溢出的问题。

2. 异或运算

^ 按位异或 若参加运算的两个二进制位值相同则为0,否则为1
此算法能够实现是由异或运算的特点决定的,通过异或运算能够使数据中的某些位翻转,其他位不变。这就意味着任意一个数与任意一个给定的值连续异或两次,值不变.

a = a ^ b;
b = a ^ b; 
a = a ^ b;

3. ES6的解构

[a, b] = [b, a];

更多参考:

2.实现sum(1,2,3)==sum(1)(2)(3)

function sum(...args){
  function currySum(...rest){
    args.push(...rest)
    return currySum
  }
  currySum.toString= function(){ 
    return args.reduce((result,cur)=>{
      return result + cur
    })
  }
  currySum.toNumber= function(){ 
    return args.reduce((result,cur)=>{
      return result + cur
    })
  }
  return currySum
}

更多参考:Advanced-Frontend/Daily-Interview-Question#134

3.观察者模式 vs 发布-订阅模式,说说区别

观察者模式的概念

观察者模式模式,属于行为型模式的一种,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主体对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。

发布订阅者模式的概念

发布-订阅模式,消息的发送方,叫做发布者(publishers),消息不会直接发送给特定的接收者,叫做订阅者。意思就是发布者和订阅者不知道对方的存在。需要一个第三方组件,叫做信息中介,它将订阅者和发布者串联起来,它过滤和分配所有输入的消息。换句话说,发布-订阅模式用来处理不同系统组件的信息交流,即使这些组件不知道对方的存在。

区别

我们把这些差异快速总结一下:

在观察者模式中,观察者是知道Subject的,Subject一直保持对观察者进行记录。然而,在发布订阅模式中,发布者和订阅者不知道对方的存在。它们只有通过消息代理进行通信。

在发布订阅模式中,组件是松散耦合的,正好和观察者模式相反。

观察者模式大多数时候是同步的,比如当事件触发,Subject就会去调用观察者的方法。而发布-订阅模式大多数时候是异步的(使用消息队列)。

观察者 模式需要在单个应用程序地址空间中实现,而发布-订阅更像交叉应用模式。

更多:https://juejin.im/post/5a14e9edf265da4312808d86
PS:最好把对应的模式的模型代码也写一下

4.实现LRU算法

实现一个LRU过期算法的KV cache, 所有KV过期间隔相同, 满足如下性质:

  1. 最多存储n对KV;
  2. 如果大于n个, 则随意剔除一个已经过期的KV;
  3. 如果没有过期的KV, 则按照LRU的规则剔除一个KV;
  4. 查询时如果已经过期, 则返回空;
class LRUCache {
    constructor(capacity,intervalTime){
        this.cache = new Map();
        this.capacity = capacity;
        this.intervalTime = intervalTime;
    }
    get(key){
        if(!this.cache.has(key)){
            return null
        }
        const tempValue = this.cache.get(key)
        this.cache.delete(key);
        if(Date.now() - tempValue.time > this.intervalTime){
            return null
        }
        this.cache.set(key, {value: tempValue.value, time: Date.now()})
        return tempValue.value
    }
    put(key, value){
        if(this.cache.has(key)){
            this.cache.delete(key)
        }
        if(this.cache.size >= capacity){ //满了
            const keys = this.cache.keys()
            this.cache.delete(keys.next().value)
        }
        this.cache.set(key, {value,time:Date.now()})
    }
}

巧妙地利用了Map结构的key是有序的这个特点。普通Object的key是无序的。

5.如何监控网页崩溃?

基于 Service Worker 的崩溃统计方案

随着 PWA 概念的流行,大家对 Service Worker 也逐渐熟悉起来。基于以下原因,我们可以使用 Service Worker 来实现网页崩溃的监控:

  1. Service Worker 有自己独立的工作线程,与网页区分开,网页崩溃了,Service Worker 一般情况下不会崩溃;
  2. Service Worker 生命周期一般要比网页还要长,可以用来监控网页的状态;
  3. 网页可以通过navigator.serviceWorker.controller.postMessage API 向掌管自己的 SW 发送消息。

基于以上几点,我们可以实现一种基于心跳检测的监控方案:

  • p1:网页加载后,通过postMessageAPI 每5s给 sw 发送一个心跳,表示自己的在线,sw 将在线的网页登记下来,更新登记时间;
  • p2:网页在beforeunload时,通过postMessageAPI 告知自己已经正常关闭,sw 将登记的网页清除;
  • p3:如果网页在运行的过程中 crash 了,sw 中的running状态将不会被清除,更新时间停留在奔溃前的最后一次心跳;
  • sw:Service Worker 每10s查看一遍登记中的网页,发现登记时间已经超出了一定时间(比如 15s)即可判定该网页 crash 了。

更多:https://zhuanlan.zhihu.com/p/40273861

6.求代码输出,并说出为什么

var obj = {
    '2':3,
    '3':4,
    'length':2,
    'splice':Array.prototype.splice,
    'push':Array.prototype.push
}
obj.push(1)
obj.push(2)
obj.push(3)
console.log(obj)

答案:

{
    '2':1
    '3':2,
    '4':3,
    'length':5,
    'splice':Array.prototype.splice,
    'push':Array.prototype.push
}

obj有长度,相当于类数组,调用数组的push,会在数组的最后加一项,第一次调用,相当于长度变为3,那么下标为2的那一项被赋值为1,下标是2,当其作为对象的key值的时候,会隐式调用toString方法转为字符串2,则和obj本来有的key '2'相同,原来的key为2的value就被覆盖了。以此类推后面的2个push。

详情:LuckyWinty/fe-weekly-questions#48

7.node中的 setTimeout 和 setImmediate 有什么区别

setImmediate() 和 setTimeout() 很类似,但是基于被调用的时机,他们也有不同表现。

  • setImmediate 设计在poll阶段完成时执行,即check阶段;
  • setTimeout 设计在poll阶段为空闲时,且设定时间到达后执行,但它在timer阶段执行

执行计时器的顺序将根据调用它们的上下文而异。如果二者都从主模块内调用,则计时器将受进程性能的约束。举个例子,有如下代码:

setTimeout(() => console.log(1));
setImmediate(() => console.log(2));

上面代码应该先输出1,再输出2,但是实际执行的时候,结果却是不确定,有时还会先输出2,再输出1。

这是因为setTimeout的第二个参数默认为0。但是实际上,Node 做不到0毫秒,最少也需要1毫秒,根据官方文档,第二个参数的取值范围在1毫秒到2147483647毫秒之间。也就是说,setTimeout(f, 0)等同于setTimeout(f, 1)。

实际执行的时候,进入事件循环以后,有可能到了1毫秒,也可能还没到1毫秒,取决于系统当时的状况。如果没到1毫秒,那么 timers 阶段就会跳过,进入 check 阶段,先执行setImmediate的回调函数。

但是,如果是这样的情况,输出顺序就固定了,例:

const fs = require('fs');
fs.readFile('test.js', () => {
 setTimeout(() => console.log(1));
 setImmediate(() => console.log(2));
});

在上述代码中,一定是先输出2,再输出1。因为两个代码写在 IO 回调中,IO 回调是在 poll 阶段执行,当回调执行完毕后队列为空,发现存在 setImmediate 回调,所以就直接跳转到 check 阶段去执行回调了,执行完成后再去到 timers 阶段,然后执行setTimeout。

8.写一个正则,根据name取cookie中的值。

function get(name){
  var reg = new RegExp(name+'=([^;]*)?(;|$)');
  var res = reg.exec(document.cookie);
  if(!res || !res[1])return '';
    try{
      if(/(%[0-9A-F]{2}){2,}/.test(res)){//utf8编码
      return decodeURIComponent(res);
    }else{//unicode编码
      return unescape(res);
    }
  }catch(e){
    return unescape(res);
  }
}

正则表达式中重点看这几句代码:
'([^;])', 意思是匹配str=后面的不为;
([^;]表示非集, 也就是所有不为;的字符都能被匹配)的字符串, 该字符串出现0或更多次(
), 之后将匹配到的字符串放入第一个捕获组.

详情:LuckyWinty/fe-weekly-questions#32

9.在不改变html结构的情况下,写出至少7种方法实现以下布局。

要求:左边2列,右边1列,中间自适应

    <div class="parent" style="width: 200px">
        <div class="child child1" style="width: 20px"></div>
        <div class="child child2" style="width: 20px">2</div>
        <div class="child child3" style="width: 20px">3</div>
    </div>

答案:

/* 1 */
    .parent{
        background-color: burlywood;
        display: flex;
    }
    .child{
        background-color: black;
        font-size: 20px;
        color: white;
    }
    .child3{
        margin-left: auto;
    }

/* 2 */
    .parent{
        background-color: burlywood;
        position: relative;
    }
    .child{
        font-size: 20px;
        color: white;
    }
    .child1{
        background-color: black;
        position: absolute;
        left: 0;
    }
    .child2{
        background-color: black;
        position: absolute;
        left: 20px;
    }
    .child3{
        background-color: black;
        position: absolute;
        right: 0;
    }

/* 3 */
    .parent{
        background-color: burlywood;
    }
    .child1{
        background-color: black;
        float: left;
    }
    .child2{
        background-color: red;
        float: left;
    }
    .child3{
        float: right;
        background-color: blue
    }

/* 4 */
    .parent{
        background-color: burlywood;
        display: table;
    }
    .child{
        background-color: black;
        display: table-cell;
        height: 20px;
    }
    .child3{
        display: block;
        margin-left: auto;
    }

/* 5 */
    .parent{
        background-color: burlywood;
        position: relative;
    }
    .child{
        background-color: black;
        position: absolute;
        top:0;
        left:0;
    }
    .child2{
        transform: translate(20px, 0);
    }
    .child3{
        transform: translate(180px, 0);
    }

/* 6 */
  .parent{
        background-color: burlywood;
        display: grid;
        grid-template-columns: repeat(10, 1fr);
    }
    .child{
        background-color: black;
        font-size: 20px;
        color:white;
    }
    .child3{
        grid-column: 10 / 11;
    }

/* 7 */
    .parent{
        background-color: burlywood;
        font-size: 0;
    }
    .child{
        background-color: black;
        display: inline-block;
        font-size: 20px;
        color: white;
    }
    .child3{
        margin-left: 140px;
    }

10.用css画一个扇形?

width: 0;
height: 0;
border: solid 100px red;
border-color: red transparent transparent transparent;
border-radius: 100px;

每周N题12月汇总

年底了,又到了跳槽季啦,该刷题走起了。这里总结了一些被问到可能会懵逼的面试真题,有需要的可以看下~

1. 说说JavaScript中有哪些异步编程方式?

1. 回调函数

f1(f2);

回调函数是异步编程的基本方法。其优点是易编写、易理解和易部署;缺点是不利于代码的阅读和维护,各个部分之间高度耦合 (Coupling),流程比较混乱,而且每个任务只能指定一个回调函数。

2. 事件监听

f1.on('done',f2);

事件监听即采用事件驱动模式,任务的执行不取决于代码的顺序,而取决于某个事件是否发生。其优点是易理解,可以绑定多个事件,每个事件可以指定多个回调函数,可以去耦合, 有利于实现模块化;缺点是整个程序都要变成事件驱动型,运行流程会变得不清晰。

3. 发布/订阅

f1: jQuery.publish("done");
f2: jQuery.subscribe("done", f2);

假定存在一个"信号中心",某个任务执行完成,就向信号中心"发布"(publish)一个信号,其他任务可以向信号中心"订阅"(subscribe)这个信号,从而知道什么时候自己可以开始执行,这就叫做 "发布/订阅模式" (publish-subscribe pattern),又称 "观察者模式" (observer pattern)。该 方法的性质与"事件监听"类似,但其优势在于可以 通过查看"消息中心",了解存在多少信号、每个信号有多少订阅者,从而监控程序的运行。

4. promise对象

f1().then(f2);

Promises对象是CommonJS工作组提出的一种规范,目的是为异步编程提供 统一接口 ;思想是, 每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数。其优点是回调函数是链式写法,程序的流程非常清晰,而且有一整套的配套方法, 可以实现许多强大的功能,如指定多个回调函数、指定发生错误时的回调函数, 如果一个任务已经完成,再添加回调函数,该回调函数会立即执行,所以不用担心是否错过了某个事件或信号;缺点就是编写和理解相对比较难。

2. 有哪些监控网页卡顿的方法?

卡顿

网页的 FPS

网页内容在不断变化之中,网页的 FPS 是只浏览器在渲染这些变化时的帧率。帧率越高,用户感觉网页越流畅,反之则会感觉卡顿。

监控卡顿方法

每秒中计算一次网页的 FPS 值,获得一列数据,然后分析。通俗地解释就是,通过 requestAnimationFrame API 来定时执行一些 JS 代码,如果浏览器卡顿,无法很好地保证渲染的频率,1s 中 frame 无法达到 60 帧,即可间接地反映浏览器的渲染帧率。

var lastTime = performance.now();
var frame = 0;
var lastFameTime = performance.now();
var loop = function(time) {
    var now =  performance.now();
    var fs = (now - lastFameTime);
    lastFameTime = now;
    var fps = Math.round(1000/fs);
    frame++;
    if (now > 1000 + lastTime) {
        var fps = Math.round( ( frame * 1000 ) / ( now - lastTime ) );
        frame = 0;    
        lastTime = now;    
    };           
    window.requestAnimationFrame(loop);   
}

我们可以定义一些边界值,比如连续出现3个低于20的 FPS 即可认为网页存在卡顿。

3. 说说script 标签中的defer 和 async 异同点?

defer

这个属性的用途是表明脚本在执行时不会影响页面的构造。也就是说,脚本会被延迟到整个页面都解析完毕后再运行。因此,在script元素中设置defer属性,相当于告诉浏览器立即下载,但延迟执行。

HTML5规范要求脚本按照它们出现的先后顺序执行,因此第一个延迟脚本会先于第二个延迟脚本执行,而这两个脚本会先于DOMContentLoaded事件执行。在现实当中,延迟脚本并不一定会按照顺序执行,也不一定会在DOMContentLoad时间触发前执行,因此最好只包含一个延迟脚本。

对于不支持的浏览器,如safari,并不会延迟执行,还是会阻塞浏览器渲染。

async

这个属性与defer类似,都用于改变处理脚本的行为。同样与defer类似,async只适用于外部脚本文件,并告诉浏览器立即下载文件。但与defer不同的是,标记为async的脚本并不保证按照它们的先后顺序执行。

第二个脚本文件可能会在第一个脚本文件之前执行。因此确保两者之间互不依赖非常重要。指定async属性的目的是不让页面等待两个脚本下载和执行,从而异步加载页面其他内容。

总结

async 和 defer 虽然都是异步的,不过还有一些差异,使用 async 标志的脚本文件一旦加载完成,会立即执行;而使用了 defer 标记的脚本文件,需要在 DOMContentLoaded 事件之前执行。

可以再扩展一下:link preload也可以用于js提前加载,和上述两者的区别? 还有应用场景上的建议

4. margin:auto 为什么可以实现垂直居中?

margin概念:

margin属性为给定元素设置所有四个(上下左右)方向的外边距属性。这是四个外边距属性设置的简写。四个外边距属性设置分别是: margin-top,margin-right,margin-bottom和margin-left。指定的外边距允许为负数。

margin的top和bottom属性对非替换内联元素无效,例如spancode

实现垂直居中

想要实现垂直方向的居中可以用绝对定位:

div  {
        width: 20px;
        height: 20px;
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        margin: auto;
     }

为什么能实现垂直居中

块状水平元素,如div元素(下同),在默认情况下(非浮动、绝对定位等),水平方向会自动填满外部的容器;如果有margin-left/margin-right,padding-left/padding-right,border-left-width/border-right-width等,实际内容区域会响应变窄。

但是,当一个绝对定位元素,其对立定位方向属性同时有具体定位数值的时候,流体特性就发生了。具有流体特性绝对定位元素的margin:auto的填充规则和普通流体元素一模一样,含有以下特性:

  • 如果一侧定值,一侧auto,auto为剩余空间大小;

  • 如果两侧均是auto, 则平分剩余空间

5. Cdn有哪些优化静态资源加载速度的方法?

可以参考阿里云团队的《CDN之我见》。总结如下:

资源调度:CDN会根据用户接入网络的ip寻找距离用户最优路径的服务器。调度的方式主要有DNS调度、http 302调度、使用 HTTP 进行的 DNS 调度(多用于移动端);
缓存策略和数据检索:CDN服务器使用高效的算法和数据结构,快速的检索资源和更新读取缓存;
网络优化:从OSI七层模型进行优化,达到网络优化的目的。
L1物理层:硬件设备升级提高速度
L2数据链路层:寻找更快的网络节点、确保 Lastmile 尽量短
L3路由层:路径优化,寻找两点间最优路径
L4传输层:协议TCP优化,保持长连接、TCP快速打开
L7应用层:静态资源压缩、请求合并

6. fetch 和 ajax 的区别?

Ajax 技术的核心是XMLHttpRequest 对象(简称XHR)。
XHR 为向服务器发送请求和解析服务器响应提供了流畅的接口。能够以异步方式从服务器取得更多信息,意味着用户单击后,可以不必刷新页面也能取得新数据。
看一个调用例子:

var xhr = createXHR();
xhr.onreadystatechange = function(){
    if (xhr.readyState == 4){
       if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304){
          alert(xhr.responseText);
          } else {
          alert("Request was unsuccessful: " + xhr.status);
        }
    }
};
xhr.open("get", "example.txt", true);
xhr.send(null);

fetch号称是ajax的替代品,它的API是基于Promise设计的,旧版本的浏览器不支持Promise,需要使用polyfill es6-promise,举个例子:

// 原生XHR
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onreadystatechange = function() {
    if (xhr.readyState === 4 && xhr.status === 200) {
        console.log(xhr.responseText)   // 从服务器获取数据
    }
}
xhr.send()
// fetch
fetch(url)
    .then(response => {
        if (response.ok) {
            return response.json();
        }
    })
    .then(data => console.log(data))
    .catch(err => console.log(err))

看起来好像是方便点,then链就像之前熟悉的callback。
在MDN上,讲到它跟jquery ajax的区别,这也是fetch很奇怪的地方:

当接收到一个代表错误的 HTTP 状态码时,从 fetch()返回的 Promise 不会被标记为 reject, 即使该 HTTP 响应的状态码是 404 或 500。相反,它会将 Promise 状态标记为 resolve (但是会将 resolve 的返回值的 ok 属性设置为 false ), 仅当网络故障时或请求被阻止时,才会标记为 reject。
默认情况下, fetch 不会从服务端发送或接收任何 cookies, 如果站点依赖于用户 session,则会导致未经认证的请求(要发送 cookies,必须设置 credentials 选项).

7. 求代码输出值(函数/原型 指向问题)?

function Foo() {
  getName = function () { console.log(1) }
  return this
}
Foo.getName = function () { console.log(2) }
Foo.prototype.getName = function() { console.log(3) }
var getName = function () { console.log(4) }
function getName() {
  console.log(5)
}

Foo.getName()
getName()
Foo().getName()
getName()
new Foo.getName()
new Foo().getName()
new new Foo().getName()

输出:

2

函数也是一个 object, 可以拥有属性和方法, Foo.getName 被赋值为一个输出 2 的函数, 所以输出 2

Foo.getName = function () { console.log(2) }
Foo.getName() // 输出 2

4

getName() 父级作用域为 window, 相当于调用 window.getName(), Foo() 还未被执行, 不需要考虑 Foo 函数体内对 getName 的影响, 剩下最后两个

var getName = function () { console.log(4) } // 函数表达式
function getName() { // 函数声明
  console.log(5)
}

函数声明会提升, 也就是即使在后边声明的函数, 在前别也能调用, 如:

getName() // 输出 5
function getName() { // 函数声明
  console.log(5)
}

但题中函数表达式会覆盖函数声明, 来看下边这段:

getName() // 输出 5, 函数声明提升的结果
var getName = function () { console.log(4) } // 函数表达式
getName() // 输出 4, 函数声明提升后, 又被函数表达式覆盖了
function getName() { // 函数声明
  console.log(5)
}
getName() // 还是输出 4, 覆盖的结果, 函数声明提升了, 不按照文本的顺序重新声明的, 你可以想象它被移到了最前边

1

Foo().getName()

我们一步步拆解, 上边的语句相当于

var context = Foo();
context.getName();

来看 Foo 的声明:

function Foo() {
  // 下边这句没有 var, let 或 const, 相当于 window.getName = xxx
  getName = function () { console.log(1) }
  return this // 这里的 this 要看调用方式, 直接调用 Foo() 则 this 指向 window, new 调用, this 指向 new 出来的实例
}

仔细看上边的注释, Foo 函数体内对 window.getName 进行了改写, 这是下一个输出的关键

1

如上边分析的, Foo() 函数的执行, 对 window.getName 进行了改写, window.getName 此时已经变为 function () { console.log(1) }

2

new Foo.getName()

该语句先执行 Foo.getName, 与第一个结论一致, 输出 2, 只是 new 会返回一个 object, 这个 object 指向 new 出来的实例, 但这里这个实例没被使用, 就不进一步分析了

3

new Foo().getName()

拆解如下

var foo = new Foo()
foo.getName()

如果你是一路看分析下来的, 就会明白 foo 这个实例, 就是 Foo 函数体里的 this. 从原型的知识中, 我们可以知道, 如果调用一个实例的方法, 在实例方法中找不到, 就会从实例原型中找.

也就是会找到下边这个方法, 并执行:

Foo.prototype.getName = function() { console.log(3) }

3

new new Foo().getName()

拆解如下

var foo = new Foo();
var bar = new foo.getName();

从上边 new Foo().getName() 的分析, 可以知道 foo.getName() 是在 foo 的原型里边的, 这里 new 了一下原型里边的函数, 相当于先执行了, 再返回了一个新的实例. 这里的执行, 也就是执行了下边这个方法:

Foo.prototype.getName = function() { console.log(3) }

只是 new 额外返回一个实例, 实例没被使用, 没什么特殊的.

如果还有不明白的地方, 建议阅读 《JavaScript 语言精髓》第四章 (本人看的是修订版) 关于函数调用相关的内容. 相关电子书可以微信关注公众号 前端Q, 回复 ebook 阅读. 此外, 建议购买正版.

8. 如何判断左右小括号是否全部匹配。如 ( ( ))()((((()))))?

/**
 * @param {string} s
 * @return {boolean}
 */
var isValid = function(s) {
    var stack = []
    var map = { 
        '(' : ')',
        '[': ']',
        '{': '}'
    }
    
    for (var char of s) {
        if(char in map) {
            stack.push(char)
        } else {
            if( !stack.length || char != map[stack.pop()]) {
                return false
            }
        }
    }
    
    // 如果最后stack 里没有元素了, 就一定是匹配的
    return !stack.length
};

9. 用css画一个扇形?

width: 0;
height: 0;
border: solid 100px red;
border-color: red transparent transparent transparent;
border-radius: 100px;

10. 用css实现已知或者未知宽度的垂直水平居中?

/
/ 1
.wraper {
position: relative;
.box {
position: absolute;
top: 50%;
left: 50%;
width: 100px;
height: 100px;
margin: -50px 0 0 -50px;
}
}


// 2
.wraper {
position: relative;
.box {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}


// 3
.wraper {
.box {
display: flex;
justify-content:center;
align-items: center;
height: 100px;
}
}


// 4
.wraper {
display: table;
.box {
display: table-cell;
vertical-align: middle;
}
}
//5
.container {
display: grid;
grid-auto-columns: 1fr;
grid-auto-rows: 200px;
background: #eee;
}
.parent {
background: grey;
justify-self: center;
align-self: center;
}
.child {
font-size: 30px;
}
//6、块级元素:calc()
.parent {
width: 300px;
height: 300px;
border: 1px solid red;
position: relative;
}
.child {
width: 100px;
height: 100px;
background: blue;
padding: -webkit-calc((100% - 100px) / 2);
padding: -moz-calc((100% - 100px) / 2);
padding: -ms-calc((100% - 100px) / 2);
padding: calc((100% - 100px) / 2);
background-clip: content-box;
}
//7、margin:auto实现绝对定位元素的居中
.element {
width: 600px; height: 400px;
position: absolute; left: 0; top: 0; right: 0; bottom: 0;
margin: auto; /* 有了这个就自动居中了 */
}
//8、
.parent{
display: flex;
}
.parent{
display: flex;
width: 500px;
height: 500px;
background-color: pink;
}
.child{
flex: 0 0 auto;
margin: auto;
width: 100px;
height: 100px;
background-color: red;

11. 如何理解回流和重绘? ?

回流:当我们对 DOM 的修改引发了 DOM 几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性(其他元素的几何属性和位置也会因此受到影响),然后再将计算的结果绘制出来。这个过程就是回流(也叫重排)。

重绘:当我们对 DOM 的修改导致了样式的变化、却并未影响其几何属性(比如修改了颜色或背景色)时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式(跳过了上图所示的回流环节)。这个过程叫做重绘。 由此我们可以看出,重绘不一定导致回流,回流一定会导致重绘。

常见的会导致回流的元素:
  • 常见的几何属性有 width、height、padding、margin、left、top、border 等等。
  • 最容易被忽略的操作:获取一些需要通过即时计算得到的属性,当你要用到像这样的属性:offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight 时,浏览器为了获取这些值,也会进行回流。
  • 当我们调用了 getComputedStyle 方法,或者 IE 里的 currentStyle 时,也会触发回流。原理是一样的,都为求一个“即时性”和“准确性”。
避免方式:
  1. 避免逐条改变样式,使用类名去合并样式
  2. 将 DOM “离线”,使用DocumentFragment
  3. 提升为合成层,如使用will-change
#divId {
  will-change: transform;
}

优点

  • 合成层的位图,会交由 GPU 合成,比 CPU 处理要快
  • 当需要 repaint 时,只需要 repaint 本身,不会影响到其他的层
  • 对于 transform 和 opacity 效果,不会触发 layout 和 paint

注意:

部分浏览器缓存了一个 flush 队列,把我们触发的回流与重绘任务都塞进去,待到队列里的任务多起来、或者达到了一定的时间间隔,或者“不得已”的时候,再将这些任务一口气出队。但是当我们访问一些即使属性时,浏览器会为了获得此时此刻的、最准确的属性值,而提前将 flush 队列的任务出队。

12. 手写一个Promise?

答案太长,你可以参考这个issues:LuckyWinty/fe-weekly-questions#20

13. 谈谈web安全问题及解决方案

答案太长,你可以参考这个issues:LuckyWinty/fe-weekly-questions#1

14. HTTPS和HTTP有什么区别?

答案太长,你可以参考这个issues:LuckyWinty/fe-weekly-questions#2

15. Webpack性能优化你知道哪些?

答案太长,你可以参考这个issues:LuckyWinty/fe-weekly-questions#4

更多

本期汇总暂时到这里,更多题目,可以关注:
https://github.com/LuckyWinty/fe-weekly-questions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant