乐趣区

关于javascript:2022秋招前端面试题三附答案

TCP 粘包是怎么回事,如何解决?

默认状况下, TCP 连贯会启⽤提早传送算法 (Nagle 算法), 在数据发送之前缓存他们. 如果短时间有多个数据发送, 会缓冲到⼀起作⼀次发送 (缓冲⼤⼩⻅ socket.bufferSize), 这样能够缩小 IO 耗费提⾼性能.

如果是传输⽂件的话, 那么基本不⽤解决粘包的问题, 来⼀个包拼⼀个包就好了。然而如果是多条音讯, 或者是别的⽤途的数据那么就须要解决粘包.

上面看⼀个例⼦, 间断调⽤两次 send 别离发送两段数据 data1 和 data2, 在接收端有以下⼏种常⻅的状况:
A. 先接管到 data1, 而后接管到 data2 .
B. 先接管到 data1 的局部数据, 而后接管到 data1 余下的局部以及 data2 的全副.
C. 先接管到了 data1 的全副数据和 data2 的局部数据, 而后接管到了 data2 的余下的数据.
D. ⼀次性接管到了 data1 和 data2 的全副数据.

其中的 BCD 就是咱们常⻅的粘包的状况. ⽽对于解决粘包的问题, 常⻅的解决⽅案有:

  • 屡次发送之前距离⼀个等待时间:只须要等上⼀段时间再进⾏下⼀次 send 就好, 适⽤于交互频率特地低的场景. 毛病也很显著, 对于⽐较频繁的场景⽽⾔传输效率切实太低,不过⼏乎不⽤做什么解决.
  • 敞开 Nagle 算法:敞开 Nagle 算法, 在 Node.js 中你能够通过 socket.setNoDelay() ⽅法来敞开 Nagle 算法, 让每⼀次 send 都不缓冲间接发送。该⽅法⽐较适⽤于每次发送的数据都⽐较⼤ (但不是⽂件那么⼤), 并且频率不是特地⾼的场景。如果是每次发送的数据量⽐较⼩, 并且频率特地⾼的, 敞开 Nagle 纯属⾃废文治。另外, 该⽅法不适⽤于⽹络较差的状况, 因为 Nagle 算法是在服务端进⾏的包合并状况, 然而如果短时间内客户端的⽹络状况不好, 或者应⽤层因为某些起因不能及时将 TCP 的数据 recv, 就会造成多个包在客户端缓冲从⽽粘包的状况。(如果是在稳固的机房外部通信那么这个概率是⽐较⼩能够抉择疏忽的)
  • 进⾏封包 / 拆包: 封包 / 拆包是⽬前业内常⻅的解决⽅案了。即给每个数据包在发送之前, 于其前 / 后放⼀些有特色的数据, 而后收到数据的时 候依据特色数据宰割进去各个数据包。

::before 和 :after 的双冒号和单冒号有什么区别?

(1)冒号 (:) 用于 CSS3 伪类,双冒号 (::) 用于 CSS3 伪元素。
(2)::before就是以一个子元素的存在,定义在元素主体内容之前的一个伪元素。并不存在于 dom 之中,只存在在页面之中。

留神: :before:after 这两个伪元素,是在 CSS2.1 里新呈现的。起初,伪元素的前缀应用的是单冒号语法,但随着 Web 的进化,在 CSS3 的标准里,伪元素的语法被批改成应用双冒号,成为::before::after

Vue 的父子组件生命周期钩子函数执行程序?

<!-- 加载渲染过程 -->
    <!-- 父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created ->
    子 beforeMount -> 子 mounted -> 父 mounted -->
    <!-- 子组件更新过程 -->
    <!-- 父 beforeUpdate -> 子 beforeUpdate -> 子 updaed -> 父 updated -->
    <!-- 父组件跟新过程 -->
    <!-- 父 beforeUpdate -> 父 updated -->
    <!-- 销毁过程 -->
    <!-- 父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed -->
