面试

120 阅读16分钟

知道 JSONP 吗,为什么没有跨域问题

jsonp是一种轻量级的数据交换格式,数据可以跨域获取,是一种跨域方式之一,他的缺点就是只能get请求

原理

在html标签中,script,link, img,iframe等含有src属性的标签,不受同源策略的影响,可以进行跨域请求资源,所以jsonp利用script这一特性,向服务器进行请求,服务端接收到请求并返回一段js代码,浏览器接受并响应执行

jsonp的请求过程

1.请求阶段:动态创建一个script标签,然后向src属性赋值,类似(api.douban.com/v2/book/sea…

2.发送请求:将src赋值,然后向服务器发送请求

3.数据响应:服务器接收到处理请求,并将要返回的数据作为参数和函数名拼接成字符串返回,(show({name: 'success'})),浏览器接收到并响应数据,由于是script发起的一个请求,所以他会立即调用show这个方法并传入参数


手写一个jsonp


实现ajax



什么是同源策略

同源策略就是相同的协议,域名,端口

什么是跨域, 如何解决跨域问题

跨域就是浏览器不能访问其他网站的脚本,这是同源策略限制的,是浏览器考虑安全作出的限制,浏览器从两个方面去做了同源限制,一个是对接口的请求,二是对dom的查询

jsonp,cors,nginx反向代理,postMessage(),document.domain


https 相较于 http 有什么改进,简述加密过程

增加了ssl安全层,http是超文本传输协议,位于应用层,http协议通过请求/响应方式,在客户端和服务端进行通信,但他很不安全,因为他都是明文传输,有三个重要安全问题

  • 数据会被篡改
  • 会被监听
  • 会伪装身份

那么https出现就是为了解决这个问题

为了安全,人们先想到对称加密密钥方式,但是这样中间人依然可以拦截,进行攻击,因为第一次通信还是使用明文通信的,所以截取后获取密钥及以后信息,所以人们又想到非对称加密密钥,就是使用公钥/私钥解密,通信方a发起通信并携带自己的公钥,接收方b利用公钥来加密对称加密密钥,然后通信方a拿着之前公钥的私钥去解密,获取b的对称加密密钥,然后按着对称加密方式进行通信,但这样中间人仍然可以进行拦截,做个偷天换日,将拦截下来的公钥,自己生成一对公钥私钥,然后把公钥发送给通信方b,然后通信方b并不知道公钥是被替换过的,所以就按照之前的流程,这样中间人再次拦截下来b发送出去的,这样就可以轻松加解密,虽然a和b还在用之前的密钥通信,但已经被中间人攻击

为了解决这个问题,就引入了权威证书颁发机构ca,通信方a向权威证书机构发送自己的公钥key1向他申请证书,然后权威机构自己也有一对公钥和私钥,他利用私钥对key1进行了加密,并利用服务端网址等信息生成签名,并将签名也用私钥加密,然后将证书给a,当b向a发送通信时候,a发送给b这个证书,b拿到这个证书先要进行验证证书的真假,因为各大浏览器和操作系统都知道各大权威机构的名称和公钥,所以b通过证书名称找到公钥进行解密出签名,然后按着签名规则自己生成一套签名,若签名一致,则说明证书是真的,然后b利用公钥解密出来a的公钥,然后进行上面的流程


DNS 是什么,做了什么

dns (domin name system) 域名系统,是互联网将ip和域名相互映射的分布式数据库,让人们更好的访问网络,而不是去记住服务器直接能读懂的ip串,通过域名,找到对应的ip地址,这个过程叫做域名解析,dns起到一个翻译作用

dns对我们影响

  • 若dns出现问题,会导致我们无法正常上网
  • 若dns遭到拦截,我们信息将会被泄漏,存在安全隐患

所以我们要优先选择运营商的dns,其次选择公用的dns

简述三次握手

三次握手其实就是客户端和服务端都证明自己的发送和接收能力没问题,且保证是最小的次数
  • 第一次客户端向服务端发送请求,说明客户端具有发送的能力
  • 第二次服务端接受客户端发送的请求并响应返回给客户端,说明服务器具有接受和发送能力
  • 第三次客户端接受到服务器的包并返回给服务器一个确认包,两者建立连接,完成三次握手

为了保证每次握手都是应答上一次的握手,每次握手都会带一个表示seq,然后后续的ack都会对这个seq进行+1确认


详细说一说浏览器从拿到 html 文件到完成页面渲染的过程,这个过程中哪几个环节可以进行性能优化

浏览器是流式布局模型

  • 解析html,生成dom树
  • 解析css,生成css树
  • 结合dom树和css树,合并成渲染树
  • (回流)通过渲染树进行回流,知道所有节点的几何信息(位置,大小)
  • (重绘)根据渲染树以及回流得到的几何信息,得到节点绝对像素
  • 将像素发送给GPU,显示在页面上
在回流和重绘阶段进行性能优化

何时触发回流和重绘

触发回流

  • 在添加或者删除dom节点时候
  • 浏览器窗口大小发生变化的时候
  • 元素位置更改的时候
  • 元素大小更改的时候
  • 内容发生变化的时候,比如文本内容,或者图片大小或者被替换不同尺寸图片
  • 第一次渲染时候,避免不了
触发重绘

改变元素的样式,但不改变元素位置(例如:color、background-color、visibility等)

如何避免重绘和回流

css

  • 避免使用table布局
  • 避免使用多层内联样式
  • 用transform替代top
  • 尽量避免使用css计算属性像calc
  • 尽量将动画效果放在position absolute或者fixed上
  • 用visibility替代display none
  • 尽量在dom树的末端操作class
  • 将频繁重绘或者回流的节点设置为图层,比如video,iframe
  • css3硬件加速

js

  • 避免频繁操作样式,最好设置一个class,一次性修改
  • 避免频繁操作dom,使用documentFragment,操作完后,再放回文档流
  • 避免频繁读取容易引发回流/重绘的属性,可以将他们设置成一个变量缓存起来
  • 对复杂动画使用绝对定位,让其脱离文档流

前端和后端通信有什么方法

ajax,websocket,服务端渲染,eventsource

参考:blog.csdn.net/BBmonster/a…

前端和 PC、APP 客户端通信有什么方法


外壳和子 iframe 通信有什么方法

在同域情况下

父调用子

framename.window.chlidmethod()

子调用父

parent.window.func()

需要注意的是,要在子页面加载完在调用

判断方法

  • 使用window.onload()方法
  • 使用document.readyState == 'complete'
在跨域情况下

父向子通信

hash

子元素通过设置定时器来监听location.hash的值,是否变化

父页面

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <script>
     var i = 1;
     function say(data) {
       console.log("我是父页面");
     }

     function func() {       //通过子iframe的name获取
      fuiframe.window.say();      fuiframe.window.document.getElementById('button').innerHTML = '调用结束';    // 通过自iframe的id获取
       document.getElementById('quanquan').contentWindow.say();       document.getElementById('quanquan').contentWindow.document.getElementById('button').innerHTML = '调用结束';     // 通过window对象的frames[]数组对象直接获取子frame对象
        window.frames[0].say()        window.frames["fuiframe"].document.getElementById('button').innerHTML = '调用结束';
   </script>
   <button id="button" onclick="func()">+1</button>
   <iframe name="fuiframe" id='quanquan' src="http://localhost:3000/demo2.html?aim=quanquan#1" frameborder="0"></iframe>
</body>
</html>

子向父通信

方法一 代理一个iframe,将子父在同一个域名下,通过window.top或者window.parent获取父级页面的引用

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <script>
    function ziFn() {
      console.log('我是子页面');
    }
    function func() {
    // 通过parent对象获取,//window 可以省略
      parent.window.fuFn();
      parent.window.document.getElementById('button').innerHTML = '调用结束'
    //  通过top对象获取,window 可以省略 ,window.top 为顶级页面。
     top.window.fuFn();
     top.window.document.getElementById('button').innerHTML = '调用结束'
  </script>
  <button id="button" onclick="func()"> 调用爸爸的方法 </button>
</body>
</html>


方法二 h5的postMessage

otherWindow.postMessage(message, targetOrigin)

otherWindow: 实指对接收信息页面的window的引用

message:要传输的数据

targetOrigin: 用于限制otherWindow,“*”表示不作限制。

 父级对message进行监听,e.data为postMessage传来的信息,默认为‘ubaLoadDone‘

父页面

window.addEventListener('message', function(event) {
    
      console.log('event.data是子页面通过postMessage传来的数据'+event.data);
      if(event.data != undefined && event.data !='ubaLoadDone'){
        // do something
      }
}, false)

子页面

let post = {text:'我是儿子'}
window.parent.postMessage(post, '*')

 参考:blog.csdn.net/weixin_3939…

浏览器 tab 间通信有什么方法

参考:juejin.cn/post/684490…

对 HTML 语义化的理解

html语义化通俗理解就是从代码上来展示页面结构,而不是最终的的视觉上,它更方便用户阅读也更有利于爬虫

优点

  • html语义化有助于架构良好的html结构,更有利于爬虫的检索
  • 有利于不同设备的解析
  • 方便团队维护
常用的语义化标签

h1-h6, ul li ol,p,strong,e,b,table,th,td,header,nav,section,aside,article,footer

有没有 SEO 优化经验

网站结构优化,页面优化,网站性能优化

网站结构优化

  • 导航清晰
  • 使用nofllow,在a标签ref属性使用noflollow,可以让爬虫不继续追踪此链接,因为如果跳出去别的页面就回不来了
  • 利用布局,把最重要的内容放在前面
  • 控制页面大小,减少http请求,提高爬虫速度
  • 网址规范,确保url唯一
页面优化
  • 合理使用title,description和keywords
  • 使用语义化书写代码
  • img要用alt,在图片没加载出来时候可以知道这个图片意思
  • 重要内容不要放在js输出,要放在html中,因为爬虫不会读取js
  • 尽量少使用iframe,因为爬虫不会读取其中内容
  • 尽量不要使用display:none,使用z-index或者opcity代替
网站性能优化
  • 减少http请求
  • 使用base64格式的图片:有些小图片,可能色彩比较复杂,这个时候再用iconfont就有点不合适了,此时可以将其转化为base64格式(不能缓存),直接嵌在src中,比如webpack的url-loader设置limit参数即可
  • css雪碧图(已经很少使用,不利于维护)
  • js和css合并打包,利用webpack,grunt,glup
  • 将css样式文件放在头部,js文件放在底部
  • 《script》标签里使用defer或这async 属性 异步加载,还有动态加载脚本,创建一个script标签,等到html解析完插入到文档中
  • 采用懒加载
  • 使用gzip压缩
  • 减少回流和重排
  • 图标用iconfont替代
  • 使用缓存
  • js打包这块,可以使用代码分割

参考:juejin.cn/post/684490…

juejin.cn/post/684490…

juejin.cn/post/684490…

juejin.cn/post/684490…

为什么引入 JS 使用 script 标签的 src 属性, 而引入 CSS 使用 link 标签的 href 属性

script标签的src的是引用资源,表示替换当前标签,它用在img,script,iframe上,在浏览器解析html时候,遇到script标签会暂停其他资源的下载和解析,知道script加载编译执行完毕才开始加载其他资源,这就是为什么放在底部原因

css的link是引用和页面关联,是当前元素和外部引用文件资源的桥梁,在浏览器解析时候,遇到会下载不会停止对当前文档的解析

知道 script 标签有个 integrity 属性吗,Subresource Integrity 策略解决了什么问题

解决了xss攻击问题,因为如果cdn资源被污染,就会收到xss攻击,这时候浏览器会检测脚本的签名和给定的签名是否一致,不一致则拒绝执行脚本,但是有个条件,必须同域,或者设置cros,配置了Access-Control-Allow-Origin响应头

JS画个圆


深拷贝和浅拷贝的区别

深浅拷贝对于复杂的数据类型来说,浅拷贝只是拷贝一层,而深拷贝是层层拷贝

浅拷贝

浅拷贝就是拷贝每个属性的值,当属性是基本类型的时候,拷贝的是基本类型的值,如果是个引用类型的时候,则是拷贝他的内存地址,如果其中一个对象改变了这个地址,就会影响另外一个对象

常用方法 for..in , Object.assign, 扩展运算符 ... 


深拷贝

深拷贝是指将对象从内存中完整的拷贝出来,在堆内存中开辟一个新的领域来存贮对象,两个对象互不影响,是完全隔离的

深拷贝实现


css画个圆

方法一: 使用border

width: 100px;

height: 100px;

border-radius: 50%;

border: 1px solid red;

方法二:使用box-shadow

width: 100px;

height: 100px;

border-radius: 50%;

box-shodow: 0 0 0 1px red;

方法三:使用radial-gradient

width: 100px;

height: 100px;

border-radius: 50%;

background: -webkit-radial-gradient(circle, red, red)

原生 CSS 支持变量吗

原生css没有变量这个概念,css预处理器支持,less,sass,stylus等

CSS 为什么没有父元素选择器

因为浏览器解析html文档是从前往后,从外向内的,如果css有了父元素选择器,那么就要等页面所有子元素全部加载完,才能解析html,这样就会造成长时间大面积白屏,这样体验很不好,加载多少html就渲染多少html,在网速慢的时候尤为重要,所以没有父元素选择器是考虑浏览器的渲染机制

JSX 和 template 各有什么优劣

jsx就是在js里写html,而template是在html里写js

jsx的表达式是个{},而template是{{}}

jsx

jsx的本事就是对js的一种扩展,基本上一个有状态的组件就是一个类。在一个固定的render里返回标签和数据逻辑处理全部耦合在一个类里

jsx的优点

  • 可以用js来构建视图,比如可以使用临时变量,直接引用当前作用域里的值,使用js里的流程控制
  • 开发工具对jsx的支持比对template的更先进(例如eslint,类型检查,编辑器自动补齐等)
  • 对处理复杂且灵活的逻辑,比较适合
  • 类似与你去登山jsx只给你一些必要的工具,而vue是给你准备很多,你直接带着指南书就可以了

jsx的缺点

  • 一些简单的功能,比如v-on,v-if,v-else等,在template里是很容易,但jsx却需要更多的代码实现
  • 原生浏览器不支持,需要bable编译
  • jsx的标签和逻辑都是混在一起的,代码不够优雅,而且会容易带入个人编码风格
templates

template的优点

  • 对于新手更好上手,更符合传统html的编码风格,比jsx更容易读懂,它已经帮你优化了很多东西,只需记住规则使用就行
  • vue更适合处理简单快速的渲染
  • html页面迁移到vue项目中更容易
template的缺点
  • 不容易发现错误
  • 在处理逻辑复杂的时候,用jsx更好

参考:cn.vuejs.org/v2/guide/co…

对 MVC、MVVM 架构模式有没有理解

MVC

Model-View-Controller 即模型-视图-控制器 

Model用来存取数据,你可以理解他只和数据库打交道,他不关心数据将来要干嘛

View用来展示数据

Controller的作用就是将Model和View分离开

Controller这个控制器起到承上启下作用,Controller从model里读取数据,然后给view展示,

view将要传递的事件给Controller,Controller做逻辑处理,Controller然后给model进行数据存贮

它是单项通信的,就是View跟Model,必须通过Controller来承上启下

MVVM

Model-View-ViewModel 即模型-数据-数据模型

之所以推出mvvm,是因为在以前手机端页面逻辑简单,数据不复杂,而现在对交互的要求愈来越高,页面逻辑数据也越来愈复杂,Controller用来解析数据,似乎显得很臃肿,于是就新建了一个类vm,vm相当于m和v的桥梁,这里Controller并没有被取代,只是存在感变弱了,就相当于Controller是个大boss,Model就是一堆要处理的文件,数据解析就是整理文件,如果是mvc模式,就是让大boss来整理这样乱七八糟的文件,而大boss它只要结果,不关心过程,所以他就招了一个小秘书,vm,让他来解析数据,就是整理文件,所以Controller只是处理操纵视图,ViewModel处理解析数据,抽离Controller中的业务逻辑,所以使用mvvm实现了业务逻辑的重用,提高了开发效率增加了代码复用性

所以Controller持有ViewModel,ViewModel持有Model

当Model想改变view视图时候,通过数据绑定

view想改变model时候,同时dom事件监听,这就是双向绑定


双向绑定实现

  • 数据劫持
  • 发布-订阅模式
  • 脏检查

数据劫持是通过Object.defineProperty来监听各属性的getter,setter,如果数据发生改变,则通知订阅者,触发相应的监听回调。vue的双向绑定大致分为三部分


observe: 数据监听,数据变化后通知订阅者

compiler:指令解析器,解析模版指令,绑定指定事件

watcher:订阅者,关联oberve和compiler,能够收到订阅和属性变动通知,执行指令绑定的事件,更新视图,update()是watcher自身的方法,更新视图

数据劫持


 这时候已经完成了对数据的监听,但是我们需要通知watcher数据变化了,这时候需要实现一个消息订阅器Dep,watcher通过dep添加订阅者,数据变化会触发Dep.notify(),获取通知,然后通过update()更新视图

谈谈你对函数式编程的看法,它和面向对象编程各有什么优劣

什么是设计模式,用过什么设计模式解决什么问题

怎么理解 React 的 Hooks,解决了什么问题

Hooks 是以 use 开头命名的,为什么不是 create、generate

HTML 是什么,有什么缺点

XML 是什么,能做什么

XHTML 是什么,解决了什么问题

JSON 相比于 XML 各有什么优缺点