写点什么

前端打工人的面试总结

作者:loveX001
  • 2022-11-08
    浙江
  • 本文字数:9479 字

    阅读完需:约 31 分钟

v-model 语法糖是怎么实现的

<!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>    <!-- v-model 只是语法糖而已 -->    <!-- v-model 在内部为不同的输入元素使用不同的property并抛出不同的事件 -->    <!-- text和textarea 元素使用value property 和 input事件 -->    <!-- checkbox 和radio使用checked  property 和 change事件-->    <!-- select 字段将value 作为prop 并将change 作为事件 -->    <!-- 注意:对于需要使用输入法(如中文、日文、韩文等)的语言,你将会发现v-model不会再输入法    组合文字过程中得到更新 -->    <!-- 再普通标签上 -->    <input v-model="sth" />  //这一行等于下一行    <input v-bind:value="sth" v-on:input="sth = $event.target.value" />    <!-- 再组件上 -->    <currency-input v-model="price"></currentcy-input>        <!--上行代码是下行的语法糖         <currency-input :value="price" @input="price = arguments[0]"></currency-input>        -->         <!-- 子组件定义 -->        Vue.component('currency-input', {         template: `          <span>           <input            ref="input"            :value="value"            @input="$emit('input', $event.target.value)"           >          </span>         `,         props: ['value'],        })   </body></html>
复制代码

数字证书是什么?

现在的方法也不一定是安全的,因为没有办法确定得到的公钥就一定是安全的公钥。可能存在一个中间人,截取了对方发给我们的公钥,然后将他自己的公钥发送给我们,当我们使用他的公钥加密后发送的信息,就可以被他用自己的私钥解密。然后他伪装成我们以同样的方法向对方发送信息,这样我们的信息就被窃取了,然而自己还不知道。为了解决这样的问题,可以使用数字证书。


首先使用一种 Hash 算法来对公钥和其他信息进行加密,生成一个信息摘要,然后让有公信力的认证中心(简称 CA )用它的私钥对消息摘要加密,形成签名。最后将原始的信息和签名合在一起,称为数字证书。当接收方收到数字证书的时候,先根据原始信息使用同样的 Hash 算法生成一个摘要,然后使用公证处的公钥来对数字证书中的摘要进行解密,最后将解密的摘要和生成的摘要进行对比,就能发现得到的信息是否被更改了。


这个方法最要的是认证中心的可靠性,一般浏览器里会内置一些顶层的认证中心的证书,相当于我们自动信任了他们,只有这样才能保证数据的安全。

其他值到布尔类型的值的转换规则?

以下这些是假值:• undefined• null• false• +0、-0 和 NaN• ""


假值的布尔强制类型转换结果为 false。从逻辑上说,假值列表以外的都应该是真值。

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); }}
复制代码


参考:前端进阶面试题详细解答

当在浏览器中输入 Google.com 并且按下回车之后发生了什么?

(1)解析 URL: 首先会对 URL 进行解析,分析所需要使用的传输协议和请求的资源的路径。如果输入的 URL 中的协议或者主机名不合法,将会把地址栏中输入的内容传递给搜索引擎。如果没有问题,浏览器会检查 URL 中是否出现了非法字符,如果存在非法字符,则对非法字符进行转义后再进行下一过程。


(2)缓存判断: 浏览器会判断所请求的资源是否在缓存里,如果请求的资源在缓存里并且没有失效,那么就直接使用,否则向服务器发起新的请求。


(3)DNS 解析: 下一步首先需要获取的是输入的 URL 中的域名的 IP 地址,首先会判断本地是否有该域名的 IP 地址的缓存,如果有则使用,如果没有则向本地 DNS 服务器发起请求。本地 DNS 服务器也会先检查是否存在缓存,如果没有就会先向根域名服务器发起请求,获得负责的顶级域名服务器的地址后,再向顶级域名服务器请求,然后获得负责的权威域名服务器的地址后,再向权威域名服务器发起请求,最终获得域名的 IP 地址后,本地 DNS 服务器再将这个 IP 地址返回给请求的用户。用户向本地 DNS 服务器发起请求属于递归请求,本地 DNS 服务器向各级域名服务器发起请求属于迭代请求。