复制代码

说一下你对盒模型的了解?

CSS3 中的盒模型有以下两种: 规范盒模型、IE 盒模型
盒模型都是由四个局部组成的, 别离是 margin、border、padding 和 content
规范盒模型和 IE 盒模型的区别在于设置 width 和 height 时, 所对应的范畴不同
1、规范盒模型的 width 和 height 属性的范畴只蕴含了 content
2、IE 盒模型的 width 和 height 属性的范畴蕴含了 border、padding 和 content
能够通过批改元素的 box-sizing 属性来扭转元素的盒模型;1、box-sizing:content-box 示意规范盒模型(默认值)2、box-sizing:border-box 示意 IE 盒模型(怪异盒模型)复制代码

Vue 的生命周期是什么 每个钩子外面具体做了什么事件

Vue 实例有⼀个残缺的⽣命周期,也就是从开始创立、初始化数据、编译模版、挂载 Dom -> 渲染、更新 -> 渲染、卸载 等⼀系列过程,称这是 Vue 的⽣命周期。1、beforeCreate(创立前):数据观测和初始化事件还未开始,此时 data 的响应式追踪、event/watcher 都还没有被设置,也就是说不能拜访到 data、computed、watch、methods 上的办法和数据。2、created(创立后):实例创立实现,实例上配置的 options 包含 data、computed、watch、methods 等都配置实现,然而此时渲染得节点还未挂载到 DOM,所以不能拜访到 `$el` 属性。3、beforeMount(挂载前):在挂载开始之前被调用,相干的 render 函数首次被调用。实例已实现以下的配置:编译模板,把 data 外面的数据和模板生成 html。此时还没有挂载 html 到页面上。4、mounted(挂载后):在 el 被新创建的 vm.$el 替换,并挂载到实例下来之后调用。实例已实现以下的配置:用下面编译好的 html 内容替换 el 属性指向的 DOM 对象。实现模板中的 html 渲染到 html 页面中。此过程中进行 ajax 交互。5、beforeUpdate(更新前):响应式数据更新时调用,此时尽管响应式数据更新了,然而对应的实在 DOM 还没有被渲染。6、updated(更新后):在因为数据更改导致的虚构 DOM 从新渲染和打补丁之后调用。此时 DOM 曾经依据响应式数据的变动更新了。调用时,组件 DOM 曾经更新,所以能够执行依赖于 DOM 的操作。然而在大多数状况下,应该防止在此期间更改状态,因为这可能会导致更新有限循环。该钩子在服务器端渲染期间不被调用。7、beforeDestroy(销毁前):实例销毁之前调用。这一步,实例依然齐全可用,`this` 仍能获取到实例。8、destroyed(销毁后):实例销毁后调用,调用后,Vue 实例批示的所有货色都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务端渲染期间不被调用。另外还有 `keep-alive` 独有的生命周期,别离为 `activated` 和 `deactivated`。用 `keep-alive` 包裹的组件在切换时不会进行销毁,而是缓存到内存中并执行 `deactivated` 钩子函数,命中缓存渲染后会执行 `activated` 钩子函数。复制代码

Loader 和 Plugin 有什么区别

Loader:直译为 ” 加载器 ”。Webpack 将所有文件视为模块,然而 webpack 原生是只能解析 js 文件,如果想将其余文件也打包的话,就会用到loader。所以 Loader 的作用是让 webpack 领有了加载和解析非 JavaScript 文件的能力。Plugin:直译为 ” 插件 ”。Plugin 能够扩大 webpack 的性能,让 webpack 具备更多的灵活性。在 Webpack 运行的生命周期中会播送出许多事件,Plugin 能够监听这些事件,在适合的机会通过 Webpack 提供的 API 扭转输入后果。

iframe 有那些长处和毛病?

