写点什么

总结了一下前端高频面试题的答案

作者:loveX001
  • 2022 年 9 月 26 日
    浙江
  • 本文字数:10193 字

    阅读完需:约 33 分钟

call apply bind

题目描述:手写 call apply bind 实现


实现代码如下:


Function.prototype.myCall = function (context, ...args) {  if (!context || context === null) {    context = window;  }  // 创造唯一的key值  作为我们构造的context内部方法名  let fn = Symbol();  context[fn] = this; //this指向调用call的函数  // 执行函数并返回结果 相当于把自身作为传入的context的方法进行调用了  return context[fn](...args);};
// apply原理一致 只是第二个参数是传入的数组Function.prototype.myApply = function (context, args) { if (!context || context === null) { context = window; } // 创造唯一的key值 作为我们构造的context内部方法名 let fn = Symbol(); context[fn] = this; // 执行函数并返回结果 return context[fn](...args);};
//bind实现要复杂一点 因为他考虑的情况比较多 还要涉及到参数合并(类似函数柯里化)
Function.prototype.myBind = function (context, ...args) { if (!context || context === null) { context = window; } // 创造唯一的key值 作为我们构造的context内部方法名 let fn = Symbol(); context[fn] = this; let _this = this; // bind情况要复杂一点 const result = function (...innerArgs) { // 第一种情况 :若是将 bind 绑定之后的函数当作构造函数,通过 new 操作符使用,则不绑定传入的 this,而是将 this 指向实例化出来的对象 // 此时由于new操作符作用 this指向result实例对象 而result又继承自传入的_this 根据原型链知识可得出以下结论 // this.__proto__ === result.prototype //this instanceof result =>true // this.__proto__.__proto__ === result.prototype.__proto__ === _this.prototype; //this instanceof _this =>true if (this instanceof _this === true) { // 此时this指向指向result的实例 这时候不需要改变this指向 this[fn] = _this; this[fn](...[...args, ...innerArgs]); //这里使用es6的方法让bind支持参数合并 } else { // 如果只是作为普通函数调用 那就很简单了 直接改变this指向为传入的context context[fn](...[...args, ...innerArgs]); } }; // 如果绑定的是构造函数 那么需要继承构造函数原型属性和方法 // 实现继承的方式: 使用Object.create result.prototype = Object.create(this.prototype); return result;};
//用法如下
// function Person(name, age) {// console.log(name); //'我是参数传进来的name'// console.log(age); //'我是参数传进来的age'// console.log(this); //构造函数this指向实例对象// }// // 构造函数原型的方法// Person.prototype.say = function() {// console.log(123);// }// let obj = {// objName: '我是obj传进来的name',// objAge: '我是obj传进来的age'// }// // 普通函数// function normalFun(name, age) {// console.log(name); //'我是参数传进来的name'// console.log(age); //'我是参数传进来的age'// console.log(this); //普通函数this指向绑定bind的第一个参数 也就是例子中的obj// console.log(this.objName); //'我是obj传进来的name'// console.log(this.objAge); //'我是obj传进来的age'// }
// 先测试作为构造函数调用// let bindFun = Person.myBind(obj, '我是参数传进来的name')// let a = new bindFun('我是参数传进来的age')// a.say() //123
// 再测试作为普通函数调用// let bindFun = normalFun.myBind(obj, '我是参数传进来的name')// bindFun('我是参数传进来的age')
复制代码

介绍一下 HTTPS 和 HTTP 区别

HTTPS 要比 HTTPS 多了 secure 安全性这个概念,实际上, HTTPS 并不是一个新的应用层协议,它其实就是 HTTP + TLS/SSL 协议组合而成,而安全性的保证正是 SSL/TLS 所做的工作。


SSL


安全套接层(Secure Sockets Layer)


TLS


(传输层安全,Transport Layer Security)


现在主流的版本是 TLS/1.2, 之前的 TLS1.0、TLS1.1 都被认为是不安全的,在不久的将来会被完全淘汰。


HTTPS 就是身披了一层 SSL 的 HTTP



那么区别有哪些呢👇


  • HTTP 是明文传输协议,HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 HTTP 协议安全。

  • HTTPS 比 HTTP 更加安全,对搜索引擎更友好,利于 SEO,谷歌、百度优先索引 HTTPS 网页。

  • HTTPS 标准端口 443,HTTP 标准端口 80。

  • HTTPS 需要用到 SSL 证书,而 HTTP 不用。


我觉得记住以下两点 HTTPS 主要作用就行👇


  1. 对数据进行加密,并建立一个信息安全通道,来保证传输过程中的数据安全;

  2. 对网站服务器进行真实身份认证。


HTTPS 的缺点


  • 证书费用以及更新维护。

  • HTTPS 降低一定用户访问速度(实际上优化好就不是缺点了)。

  • HTTPS 消耗 CPU 资源,需要增加大量机器。


前端进阶面试题详细解答

什么是 DOM 和 BOM?

  • DOM 指的是文档对象模型,它指的是把文档当做一个对象,这个对象主要定义了处理网页内容的方法和接口。

  • BOM 指的是浏览器对象模型,它指的是把浏览器当做一个对象来对待,这个对象主要定义了与浏览器进行交互的法和接口。BOM 的核心是 window,而 window 对象具有双重角色,它既是通过 js 访问浏览器窗口的一个接口,又是一个 Global(全局)对象。这意味着在网页中定义的任何对象,变量和函数,都作为全局对象的一个属性或者方法存在。window 对象含有 location 对象、navigator 对象、screen 对象等子对象,并且 DOM 的最根本的对象 document 对象也是 BOM 的 window 对象的子对象。

intanceof 操作符的实现原理及实现

instanceof 运算符用于判断构造函数的 prototype 属性是否出现在对象的原型链中的任何位置。


function myInstanceof(left, right) {  // 获取对象的原型  let proto = Object.getPrototypeOf(left)  // 获取构造函数的 prototype 对象  let prototype = right.prototype; 
// 判断构造函数的 prototype 对象是否在对象的原型链上 while (true) { if (!proto) return false; if (proto === prototype) return true; // 如果没有找到,就继续从其原型上找,Object.getPrototypeOf方法用来获取指定对象的原型 proto = Object.getPrototypeOf(proto); }}
复制代码

display 的属性值及其作用

浏览器乱码的原因是什么?如何解决?

产生乱码的原因:


  • 网页源代码是gbk的编码,而内容中的中文字是utf-8编码的,这样浏览器打开即会出现html乱码,反之也会出现乱码;

  • html网页编码是gbk,而程序从数据库中调出呈现是utf-8编码的内容也会造成编码乱码;

  • 浏览器不能自动检测网页编码,造成网页乱码。


解决办法:


  • 使用软件编辑 HTML 网页内容;

  • 如果网页设置编码是gbk,而数据库储存数据编码格式是UTF-8,此时需要程序查询数据库数据显示数据前进程序转码;

  • 如果浏览器浏览时候出现网页乱码,在浏览器中找到转换编码的菜单进行转换。

li 与 li 之间有看不见的空白间隔是什么原因引起的?如何解决?

浏览器会把 inline 内联元素间的空白字符(空格、换行、Tab 等)渲染成一个空格。为了美观,通常是一个<li>放在一行,这导致<li>换行后产生换行字符,它变成一个空格,占用了一个字符的宽度。


解决办法:


(1)为<li>设置 float:left。不足:有些容器是不能设置浮动,如左右切换的焦点图等。


(2)将所有<li>写在同一行。不足:代码不美观。


(3)将<ul>内的字符尺寸直接设为 0,即 font-size:0。不足:<ul>中的其他字符尺寸也被设为 0,需要额外重新设定其他字符尺寸,且在 Safari 浏览器依然会出现空白间隔。


(4)消除<ul>的字符间隔 letter-spacing:-8px,不足:这也设置了<li>内的字符间隔,因此需要将<li>内的字符间隔设为默认 letter-spacing:normal。

display:none 与 visibility:hidden 的区别

这两个属性都是让元素隐藏,不可见。两者区别如下:


(1)在渲染树中


  • display:none会让元素完全从渲染树中消失,渲染时不会占据任何空间;

  • visibility:hidden不会让元素从渲染树中消失,渲染的元素还会占据相应的空间,只是内容不可见。


(2)是否是继承属性


  • display:none是非继承属性,子孙节点会随着父节点从渲染树消失,通过修改子孙节点的属性也无法显示;

  • visibility:hidden是继承属性,子孙节点消失是由于继承了hidden,通过设置visibility:visible可以让子孙节点显示;(3)修改常规文档流中元素的 display 通常会造成文档的重排,但是修改visibility属性只会造成本元素的重绘;


(4)如果使用读屏器,设置为display:none的内容不会被读取,设置为visibility:hidden的内容会被读取。

display:inline-block 什么时候会显示间隙?

  • 有空格时会有间隙,可以删除空格解决;

  • margin正值时,可以让margin使用负值解决;

  • 使用font-size时,可通过设置font-size:0letter-spacingword-spacing解决;

TCP/IP 五层协议

TCP/IP五层协议和OSI的七层协议对应关系如下:


  • 应用层 (application layer):直接为应用进程提供服务。应用层协议定义的是应用进程间通讯和交互的规则,不同的应用有着不同的应用层协议,如 HTTP 协议(万维网服务)、FTP 协议(文件传输)、SMTP 协议(电子邮件)、DNS(域名查询)等。

  • 传输层 (transport layer):有时也译为运输层,它负责为两台主机中的进程提供通信服务。该层主要有以下两种协议:

  • 传输控制协议 (Transmission Control Protocol,TCP):提供面向连接的、可靠的数据传输服务,数据传输的基本单位是报文段(segment);

  • 用户数据报协议 (User Datagram Protocol,UDP):提供无连接的、尽最大努力的数据传输服务,但不保证数据传输的可靠性,数据传输的基本单位是用户数据报。

  • 网络层 (internet layer):有时也译为网际层,它负责为两台主机提供通信服务,并通过选择合适的路由将数据传递到目标主机。

  • 数据链路层 (data link layer):负责将网络层交下来的 IP 数据报封装成帧,并在链路的两个相邻节点间传送帧,每一帧都包含数据和必要的控制信息(如同步信息、地址信息、差错控制等)。

  • 物理层 (physical Layer):确保数据可以在各种物理媒介上进行传输,为数据的传输提供可靠的环境。


从上图中可以看出,TCP/IP模型比OSI模型更加简洁,它把应用层/表示层/会话层全部整合为了应用层


在每一层都工作着不同的设备,比如我们常用的交换机就工作在数据链路层的,一般的路由器是工作在网络层的。 在每一层实现的协议也各不同,即每一层的服务也不同,下图列出了每层主要的传输协议:


同样,TCP/IP五层协议的通信方式也是对等通信:

JavaScript 有哪些数据类型,它们的区别?

JavaScript 共有八种数据类型,分别是 Undefined、Null、Boolean、Number、String、Object、Symbol、BigInt。


其中 Symbol 和 BigInt 是 ES6 中新增的数据类型:


  • Symbol 代表创建后独一无二且不可变的数据类型,它主要是为了解决可能出现的全局变量冲突的问题。

  • BigInt 是一种数字类型的数据,它可以表示任意精度格式的整数,使用 BigInt 可以安全地存储和操作大整数,即使这个数已经超出了 Number 能够表示的安全整数范围。


这些数据可以分为原始数据类型和引用数据类型:


  • 栈:原始数据类型(Undefined、Null、Boolean、Number、String)

  • 堆:引用数据类型(对象、数组和函数)


两种类型的区别在于存储位置的不同:


  • 原始数据类型直接存储在栈(stack)中的简单数据段,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储;

  • 引用数据类型存储在堆(heap)中的对象,占据空间大、大小不固定。如果存储在栈中,将会影响程序运行的性能;引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。


堆和栈的概念存在于数据结构和操作系统内存中,在数据结构中:


  • 在数据结构中,栈中数据的存取方式为先进后出。

  • 堆是一个优先队列,是按优先级来进行排序的,优先级可以按照大小来规定。


在操作系统中,内存被分为栈区和堆区:


  • 栈区内存由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

  • 堆区内存一般由开发着分配释放,若开发者不释放,程序结束时可能由垃圾回收机制回收。

箭头函数与普通函数的区别

(1)箭头函数比普通函数更加简洁


  • 如果没有参数,就直接写一个空括号即可

  • 如果只有一个参数,可以省去参数的括号

  • 如果有多个参数,用逗号分割

  • 如果函数体的返回值只有一句,可以省略大括号

  • 如果函数体不需要返回值,且只有一句话,可以给这个语句前面加一个 void 关键字。最常见的就是调用一个函数:


let fn = () => void doesNotReturn();
复制代码


(2)箭头函数没有自己的 this


箭头函数不会创建自己的 this, 所以它没有自己的 this,它只会在自己作用域的上一层继承 this。所以箭头函数中 this 的指向在它在定义时已经确定了,之后不会改变。


(3)箭头函数继承来的 this 指向永远不会改变


var id = 'GLOBAL';var obj = {  id: 'OBJ',  a: function(){    console.log(this.id);  },  b: () => {    console.log(this.id);  }};obj.a();    // 'OBJ'obj.b();    // 'GLOBAL'new obj.a()  // undefinednew obj.b()  // Uncaught TypeError: obj.b is not a constructor
复制代码


对象 obj 的方法 b 是使用箭头函数定义的,这个函数中的 this 就永远指向它定义时所处的全局执行环境中的 this,即便这个函数是作为对象 obj 的方法调用,this 依旧指向 Window 对象。需要注意,定义对象的大括号{}是无法形成一个单独的执行环境的,它依旧是处于全局执行环境中。


(4)call()、apply()、bind()等方法不能改变箭头函数中 this 的指向


var id = 'Global';let fun1 = () => {    console.log(this.id)};fun1();                     // 'Global'fun1.call({id: 'Obj'});     // 'Global'fun1.apply({id: 'Obj'});    // 'Global'fun1.bind({id: 'Obj'})();   // 'Global'
复制代码


(5)箭头函数不能作为构造函数使用


构造函数在 new 的步骤在上面已经说过了,实际上第二步就是将函数中的 this 指向该对象。 但是由于箭头函数时没有自己的 this 的,且 this 指向外层的执行环境,且不能改变指向,所以不能当做构造函数使用。


(6)箭头函数没有自己的 arguments


箭头函数没有自己的 arguments 对象。在箭头函数中访问 arguments 实际上获得的是它外层函数的 arguments 值。


(7)箭头函数没有 prototype


(8)箭头函数不能用作 Generator 函数,不能使用 yeild 关键字

什么是 JavaScript 中的包装类型?

在 JavaScript 中,基本类型是没有属性和方法的,但是为了便于操作基本类型的值,在调用基本类型的属性或方法时 JavaScript 会在后台隐式地将基本类型的值转换为对象,如:


const a = "abc";a.length; // 3a.toUpperCase(); // "ABC"
复制代码


在访问'abc'.length时,JavaScript 将'abc'在后台转换成String('abc'),然后再访问其length属性。


JavaScript 也可以使用Object函数显式地将基本类型转换为包装类型:


var a = 'abc'Object(a) // String {"abc"}
复制代码


也可以使用valueOf方法将包装类型倒转成基本类型:


var a = 'abc'var b = Object(a)var c = b.valueOf() // 'abc'
复制代码


看看如下代码会打印出什么:


var a = new Boolean( false );if (!a) {    console.log( "Oops" ); // never runs}
复制代码


答案是什么都不会打印,因为虽然包裹的基本类型是false,但是false被包裹成包装类型后就成了对象,所以其非值为false,所以循环体中的内容不会运行。

左右居中方案

  • 行内元素: text-align: center

  • 定宽块状元素: 左右 margin 值为 auto

  • 不定宽块状元素: table布局,position + transform


/* 方案1 */.wrap {  text-align: center}.center {  display: inline;  /* or */  /* display: inline-block; */}/* 方案2 */.center {  width: 100px;  margin: 0 auto;}/* 方案2 */.wrap {  position: relative;}.center {  position: absulote;  left: 50%;  transform: translateX(-50%);}
复制代码

DNS 如何工作的

DNS 的作用就是通过域名查询到具体的 IP。DNS 协议提供的是一种主机名到 IP 地址的转换服务,就是我们常说的域名系统。是应用层协议,通常该协议运行在 UDP 协议之上,使用的是 53 端口号。


因为 IP 存在数字和英文的组合(IPv6),很不利于人类记忆,所以就出现了域名。你可以把域名看成是某个 IP 的别名,DNS 就是去查询这个别名的真正名称是什么。


当你在浏览器中想访问 www.google.com 时,会通过进行以下操作:


  • 本地客户端向服务器发起请求查询 IP 地址

  • 查看浏览器有没有该域名的 IP 缓存

  • 查看操作系统有没有该域名的 IP 缓存

  • 查看 Host 文件有没有该域名的解析配置

  • 如果这时候还没得话,会通过直接去 DNS 根服务器查询,这一步查询会找出负责 com 这个一级域名的服务器

  • 然后去该服务器查询 google.com 这个二级域名

  • 接下来查询 www.google.com 这个三级域名的地址

  • 返回给 DNS 客户端并缓存起来



我们通过一张图来看看它的查询过程吧 👇



这张图很生动的展示了 DNS 在本地 DNS 服务器是如何查询的,一般向本地 DNS 服务器发送请求是递归查询的


本地 DNS 服务器向其他域名服务器请求的过程是迭代查询的过程👇



递归查询和迭代查询


  • 递归查询指的是查询请求发出后,域名服务器代为向下一级域名服务器发出请求,最后向用户返回查询的最终结果。使用递归 查询,用户只需要发出一次查询请求。

  • 迭代查询指的是查询请求后,域名服务器返回单次查询的结果。下一级的查询由用户自己请求。使用迭代查询,用户需要发出 多次的查询请求。


所以一般而言, 本地服务器查询是递归查询 ,而本地 DNS 服务器向其他域名服务器请求的过程是迭代查询的过程


DNS 缓存


缓存也很好理解,在一个请求中,当某个 DNS 服务器收到一个 DNS 回答后,它能够回答中的信息缓存在本地存储器中。返回的资源记录中的 TTL 代表了该条记录的缓存的时间。


DNS 实现负载平衡


它是如何实现负载均衡的呢?首先我们得清楚 DNS 是可以用于在冗余的服务器上实现负载平衡。


原因: 这是因为一般的大型网站使用多台服务器提供服务,因此一个域名可能会对应 多个服务器地址。


举个例子来说👇


  • 当用户发起网站域名的 DNS 请求的时候,DNS 服务器返回这个域名所对应的服务器 IP 地址的集合

  • 在每个回答中,会循环这些 IP 地址的顺序,用户一般会选择排在前面的地址发送请求。

  • 以此将用户的请求均衡的分配到各个不同的服务器上,这样来实现负载均衡。


DNS 为什么使用 UDP 协议作为传输层协议?


DNS 使用 UDP 协议作为传输层协议的主要原因是为了避免使用 TCP 协议时造成的连接时延


  • 为了得到一个域名的 IP 地址,往往会向多个域名服务器查询,如果使用 TCP 协议,那么每次请求都会存在连接时延,这样使 DNS 服务变得很慢。

  • 大多数的地址查询请求,都是浏览器请求页面时发出的,这样会造成网页的等待时间过长。


总结


  • DNS 域名系统,是应用层协议,运行 UDP 协议之上,使用端口 43。

  • 查询过程,本地查询是递归查询,依次通过浏览器缓存 —>> 本地 hosts 文件 —>> 本地 DNS 解析器 —>>本地 DNS 服务器 —>> 其他域名服务器请求。 接下来的过程就是迭代过程。

  • 递归查询一般而言,发送一次请求就够,迭代过程需要用户发送多次请求。

垃圾回收

  • 对于在 JavaScript 中的字符串,对象,数组是没有固定大小的,只有当对他们进行动态分配存储时,解释器就会分配内存来存储这些数据,当 JavaScript 的解释器消耗完系统中所有可用的内存时,就会造成系统崩溃。

  • 内存泄漏,在某些情况下,不再使用到的变量所占用内存没有及时释放,导致程序运行中,内存越占越大,极端情况下可以导致系统崩溃,服务器宕机。

  • JavaScript 有自己的一套垃圾回收机制,JavaScript 的解释器可以检测到什么时候程序不再使用这个对象了(数据),就会把它所占用的内存释放掉。

  • 针对 JavaScript 的来及回收机制有以下两种方法(常用):标记清除,引用计数

  • 标记清除


v8 的垃圾回收机制基于分代回收机制,这个机制又基于世代假说,这个假说有两个特点,一是新生的对象容易早死,另一个是不死的对象会活得更久。基于这个假说,v8 引擎将内存分为了新生代和老生代。


  • 新创建的对象或者只经历过一次的垃圾回收的对象被称为新生代。经历过多次垃圾回收的对象被称为老生代。

  • 新生代被分为 From 和 To 两个空间,To 一般是闲置的。当 From 空间满了的时候会执行 Scavenge 算法进行垃圾回收。当我们执行垃圾回收算法的时候应用逻辑将会停止,等垃圾回收结束后再继续执行。


这个算法分为三步:


  • 首先检查 From 空间的存活对象,如果对象存活则判断对象是否满足晋升到老生代的条件,如果满足条件则晋升到老生代。如果不满足条件则移动 To 空间。

  • 如果对象不存活,则释放对象的空间。

  • 最后将 From 空间和 To 空间角色进行交换。


新生代对象晋升到老生代有两个条件:


  • 第一个是判断是对象否已经经过一次 Scavenge 回收。若经历过,则将对象从 From 空间复制到老生代中;若没有经历,则复制到 To 空间。

  • 第二个是 To 空间的内存使用占比是否超过限制。当对象从 From 空间复制到 To 空间时,若 To 空间使用超过 25%,则对象直接晋升到老生代中。设置 25% 的原因主要是因为算法结束后,两个空间结束后会交换位置,如果 To 空间的内存太小,会影响后续的内存分配。


老生代采用了标记清除法和标记压缩法。标记清除法首先会对内存中存活的对象进行标记,标记结束后清除掉那些没有标记的对象。由于标记清除后会造成很多的内存碎片,不便于后面的内存分配。所以了解决内存碎片的问题引入了标记压缩法。


由于在进行垃圾回收的时候会暂停应用的逻辑,对于新生代方法由于内存小,每次停顿的时间不会太长,但对于老生代来说每次垃圾回收的时间长,停顿会造成很大的影响。 为了解决这个问题 V8 引入了增量标记的方法,将一次停顿进行的过程分为了多步,每次执行完一小步就让运行逻辑执行一会,就这样交替运行

如何获得对象非原型链上的属性?

使用后hasOwnProperty()方法来判断属性是否属于原型链的属性:


function iterate(obj){   var res=[];   for(var key in obj){        if(obj.hasOwnProperty(key))           res.push(key+': '+obj[key]);   }   return res;} 
复制代码

VDOM:三个 part

  • 虚拟节点类,将真实 DOM节点用 js 对象的形式进行展示,并提供 render 方法,将虚拟节点渲染成真实 DOM

  • 节点 diff 比较:对虚拟节点进行 js 层面的计算,并将不同的操作都记录到 patch 对象

  • re-render:解析 patch 对象,进行 re-render


补充 1��VDOM 的必要性?


  • 创建真实 DOM 的代价高 :真实的 DOM 节点 node 实现的属性很多,而 vnode 仅仅实现一些必要的属性,相比起来,创建一个 vnode 的成本比较低。

  • 触发多次浏览器重绘及回流 :使用 vnode ,相当于加了一个缓冲,让一次数据变动所带来的所有 node 变化,先在 vnode 中进行修改,然后 diff 之后对所有产生差异的节点集中一次对 DOM tree 进行修改,以减少浏览器的重绘及回流。


补充 2:vue 为什么采用 vdom?


引入 Virtual DOM 在性能方面的考量仅仅是一方面。


  • 性能受场景的影响是非常大的,不同的场景可能造成不同实现方案之间成倍的性能差距,所以依赖细粒度绑定及 Virtual DOM 哪个的性能更好还真不是一个容易下定论的问题。

  • Vue 之所以引入了 Virtual DOM,更重要的原因是为了解耦 HTML依赖,这带来两个非常重要的好处是:


  • 不再依赖 HTML 解析器进行模版解析,可以进行更多的 AOT 工作提高运行时效率:通过模版 AOT 编译,Vue 的运行时体积可以进一步压缩,运行时效率可以进一步提升;

  • 可以渲染到 DOM 以外的平台,实现 SSR、同构渲染这些高级特性,Weex等框架应用的就是这一特性。


综上,Virtual DOM 在性能上的收益并不是最主要的,更重要的是它使得 Vue 具备了现代框架应有的高级特性。

HTTPS 握手过程

  • 第一步,客户端给出协议版本号、一个客户端生成的随机数(Client random),以及客户端支持的加密方法

  • 第二步,服务端确认双方使用的加密方法,并给出数字证书、以及一个服务器生成的随机数

  • 第三步,客户端确认数字证书有效,然后生成一个新的随机数(Premaster secret),并使用数字证书中的公钥,加密这个随机数,发给服务端

  • 第四步,服务端使用自己的私钥,获取客户端发来的随机数(即 Premaster secret)。

  • 第五步,客户端和服务端根据约定的加密方法,使用前面的三个随机数,生成"对话密钥"(session key),用来加密接下来的整个对话过程


总结


  • 客户端发起 HTTPS 请求,服务端返回证书,客户端对证书进行验证,验证通过后本地生成用于构造对称加密算法的随机数

  • 通过证书中的公钥对随机数进行加密传输到服务端(随机对称密钥),服务端接收后通过私钥解密得到随机对称密钥,之后的数据交互通过对称加密算法进行加解密。(既有对称加密,也有非对称加密)

vue 和 react 技术选型

相同点:


  1. 数据驱动页面,提供响应式的试图组件

  2. 都有 virtual DOM,组件化的开发,通过 props 参数进行父子之间组件传递数据,都实现了 webComponents 规范

  3. 数据流动单向,都支持服务器的渲染 SSR

  4. 都有支持 native 的方法,react 有 React native, vue 有 wexx


不同点:


  1. 数据绑定:Vue 实现了双向的数据绑定,react 数据流动是单向的

  2. 数据渲染:大规模的数据渲染,react 更快

  3. 使用场景:React 配合 Redux 架构适合大规模多人协作复杂项目,Vue 适合小快的项目

  4. 开发风格:react 推荐做法 jsx + inline style 把 html 和 css 都写在 js 了


vue 是采用 webpack +vue-loader 单文件组件格式,html, js, css 同一个文件

用户头像

loveX001

关注

还未添加个人签名 2022.09.01 加入

还未添加个人简介

评论

发布
暂无评论
总结了一下前端高频面试题的答案_JavaScript_loveX001_InfoQ写作社区