(4)获取 MAC 地址: 当浏览器得到 IP 地址后,数据传输还需要知道目的主机 MAC 地址,因为应用层下发数据给传输层,TCP 协议会指定源端口号和目的端口号,然后下发给网络层。网络层会将本机地址作为源地址,获取的 IP 地址作为目的地址。然后将下发给数据链路层,数据链路层的发送需要加入通信双方的 MAC 地址,本机的 MAC 地址作为源 MAC 地址,目的 MAC 地址需要分情况处理。通过将 IP 地址与本机的子网掩码相与,可以判断是否与请求主机在同一个子网里,如果在同一个子网里,可以使用 APR 协议获取到目的主机的 MAC 地址,如果不在一个子网里,那么请求应该转发给网关,由它代为转发,此时同样可以通过 ARP 协议来获取网关的 MAC 地址,此时目的主机的 MAC 地址应该为网关的地址。


(5)TCP 三次握手: 下面是 TCP 建立连接的三次握手的过程,首先客户端向服务器发送一个 SYN 连接请求报文段和一个随机序号,服务端接收到请求后向服务器端发送一个 SYN ACK 报文段,确认连接请求,并且也向客户端发送一个随机序号。客户端接收服务器的确认应答后,进入连接建立的状态,同时向服务器也发送一个 ACK 确认报文段,服务器端接收到确认后,也进入连接建立状态,此时双方的连接就建立起来了。


(6)HTTPS 握手: 如果使用的是 HTTPS 协议,在通信前还存在 TLS 的一个四次握手的过程。首先由客户端向服务器端发送使用的协议的版本号、一个随机数和可以使用的加密方法。服务器端收到后,确认加密的方法,也向客户端发送一个随机数和自己的数字证书。客户端收到后,首先检查数字证书是否有效,如果有效,则再生成一个随机数,并使用证书中的公钥对随机数加密,然后发送给服务器端,并且还会提供一个前面所有内容的 hash 值供服务器端检验。服务器端接收后,使用自己的私钥对数据解密,同时向客户端发送一个前面所有内容的 hash 值供客户端检验。这个时候双方都有了三个随机数,按照之前所约定的加密方法,使用这三个随机数生成一把秘钥,以后双方通信前,就使用这个秘钥对数据进行加密后再传输。


(7)返回数据: 当页面请求发送到服务器端后,服务器端会返回一个 html 文件作为响应,浏览器接收到响应后,开始对 html 文件进行解析,开始页面的渲染过程。


(8)页面渲染: 浏览器首先会根据 html 文件构建 DOM 树,根据解析到的 css 文件构建 CSSOM 树,如果遇到 script 标签,则判端是否含有 defer 或者 async 属性,要不然 script 的加载和执行会造成页面的渲染的阻塞。当 DOM 树和 CSSOM 树建立好后,根据它们来构建渲染树。渲染树构建好后,会根据渲染树来进行布局。布局完成后,最后使用浏览器的 UI 接口对页面进行绘制。这个时候整个页面就显示出来了。


(9)TCP 四次挥手: 最后一步是 TCP 断开连接的四次挥手过程。若客户端认为数据发送完成,则它需要向服务端发送连接释放请求。服务端收到连接释放请求后,会告诉应用层要释放 TCP 链接。然后会发送 ACK 包,并进入 CLOSE_WAIT 状态,此时表明客户端到服务端的连接已经释放,不再接收客户端发的数据了。但是因为 TCP 连接是双向的,所以服务端仍旧可以发送数据给客户端。服务端如果此时还有没发完的数据会继续发送,完毕后会向客户端发送连接释放请求,然后服务端便进入 LAST-ACK 状态。客户端收到释放请求后,向服务端发送确认应答,此时客户端进入 TIME-WAIT 状态。该状态会持续 2MSL(最大段生存期,指报文段在网络中生存的时间,超时会被抛弃) 时间,若该时间段内没有服务端的重发请求的话,就进入 CLOSED 状态。当服务端收到确认应答后,也便进入 CLOSED 状态。