iframe 元素会创立蕴含另外一个文档的内联框架(即行内框架)。

长处:

  • 用来加载速度较慢的内容(如广告)
  • 能够使脚本能够并行下载
  • 能够实现跨子域通信

毛病:

  • iframe 会阻塞主页面的 onload 事件
  • 无奈被一些搜索引擎索辨认
  • 会产生很多页面,不容易治理

解析 URL 参数为对象

function parseParam(url) {const paramsStr = /.+\?(.+)$/.exec(url)[1]; // 将 ? 前面的字符串取出来
    const paramsArr = paramsStr.split('&'); // 将字符串以 & 宰割后存到数组中
    let paramsObj = {};
    // 将 params 存到对象中
    paramsArr.forEach(param => {if (/=/.test(param)) { // 解决有 value 的参数
            let [key, val] = param.split('='); // 宰割 key 和 value
            val = decodeURIComponent(val); // 解码
            val = /^\d+$/.test(val) ? parseFloat(val) : val; // 判断是否转为数字

            if (paramsObj.hasOwnProperty(key)) { // 如果对象有 key,则增加一个值
                paramsObj[key] = [].concat(paramsObj[key], val);
            } else { // 如果对象没有这个 key,创立 key 并设置值
                paramsObj[key] = val;
            }
        } else { // 解决没有 value 的参数
            paramsObj[param] = true;
        }
    })

    return paramsObj;
}
复制代码

什么是原型什么是原型链?

<!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>Document</title>
</head>
<body>

</body>
<script>
    function Person () {}    var person  = new Person();    person.name = 'Kevin';    console.log(person.name) // Kevin

    // prototype
    function Person () {}    Person.prototype.name = 'Kevin';    var person1 = new Person();    var person2 = new Person();    console.log(person1.name)// Kevin
    console.log(person2.name)// Kevin

    // __proto__
    function Person () {}    var person = new Person();    console.log(person.__proto__ === Person.prototype) // true

    //constructor
    function Person() {}    console.log(Person === Person.prototype.constructor) // true

    // 综上所述
    function Person () {}    var person = new Person()    console.log(person.__proto__ == Person.prototype) // true
    console.log(Person.prototype.constructor == Person) // true
    // 顺便学习一下 ES5 得办法, 能够取得对象得原型
    console.log(Object.getPrototypeOf(person) === Person.prototype) // true

    // 实例与原型
    function Person () {}    Person.prototype.name = 'Kevin';    var person = new Person();    person.name = 'Daisy';    console.log(person.name) // Daisy
    delete person.name;    console.log(person.name) // Kevin

    // 原型得原型
    var obj = new Object();    obj.name = 'Kevin',    console.log(obj.name) //Kevin

     // 原型链
     console.log(Object.prototype.__proto__ === null) //true
     // null 示意 "没用对象" 即该处不应该有值

     // 补充
     function Person() {}     var person = new Person()     console.log(person.constructor === Person) // true
     // 当获取 person.constructor 时,其实 person 中并没有 constructor 属性, 当不能读取到 constructor 属性时, 会从 person 的原型
     // 也就是 Person.prototype 中读取时, 正好原型中有该属性, 所以
     person.constructor === Person.prototype.constructor

     //__proto__
     // 其次是__proto__,绝大部分浏览器都反对这个非标准的办法拜访原型,然而它并不存在于 Person.prototype 中,实际上,它
     // 是来自与 Object.prototype, 与其说是一个属性,不如说是一个 getter/setter, 当应用 obj.__proto__时,能够了解成返回了
     // Object.getPrototypeOf(obj)
     总结:1、当一个对象查找属性和办法时会从本身查找, 如果查找不到则会通过__proto__指向被实例化的构造函数的 prototype     2、隐式原型也是一个对象, 是指向咱们构造函数的原型     3、除了最顶层的 Object 对象没有__proto_,其余所有的对象都有__proto__, 这是隐式原型     4、隐式原型__proto__的作用是让对象通过它来始终往上查找属性或办法,直到找到最顶层的 Object 的__proto__属性,它的值是 null, 这个查找的过程就是原型链



</script>
</html>
复制代码

NaN 是什么,用 typeof 会输入什么?

Not a Number,示意非数字,typeof NaN === ‘number’

代码输入问题

function fun(n, o) {console.log(o)
  return {fun: function(m){return fun(m, n);
    }
  };
}
var a = fun(0);  a.fun(1);  a.fun(2);  a.fun(3);
var b = fun(0).fun(1).fun(2).fun(3);
var c = fun(0).fun(1);  c.fun(2);  c.fun(3);
复制代码

输入后果:

undefined  0  0  0
undefined  0  1  2
undefined  0  1  1
复制代码

这是一道对于闭包的题目,对于 fun 办法,调用之后返回的是一个对象。咱们晓得,当调用函数的时候传入的实参比函数申明时指定的形参个数要少,剩下的形参都将设置为 undefined 值。所以 console.log(o); 会输入 undefined。而 a 就是是 fun(0)返回的那个对象。也就是说,函数 fun 中参数 n 的值是 0,而返回的那个对象中,须要一个参数 n,而这个对象的作用域中没有 n,它就持续沿着作用域向上一级的作用域中寻找 n,最初在函数 fun 中找到了 n,n 的值是 0。理解了这一点,其余运算就很简略了,以此类推。

手写公布订阅

class EventListener {listeners = {};
    on(name, fn) {(this.listeners[name] || (this.listeners[name] = [])).push(fn)
    }
    once(name, fn) {let tem = (...args) => {this.removeListener(name, fn)
            fn(...args)
        }
        fn.fn = tem
        this.on(name, tem)
    }
    removeListener(name, fn) {if (this.listeners[name]) {this.listeners[name] = this.listeners[name].filter(listener => (listener != fn && listener != fn.fn))
        }
    }
    removeAllListeners(name) {if (name && this.listeners[name]) delete this.listeners[name]
        this.listeners = {}}
    emit(name, ...args) {if (this.listeners[name]) {this.listeners[name].forEach(fn => fn.call(this, ...args))
        }
    }
}

同步和异步的区别

  • 同步 指的是当一个过程在执行某个申请时,如果这个申请须要期待一段时间能力返回,那么这个过程会始终期待上来,直到音讯返回为止再持续向下执行。
  • 异步 指的是当一个过程在执行某个申请时,如果这个申请须要期待一段时间能力返回,这个时候过程会持续往下执行,不会阻塞期待音讯的返回,当音讯返回时零碎再告诉过程进行解决。

介绍下 promise 的个性、优缺点,外部是如何实现的,入手实现 Promise