对 rest 参数的理解

扩展运算符被用在函数形参上时,它还可以把一个分离的参数序列整合成一个数组


function mutiple(...args) {  let result = 1;  for (var val of args) {    result *= val;  }  return result;}mutiple(1, 2, 3, 4) // 24
复制代码


这里,传入 mutiple 的是四个分离的参数,但是如果在 mutiple 函数里尝试输出 args 的值,会发现它是一个数组:


function mutiple(...args) {  console.log(args)}mutiple(1, 2, 3, 4) // [1, 2, 3, 4]
复制代码


这就是 … rest 运算符的又一层威力了,它可以把函数的多个入参收敛进一个数组里。这一点经常用于获取函数的多余参数,或者像上面这样处理函数参数个数不确定的情况。

HTTP 和 HTTPS 协议的区别

HTTP 和 HTTPS 协议的主要区别如下:


  • HTTPS 协议需要 CA 证书,费用较高;而 HTTP 协议不需要;

  • HTTP 协议是超文本传输协议,信息是明文传输的,HTTPS 则是具有安全性的 SSL 加密传输协议;

  • 使用不同的连接方式,端口也不同,HTTP 协议端口是 80,HTTPS 协议端口是 443;

  • HTTP 协议连接很简单,是无状态的;HTTPS 协议是有 SSL 和 HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 HTTP 更加安全。

ES6 中模板语法与字符串处理

ES6 提出了“模板语法”的概念。在 ES6 以前,拼接字符串是很麻烦的事情:


var name = 'css'   var career = 'coder' var hobby = ['coding', 'writing']var finalString = 'my name is ' + name + ', I work as a ' + career + ', I love ' + hobby[0] + ' and ' + hobby[1]
复制代码


仅仅几个变量,写了这么多加号,还要时刻小心里面的空格和标点符号有没有跟错地方。但是有了模板字符串,拼接难度直线下降:


var name = 'css'   var career = 'coder' var hobby = ['coding', 'writing']var finalString = `my name is ${name}, I work as a ${career} I love ${hobby[0]} and ${hobby[1]}`
复制代码


字符串不仅更容易拼了,也更易读了,代码整体的质量都变高了。这就是模板字符串的第一个优势——允许用 ${}的方式嵌入变量。但这还不是问题的关键,模板字符串的关键优势有两个:


  • 在模板字符串中,空格、缩进、换行都会被保留

  • 模板字符串完全支持“运算”式的表达式,可以在 ${}里完成一些计算


基于第一点,可以在模板字符串里无障碍地直接写 html 代码:


let list = `    <ul>        <li>列表项1</li>        <li>列表项2</li>    </ul>`;console.log(message); // 正确输出,不存在报错
复制代码


基于第二点,可以把一些简单的计算和调用丢进 ${} 来做:


function add(a, b) {  const finalString = `${a} + ${b} = ${a+b}`  console.log(finalString)}add(1, 2) // 输出 '1 + 2 = 3'
复制代码


除了模板语法外, ES6 中还新增了一系列的字符串方法用于提升开发效率:


(1)存在性判定:在过去,当判断一个字符/字符串是否在某字符串中时,只能用 indexOf > -1 来做。现在 ES6 提供了三个方法:includes、startsWith、endsWith,它们都会返回一个布尔值来告诉你是否存在。


  • includes:判断字符串与子串的包含关系:


const son = 'haha' const father = 'xixi haha hehe'father.includes(son) // true
复制代码


  • startsWith:判断字符串是否以某个/某串字符开头:


const father = 'xixi haha hehe'father.startsWith('haha') // falsefather.startsWith('xixi') // true
复制代码


  • endsWith:判断字符串是否以某个/某串字符结尾:


const father = 'xixi haha hehe'  father.endsWith('hehe') // true
复制代码


(2)自动重复:可以使用 repeat 方法来使同一个字符串输出多次(被连续复制多次):