1)Promise 根本个性

  • 1、Promise 有三种状态:pending(进行中)、fulfilled(已胜利)、rejected(已失败)
  • 2、Promise 对象承受一个回调函数作为参数, 该回调函数承受两个参数,别离是胜利时的回调 resolve 和失败时的回调 reject;另外 resolve 的参数除了正常值以外,还可能是一个 Promise 对象的实例;reject 的参数通常是一个 Error 对象的实例。
  • 3、then 办法返回一个新的 Promise 实例,并接管两个参数 onResolved(fulfilled 状态的回调);onRejected(rejected 状态的回调,该参数可选)
  • 4、catch 办法返回一个新的 Promise 实例
  • 5、finally 办法不论 Promise 状态如何都会执行,该办法的回调函数不承受任何参数
  • 6、Promise.all()办法将多个多个 Promise 实例,包装成一个新的 Promise 实例,该办法承受一个由 Promise 对象组成的数组作为参数 (Promise.all() 办法的参数能够不是数组,但必须具备 Iterator 接口,且返回的每个成员都是 Promise 实例),留神参数中只有有一个实例触发 catch 办法,都会触发 Promise.all()办法返回的新的实例的 catch 办法,如果参数中的某个实例自身调用了 catch 办法,将不会触发 Promise.all()办法返回的新实例的 catch 办法
  • 7、Promise.race()办法的参数与 Promise.all 办法一样,参数中的实例只有有一个率先扭转状态就会将该实例的状态传给 Promise.race()办法,并将返回值作为 Promise.race()办法产生的 Promise 实例的返回值
  • 8、Promise.resolve()将现有对象转为 Promise 对象,如果该办法的参数为一个 Promise 对象,Promise.resolve()将不做任何解决;如果参数 thenable 对象 (即具备 then 办法),Promise.resolve() 将该对象转为 Promise 对象并立刻执行 then 办法;如果参数是一个原始值,或者是一个不具备 then 办法的对象,则 Promise.resolve 办法返回一个新的 Promise 对象,状态为 fulfilled,其参数将会作为 then 办法中 onResolved 回调函数的参数,如果 Promise.resolve 办法不带参数,会间接返回一个 fulfilled 状态的 Promise 对象。须要留神的是,立刻 resolve()的 Promise 对象,是在本轮“事件循环”(event loop)的完结时执行,而不是在下一轮“事件循环”的开始时。
  • 9、Promise.reject()同样返回一个新的 Promise 对象,状态为 rejected,无论传入任何参数都将作为 reject()的参数

2)Promise 长处

  • ①对立异步 API

    • Promise 的一个重要长处是它将逐步被用作浏览器的异步 API,对立当初各种各样的 API,以及不兼容的模式和手法。
  • ②Promise 与事件比照

    • 和事件相比拟,Promise 更适宜解决一次性的后果。在后果计算出来之前或之后注册回调函数都是能够的,都能够拿到正确的值。Promise 的这个长处很天然。然而,不能应用 Promise 解决屡次触发的事件。链式解决是 Promise 的又一长处,然而事件却不能这样链式解决。
  • ③Promise 与回调比照

    • 解决了回调天堂的问题,将异步操作以同步操作的流程表达出来。
  • ④Promise 带来的额定益处是蕴含了更好的错误处理形式(蕴含了异样解决),并且写起来很轻松(因为能够重用一些同步的工具,比方 Array.prototype.map())。

3)Promise 毛病

  • 1、无奈勾销 Promise,一旦新建它就会立刻执行,无奈中途勾销。
  • 2、如果不设置回调函数,Promise 外部抛出的谬误,不会反馈到内部。
  • 3、当处于 Pending 状态时,无奈得悉目前停顿到哪一个阶段(刚刚开始还是行将实现)。
  • 4、Promise 真正执行回调的时候,定义 Promise 那局部实际上曾经走完了,所以 Promise 的报错堆栈上下文不太敌对。

4)简略代码实现
最简略的 Promise 实现有 7 个次要属性, state(状态), value(胜利返回值), reason(错误信息), resolve 办法, reject 办法, then 办法

class Promise{constructor(executor) {
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    let resolve = value => {if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
      }
    };
    let reject = reason => {if (this.state === 'pending') {
        this.state = 'rejected';
        this.reason = reason;
      }
    };
    try {
      // 立刻执行函数
      executor(resolve, reject);
    } catch (err) {reject(err);
    }
  }
  then(onFulfilled, onRejected) {if (this.state === 'fulfilled') {let x = onFulfilled(this.value);
    };
    if (this.state === 'rejected') {let x = onRejected(this.reason);
    };
  }
}

5)面试够用版

function myPromise(constructor){ let self=this;
  self.status="pending" // 定义状态扭转前的初始状态 
  self.value=undefined;// 定义状态为 resolved 的时候的状态 
  self.reason=undefined;// 定义状态为 rejected 的时候的状态 
  function resolve(value){
    // 两个 ==="pending",保障了了状态的扭转是不不可逆的 
    if(self.status==="pending"){
      self.value=value;
      self.status="resolved"; 
    }
  }
  function reject(reason){
     // 两个 ==="pending",保障了了状态的扭转是不不可逆的
     if(self.status==="pending"){
        self.reason=reason;
        self.status="rejected"; 
      }
  }
  // 捕捉结构异样 
  try{constructor(resolve,reject);
  }catch(e){reject(e);
    } 
}
myPromise.prototype.then=function(onFullfilled,onRejected){ 
  let self=this;
  switch(self.status){case "resolved": onFullfilled(self.value); break;
    case "rejected": onRejected(self.reason); break;
    default: 
  }
}

// 测试
var p=new myPromise(function(resolve,reject){resolve(1)}); 
p.then(function(x){console.log(x)})
// 输入 1 

6)大厂专供版

const PENDING = "pending"; 
const FULFILLED = "fulfilled"; 
const REJECTED = "rejected";
const resolvePromise = (promise, x, resolve, reject) => {if (x === promise) {
    // If promise and x refer to the same object, reject promise with a TypeError as the reason.
    reject(new TypeError('循环援用'))
  }
  // if x is an object or function,
  if (x !== null && typeof x === 'object' || typeof x === 'function') {
    // If both resolvePromise and rejectPromise are called, or multiple calls to the same argument are made, the first call takes precedence, and any further calls are ignored.
    let called
    try { // If retrieving the property x.then results in a thrown exception e, reject promise with e as the reason.
      let then = x.then // Let then be x.then
      // If then is a function, call it with x as this
      if (typeof then === 'function') {// If/when resolvePromise is called with a value y, run [[Resolve]](promise, y)
        // If/when rejectPromise is called with a reason r, reject promise with r.
        then.call(x, y => {if (called) return
          called = true
          resolvePromise(promise, y, resolve, reject)
        }, r => {if (called) return
          called = true
          reject(r)
        })
      } else {
        // If then is not a function, fulfill promise with x.
        resolve(x)
      }
    } catch (e) {if (called) return
      called = true
      reject(e)
    }
  } else {
    // If x is not an object or function, fulfill promise with x
    resolve(x)
  }
}
function Promise(excutor) {
  let that = this; // 缓存以后 promise 实例例对象
  that.status = PENDING; // 初始状态
  that.value = undefined; // fulfilled 状态时 返回的信息
  that.reason = undefined; // rejected 状态时 回绝的起因 
  that.onFulfilledCallbacks = []; // 存储 fulfilled 状态对应的 onFulfilled 函数
  that.onRejectedCallbacks = []; // 存储 rejected 状态对应的 onRejected 函数
  function resolve(value) { // value 胜利态时接管的终值
    if(value instanceof Promise) {return value.then(resolve, reject);
    }
    // 实际中要确保 onFulfilled 和 onRejected ⽅办法异步执⾏行行,且应该在 then ⽅办法被调⽤用的那⼀一轮事件循环之后的新执⾏行行栈中执⾏行行。setTimeout(() => {
      // 调⽤用 resolve 回调对应 onFulfilled 函数
      if (that.status === PENDING) {// 只能由 pending 状态 => fulfilled 状态 (防止调⽤用屡次 resolve reject)
        that.status = FULFILLED;
        that.value = value;
        that.onFulfilledCallbacks.forEach(cb => cb(that.value));
      }
    });
  }
  function reject(reason) { // reason 失败态时接管的拒因
    setTimeout(() => {
      // 调⽤用 reject 回调对应 onRejected 函数
      if (that.status === PENDING) {// 只能由 pending 状态 => rejected 状态 (防止调⽤用屡次 resolve reject)
        that.status = REJECTED;
        that.reason = reason;
        that.onRejectedCallbacks.forEach(cb => cb(that.reason));
      }
    });
  }

  // 捕捉在 excutor 执⾏行行器器中抛出的异样
  // new Promise((resolve, reject) => {//     throw new Error('error in excutor')
  // })
  try {excutor(resolve, reject);
  } catch (e) {reject(e);
  }
}
Promise.prototype.then = function(onFulfilled, onRejected) {
  const that = this;
  let newPromise;
  // 解决理参数默认值 保障参数后续可能持续执⾏行行
  onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value;
  onRejected = typeof onRejected === "function" ? onRejected : reason => {throw reason;};
  if (that.status === FULFILLED) { // 胜利态
    return newPromise = new Promise((resolve, reject) => {setTimeout(() => {
        try{let x = onFulfilled(that.value);
          resolvePromise(newPromise, x, resolve, reject); // 新的 promise resolve 上⼀一个 onFulfilled 的返回值
        } catch(e) {reject(e); // 捕捉前⾯面 onFulfilled 中抛出的异样 then(onFulfilled, onRejected);
        }
      });
    })
  }
  if (that.status === REJECTED) { // 失败态
    return newPromise = new Promise((resolve, reject) => {setTimeout(() => {
        try {let x = onRejected(that.reason);
          resolvePromise(newPromise, x, resolve, reject);
        } catch(e) {reject(e);
        }
      });
    });
  }
  if (that.status === PENDING) { // 期待态
// 当异步调⽤用 resolve/rejected 时 将 onFulfilled/onRejected 收集暂存到汇合中
    return newPromise = new Promise((resolve, reject) => {that.onFulfilledCallbacks.push((value) => {
        try {let x = onFulfilled(value);
          resolvePromise(newPromise, x, resolve, reject);
        } catch(e) {reject(e);
        }
      });
      that.onRejectedCallbacks.push((reason) => {
        try {let x = onRejected(reason);
          resolvePromise(newPromise, x, resolve, reject);
        } catch(e) {reject(e);
        }
      });
    });
  }
};