const sourceCode = 'repeat for 3 times;'const repeated = sourceCode.repeat(3) console.log(repeated) // repeat for 3 times;repeat for 3 times;repeat for 3 times;
复制代码

常见的 HTTP 请求头和响应头

HTTP Request Header 常见的请求头:


  • Accept:浏览器能够处理的内容类型

  • Accept-Charset:浏览器能够显示的字符集

  • Accept-Encoding:浏览器能够处理的压缩编码

  • Accept-Language:浏览器当前设置的语言

  • Connection:浏览器与服务器之间连接的类型

  • Cookie:当前页面设置的任何 Cookie

  • Host:发出请求的页面所在的域

  • Referer:发出请求的页面的 URL

  • User-Agent:浏览器的用户代理字符串


HTTP Responses Header 常见的响应头:


  • Date:表示消息发送的时间,时间的描述格式由 rfc822 定义

  • server:服务器名称

  • Connection:浏览器与服务器之间连接的类型

  • Cache-Control:控制 HTTP 缓存

  • content-type:表示后面的文档属于什么 MIME 类型


常见的 Content-Type 属性值有以下四种:


(1)application/x-www-form-urlencoded:浏览器的原生 form 表单,如果不设置 enctype 属性,那么最终就会以 application/x-www-form-urlencoded 方式提交数据。该种方式提交的数据放在 body 里面,数据按照 key1=val1&key2=val2 的方式进行编码,key 和 val 都进行了 URL 转码。


(2)multipart/form-data:该种方式也是一个常见的 POST 提交方式,通常表单上传文件时使用该种方式。


(3)application/json:服务器消息主体是序列化后的 JSON 字符串。


(4)text/xml:该种方式主要用来提交 XML 格式的数据。

new 操作符的实现原理

new 操作符的执行过程:


(1)首先创建了一个新的空对象


(2)设置原型,将对象的原型设置为函数的 prototype 对象。


(3)让函数的 this 指向这个对象,执行构造函数的代码(为这个新对象添加属性)


(4)判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象。


具体实现:


function objectFactory() {  let newObject = null;  let constructor = Array.prototype.shift.call(arguments);  let result = null;  // 判断参数是否是一个函数  if (typeof constructor !== "function") {    console.error("type error");    return;  }  // 新建一个空对象,对象的原型为构造函数的 prototype 对象  newObject = Object.create(constructor.prototype);  // 将 this 指向新建对象,并执行函数  result = constructor.apply(newObject, arguments);  // 判断返回对象  let flag = result && (typeof result === "object" || typeof result === "function");  // 判断返回结果  return flag ? result : newObject;}// 使用方法objectFactory(构造函数, 初始化参数);
复制代码

margin 和 padding 的使用场景

  • 需要在 border 外侧添加空白,且空白处不需要背景(色)时,使用 margin;

  • 需要在 border 内测添加空白,且空白处需要背景(色)时,使用 padding。

原型链指向

p.__proto__  // Person.prototypePerson.prototype.__proto__  // Object.prototypep.__proto__.__proto__ //Object.prototypep.__proto__.constructor.prototype.__proto__ // Object.prototypePerson.prototype.constructor.prototype.__proto__ // Object.prototypep1.__proto__.constructor // PersonPerson.prototype.constructor  // Person
复制代码

什么是 margin 重叠问题?如何解决?

问题描述: 两个块级元素的上外边距和下外边距可能会合并(折叠)为一个外边距,其大小会取其中外边距值大的那个,这种行为就是外边距折叠。需要注意的是,浮动的元素和绝对定位这种脱离文档流的元素的外边距不会折叠。重叠只会出现在垂直方向


计算原则: 折叠合并后外边距的计算原则如下:


  • 如果两者都是正数,那么就去最大者

  • 如果是一正一负,就会正值减去负值的绝对值

  • 两个都是负值时,用 0 减去两个中绝对值大的那个


解决办法: 对于折叠的情况,主要有两种:兄弟之间重叠父子之间重叠 (1)兄弟之间重叠


  • 底部元素变为行内盒子:display: inline-block

  • 底部元素设置浮动:float

  • 底部元素的 position 的值为absolute/fixed


(2)父子之间重叠


  • 父元素加入:overflow: hidden

  • 父元素添加透明边框:border:1px solid transparent

  • 子元素变为行内盒子:display: inline-block

  • 子元素加入浮动属性或定位

Proxy 可以实现什么功能?

在 Vue3.0 中通过 Proxy 来替换原本的 Object.defineProperty 来实现数据响应式。


Proxy 是 ES6 中新增的功能,它可以用来自定义对象中的操作。


let p = new Proxy(target, handler)
复制代码


target 代表需要添加代理的对象,handler 用来自定义对象中的操作,比如可以用来自定义 set 或者 get 函数。


下面来通过 Proxy 来实现一个数据响应式:


let onWatch = (obj, setBind, getLogger) => {  let handler = {    get(target, property, receiver) {      getLogger(target, property)      return Reflect.get(target, property, receiver)    },    set(target, property, value, receiver) {      setBind(value, property)      return Reflect.set(target, property, value)    }  }  return new Proxy(obj, handler)}let obj = { a: 1 }let p = onWatch(  obj,  (v, property) => {    console.log(`监听到属性${property}改变为${v}`)  },  (target, property) => {    console.log(`'${property}' = ${target[property]}`)  })p.a = 2 // 监听到属性a改变p.a // 'a' = 2
复制代码


在上述代码中,通过自定义 setget 函数的方式,在原本的逻辑中插入了我们的函数逻辑,实现了在对对象任何属性进行读写时发出通知。


当然这是简单版的响应式实现,如果需要实现一个 Vue 中的响应式,需要在 get 中收集依赖,在 set 派发更新,之所以 Vue3.0 要使用 Proxy 替换原本的 API 原因在于 Proxy 无需一层层递归为每个属性添加代理,一次即可完成以上操作,性能上更好,并且原本的实现有一些数据更新不能监听到,但是 Proxy 可以完美监听到任何方式的数据改变,唯一缺陷就是浏览器的兼容性不好。

CSS 选择器及其优先级


对于选择器的优先级


  • 标签选择器、伪元素选择器:1

  • 类选择器、伪类选择器、属性选择器:10

  • id 选择器:100

  • 内联样式:1000


注意事项:


  • !important 声明的样式的优先级最高;

  • 如果优先级相同,则最后出现的样式生效;

  • 继承得到的样式的优先级最低;

  • 通用选择器(*)、子选择器(>)和相邻同胞选择器(+)并不在这四个等级中,所以它们的权值都为 0 ;

  • 样式表的来源不同时,优先级顺序为:内联样式 > 内部样式 > 外部样式 > 浏览器用户自定义样式 > 浏览器默认样式。

如何判断一个对象是否属于某个类?

  • 第一种方式,使用 instanceof 运算符来判断构造函数的 prototype 属性是否出现在对象的原型链中的任何位置。

  • 第二种方式,通过对象的 constructor 属性来判断,对象的 constructor 属性指向该对象的构造函数,但是这种方式不是很安全,因为 constructor 属性可以被改写。

  • 第三种方式,如果需要判断的是某个内置的引用类型的话,可以使用 Object.prototype.toString() 方法来打印对象的[[Class]] 属性来进行判断。

为什么需要清除浮动?清除浮动的方式

浮动的定义: 非 IE 浏览器下,容器不设高度且子元素浮动时,容器高度不能被内容撑开。 此时,内容会溢出到容器外面而影响布局。这种现象被称为浮动(溢出)。


浮动的工作原理:


  • 浮动元素脱离文档流,不占据空间(引起“高度塌陷”现象)

  • 浮动元素碰到包含它的边框或者其他浮动元素的边框停留


浮动元素可以左右移动,直到遇到另一个浮动元素或者遇到它外边缘的包含框。浮动框不属于文档流中的普通流,当元素浮动之后,不会影响块级元素的布局,只会影响内联元素布局。此时文档流中的普通流就会表现得该浮动框不存在一样的布局模式。当包含框的高度小于浮动框的时候,此时就会出现“高度塌陷”。


浮动元素引起的问题?


  • 父元素的高度无法被撑开,影响与父元素同级的元素

  • 与浮动元素同级的非浮动元素会跟随其后

  • 若浮动的元素不是第一个元素,则该元素之前的元素也要浮动,否则会影响页面的显示结构


清除浮动的方式如下:


  • 给父级 div 定义height属性

  • 最后一个浮动元素之后添加一个空的 div 标签,并添加clear:both样式

  • 包含浮动元素的父级标签添加overflow:hidden或者overflow:auto

  • 使用 :after 伪元素。由于 IE6-7 不支持 :after,使用 zoom:1 触发 hasLayout**


.clearfix:after{    content: "\200B";    display: table;     height: 0;    clear: both;  }  .clearfix{    *zoom: 1;  }
复制代码

如何使用 for...of 遍历对象

for…of 是作为 ES6 新增的遍历方式,允许遍历一个含有 iterator 接口的数据结构(数组、对象等)并且返回各项的值,普通的对象用 for..of 遍历是会报错的。


如果需要遍历的对象是类数组对象,用 Array.from 转成数组即可。


var obj = {    0:'one',    1:'two',    length: 2};obj = Array.from(obj);for(var k of obj){    console.log(k)}
复制代码


如果不是类数组对象,就给对象添加一个[Symbol.iterator]属性,并指向一个迭代器即可。


//方法一:var obj = {    a:1,    b:2,    c:3};
obj[Symbol.iterator] = function(){ var keys = Object.keys(this); var count = 0; return { next(){ if(count<keys.length){ return {value: obj[keys[count++]],done:false}; }else{ return {value:undefined,done:true}; } } }};
for(var k of obj){ console.log(k);}

// 方法二var obj = { a:1, b:2, c:3};obj[Symbol.iterator] = function*(){ var keys = Object.keys(obj); for(var k of keys){ yield [k,obj[k]] }};
for(var [k,v] of obj){ console.log(k,v);}

复制代码

数组有哪些原生方法?

  • 数组和字符串的转换方法:toString()、toLocalString()、join() 其中 join() 方法可以指定转换为字符串时的分隔符。

  • 数组尾部操作的方法 pop() 和 push(),push 方法可以传入多个参数。

  • 数组首部操作的方法 shift() 和 unshift() 重排序的方法 reverse() 和 sort(),sort() 方法可以传入一个函数来进行比较,传入前后两个值,如果返回值为正数,则交换两个参数的位置。

  • 数组连接的方法 concat() ,返回的是拼接好的数组,不影响原数组。

  • 数组截取办法 slice(),用于截取数组中的一部分返回,不影响原数组。

  • 数组插入方法 splice(),影响原数组查找特定项的索引的方法,indexOf() 和 lastIndexOf() 迭代方法 every()、some()、filter()、map() 和 forEach() 方法

  • 数组归并方法 reduce() 和 reduceRight() 方法

单行、多行文本溢出隐藏

  • 单行文本溢出


overflow: hidden;            // 溢出隐藏text-overflow: ellipsis;      // 溢出用省略号显示white-space: nowrap;         // 规定段落中的文本不进行换行
复制代码


  • 多行文本溢出


overflow: hidden;            // 溢出隐藏text-overflow: ellipsis;     // 溢出用省略号显示display:-webkit-box;         // 作为弹性伸缩盒子模型显示。-webkit-box-orient:vertical; // 设置伸缩盒子的子元素排列方式:从上到下垂直排列-webkit-line-clamp:3;        // 显示的行数
复制代码


注意:由于上面的三个属性都是 CSS3 的属性,没有浏览器可以兼容,所以要在前面加一个-webkit- 来兼容一部分浏览器。


用户头像

loveX001

关注

还未添加个人签名 2022-09-01 加入

还未添加个人简介

评论

发布
暂无评论
前端打工人的面试总结_JavaScript_loveX001_InfoQ写作社区