如何防止回流与重绘?

缩小回流与重绘的措施:

  • 操作 DOM 时,尽量在低层级的 DOM 节点进行操作
  • 不要应用 table 布局,一个小的改变可能会使整个 table 进行从新布局
  • 应用 CSS 的表达式
  • 不要频繁操作元素的款式,对于动态页面,能够批改类名,而不是款式。
  • 应用 absolute 或者 fixed,使元素脱离文档流,这样他们发生变化就不会影响其余元素
  • 防止频繁操作 DOM,能够创立一个文档片段documentFragment,在它下面利用所有 DOM 操作,最初再把它增加到文档中
  • 将元素先设置display: none,操作完结后再把它显示进去。因为在 display 属性为 none 的元素上进行的 DOM 操作不会引发回流和重绘。
  • 将 DOM 的多个读操作(或者写操作)放在一起,而不是读写操作穿插着写。这得益于 浏览器的渲染队列机制

浏览器针对页面的回流与重绘,进行了本身的优化——渲染队列

浏览器会将所有的回流、重绘的操作放在一个队列中,当队列中的操作到了肯定的数量或者到了肯定的工夫距离,浏览器就会对队列进行批处理。这样就会让屡次的回流、重绘变成一次回流重绘。

下面,将多个读操作(或者写操作)放在一起,就会等所有的读操作进入队列之后执行,这样,本来应该是触发屡次回流,变成了只触发一次回流。

常见的程度垂直形式有几种?

// 利用相对定位,先将元素的左上角通过 top:50% 和 left:50% 定位到页面的核心,而后再通过 translate 来调整元素的中心点到页面的核心。该办法须要思考浏览器兼容问题。.parent {position: relative;}

.child {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%,-50%);
}
// 利用相对定位,设置四个方向的值都为 0,并将 margin 设置为 auto,因为宽高固定,因而对应方向实现平分,能够实现程度和垂直方向上的居中。该办法实用于盒子有宽高的状况:.parent {position: relative;}

.child {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    margin: auto;
}
// 利用相对定位,先将元素的左上角通过 top:50% 和 left:50% 定位到页面的核心,而后再通过 margin 负值来调整元素的中心点到页面的核心。该办法实用于盒子宽高已知的状况
.parent {position: relative;}

.child {
    position: absolute;
    top: 50%;
    left: 50%;
    margin-top: -50px;     /* 本身 height 的一半 */
    margin-left: -50px;    /* 本身 width 的一半 */
}
// 应用 flex 布局,通过 align-items:center 和 justify-content:center 设置容器的垂直和程度方向上为居中对齐,而后它的子元素也能够实现垂直和程度的居中。该办法要 ** 思考兼容的问题 **,该办法在挪动端用的较多:.parent {
    display: flex;
    justify-content:center;
    align-items:center;
}
// 另外,如果父元素设置了 flex 布局,只须要给子元素加上 `margin:auto;` 就能够实现垂直居中布局
.parent{display:flex;}
.child{margin: auto;}
复制代码

说一下 HTTP 和 HTTPS 协定的区别?

1、HTTPS 协定须要 CA 证书, 费用较高; 而 HTTP 协定不须要
2、HTTP 协定是超文本传输协定, 信息是明文传输的,HTTPS 则是具备安全性的 SSL 加密传输协定;
3、应用不同的连贯形式, 端口也不同,HTTP 协定端口是 80,HTTPS 协定端口是 443;
4、HTTP 协定连贯很简略, 是无状态的;HTTPS 协定是具备 SSL 和 HTTP 协定构建的可进行加密传输、身份认证的网络协议, 比 HTTP 更加平安
复制代码

对节流与防抖的了解

  • 函数防抖是指在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则从新计时。这能够应用在一些点击申请的事件上,防止因为用户的屡次点击向后端发送屡次申请。
  • 函数节流是指规定一个单位工夫,在这个单位工夫内,只能有一次触发事件的回调函数执行,如果在同一个单位工夫内某事件被触发屡次,只有一次能失效。节流能够应用在 scroll 函数的事件监听上,通过事件节流来升高事件调用的频率。

防抖函数的利用场景:

  • 按钮提交场景:防⽌屡次提交按钮,只执⾏最初提交的⼀次
  • 服务端验证场景:表单验证须要服务端配合,只执⾏⼀段间断的输⼊事件的最初⼀次,还有搜寻联想词性能相似⽣存环境请⽤ lodash.debounce

节流函数的适⽤场景:

  • 拖拽场景:固定工夫内只执⾏⼀次,防⽌超⾼频次触发地位变动
  • 缩放场景:监控浏览器 resize
  • 动画场景:防止短时间内屡次触发动画引起性能问题

点击刷新按钮或者按 F5、按 Ctrl+F5(强制刷新)、地址栏回车有什么区别?

  • 点击刷新按钮或者按 F5: 浏览器间接对本地的缓存文件过期,然而会带上 If-Modifed-Since,If-None-Match,这就意味着服务器会对文件查看新鲜度,返回后果可能是 304,也有可能是 200。
  • 用户按 Ctrl+F5(强制刷新): 浏览器不仅会对本地文件过期,而且不会带上 If-Modifed-Since,If-None-Match,相当于之前素来没有申请过,返回后果是 200。
  • 地址栏回车:浏览器发动申请,依照失常流程,本地查看是否过期,而后服务器查看新鲜度,最初返回内容。

代码输入后果

Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .then(console.log)
复制代码

输入后果如下:

1
复制代码

看到这个题目,好多的 then,实际上只须要记住一个准则:.then.catch 的参数冀望是函数,传入非函数则会产生 值透传

第一个 then 和第二个 then 中传入的都不是函数,一个是数字,一个是对象,因而产生了透传,将resolve(1) 的值间接传到最初一个 then 里,间接打印出 1。

退出移动版