目录
· HTTP1.0和HTTP2.0的区别、HTTP和HTTPS的区别
· 移动端的适配方案
flex + rem + flexiable.js + less
- 我们移动端采取 flex 布局
- rem单位: 做移动端适配的
rem相对单位,跟html文字大小有关系
- 媒体查询: 检测屏幕的视口宽度
- flexiable.js :可以根据屏幕的宽度自动修改html文字大小
- less: less让我们的css具有了计算能力
less 可以让我们很方便的 把 px 转换为 rem
· get请求与post请求的区别
- 定义
- get 和 post请求是http协议中的两种请求方式。
- get一般用来获取服务器的信息的,post一般是用来更新信息。
- 区别
- get请求一般用来请求获取数据,post请求一般作为发送数据到后台,传递数据,创建数据
- get请求也可以传参到后台,但是传递的参数则显示在地址栏,安全性低,且参数的长度也有限制(2048字符);post请求则是将传递的参数放在request body中,不会在地址栏显示,安全性比get请求高,参数没有长度限制
- get请求刷新浏览器或者回退没有影响,post请求则会重新请求一遍
- get请求可以被缓存,也会保留在浏览器的历史记录中,post请求不会被缓存,也不会保留在浏览器的历史记录中
- get请求通常是通过url地址请求,post常见的则是form表单请求
- get产生一个tcp数据包,post产生两个tcp数据包
- get产生的URL地址可以被Bookmark,而post不可以
- get请求会被浏览器主动cache,而post不会,除非手动设置
- 对参数的数据类型,get只接受ASCII字符,而post没有限制。
- get比post更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
· this 指向相关
- this指向不同是由函数调用方式决定的
- 普通函数的调用,this指向window
- 构造函数的调用,this指向实例对象
- 对象方法的调用,this指向该对象方法所属的对象
- 定时器函数的调用,this指向window
- 通过事件绑定方法的调用,this指向绑定事件的对象
- 箭头函数的调用,this指向父级对象
- 匿名函数的调用,this指向全局变量window
-
更改this指向的三个方法
-
call() 方法-----把普通函数的this指向window的,更改this指向
-
apply() 方法-----apply() 方法与call()很类似,它们的不同之处在于提供参数的方式,apply()使用参数数组,而不是参数列表
-
bind()方法—bind()方法创建的是一个新函数(也称为绑定函数),它和被调用函数有相同的函数体。当目标函数被调用时,this的值就会绑定到bind()的第一个参数上面
-
存储this指向到变量里面
· 多仓库管理代码的优势
多仓库在微服务中就是每个服务一个源码仓库
- 每一个服务都有一个独立的仓库,职责单一
- 代码量和复杂性受控,服务由不同的团队独立维护、边界清晰
- 单个服务也易于自治开发测试部署和扩展,不需要集中管理集中协调
- 利于进行权限控制,可以针对单个仓库来分配权限,权限分配力度比较细
· ts 相对于 js 的优势
- ts是js的超集,存在类型的脚本语言
- 继承了js的所有编程类型,js代码可在ts环境很好的运行
- 为构建大型应用而生,但小程序同样适用
- 强大的类型系统,拥有静态类型检查能力
- 新增类型注解和类型推断
- 拥有丰富的class扩展功能
- 添加了系统级设计能力,设计模式由顶层由下进行设计
· JS和TS的区别
· 说出几种vue常见指令
- v-for:用 v-for 指令根据遍历数组来进行渲染
- v-if:v-if 可以实现条件渲染
- v-once:v-once 关联的实例,只会渲染一次
- v-bind:用来动态的绑定一个或者多个数据(特性),可以简写为 :
- v-model: 主要用于表单的双向数据绑定。
- v-show:根据条件展示元素
- v-on: 主要用来监听 dom 事件,可以简写为 @
· v-if 和 v-show 有什么区别
- v-if: v-if 可以实现条件渲染,当条件为真,元素将会被渲染,当条件为假,元素会被销毁。所以v-if多用于切换次数少的地方
- v-show: v-show只是根据条件展示元素,当条件为真,元素将会被隐藏,当条件为假,元素会被显示。因此v-show多用于经常切换的地方
· HTTP响应码
- 1xx 表示【临时响应】并需要请求者继续执行操作的状态代码
- 100(继续)请求者应当继续提出请求。
- 101(切换协议)请求者已要求服务器切换协议,服务器已确认并准备切换。
- 2xx 表示【成功】处理了请求的状态代码
- 200(成功)服务器已成功处理了请求。通常,这表示服务器提供了请求的网页。
- 201(已创建)请求成功并且服务器创建了新的资源。
- 202(已接受)服务器已接受请求,但尚未处理。
- 203(非授权信息)服务器已成功处理了请求,但返回的信息可能来自另一来源。
- 204(无内容)服务器成功处理了请求,但没有返回任何内容。
- 205(重置内容)服务器成功处理了请求,但没有返回任何内容。
- 206(部分内容)服务器成功处理了部分 GET 请求。
- 3xx 表示要完成请求,需要进一步操作。通常,这些状态代码用来【重定向】
- 300(多种选择)针对请求,服务器可执行多种操作。
- 301(永久移动)请求的网页已永久移动到新位置。
- 302(临时移动)服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
- 303(查看其他位置)请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码。
- 304(未修改)自从上次请求后,请求的网页未修改过。
- 305(使用代理)请求者只能使用代理访问请求的网页。
- 307(临时重定向)服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
- 4xx 表示【请求可能出错】,妨碍了服务器的处理
- 400(错误请求)表示客户端请求的语法错误,服务器无法理解,例如 url 含有非法字符、json 格式有问题。
- 401(未授权)请求要求身份验证。对于需要登录的网页,服务器可能返回此响应。
- 402表示保留,将来使用。
- 403(禁止)表示服务器理解请求客户端的请求,但是拒绝请求。
- 404(未找到)服务器无法根据客户端的请求找到资源(网页)。
- 405(方法禁用)禁用请求中指定的方法。
- 406(不接受)无法使用请求的内容特性响应请求的网页。
- 407(需要代理授权)此状态代码与 401(未授权)类似,但指定请求者应当授权使用代理。
- 408(请求超时)服务器等候请求时发生超时。
- 5xx 表示【服务器】在尝试处理请求时发生内部错误。这些错误可能是服务器本身的错误,而不是请求出错
- 500(服务器内部错误)服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。
- 501(尚未实施)服务器不具备完成请求的功能。例如,服务器无法识别请求方法时可能会返回此代码。
- 502(错误网关)服务器作为网关或代理,从上游服务器收到无效响应。
- 503(服务不可用)服务器目前无法使用(由于超载或停机维护)。通常,这只是暂时状态。
- 504(网关超时)服务器作为网关或代理,但是没有及时从上游服务器收到请求。
- 505(HTTP 版本不受支持)服务器不支持请求中所用的 HTTP 版本。
常见的 http 响应码:
- 200 - 服务器成功返回网页
- 404 - 请求的网页不存在
- 503 - 服务不可用
· 如何让CSS只在当前组件中起作用
只需要在Style标签添加Scoped属性,即<style scoped></style>
· 单页应用优缺点
微信小程序也是单页面应用
- 优点
- 良好的交互体验
- 良好的前后端工作分离模式
- 减轻服务器的压力
- 缺点
- 首屏加载慢
- 不利于 SEO
· 浏览器的存储方式
- 常见的浏览器存储主要有
- 属于文档对象模型:documentcookie,
- 属于浏览器对象模型:localStorage,sessionStorage,indexDB
· cookie 的有效作用域
cookie的作用域为当前域及其子域
· webpack 配置
Webpack是一种前端资源构建工具,一个静态资源打包器。
在Webpack看来,前端所有的资源文件js、css、json、img、less都会作为模块处理。
它根据模块的依赖关系进行静态分析,打包生成对应的静态文件。
- Webpack五大核心概念
1. Entry
入口指示 Webpack以哪个文件为入口起点开始打包,分析构建内部依赖图
2. Output
输出指示 Webpack打包后的资源 bundles输出到哪里去,以及如何命名
3. Loder
让Webpack能够去处理那些非js文件
4. Plugins
Plugin是(插件)可以用于执行范围更广的任务,插件的范围包括从打包优化和压缩,一直重新定义环境中的的变量等
5. Mode
模式指示 Webpack使用相应的模式配置(development、production)
· react 声明周期
- 挂载阶段
- constructor:构造函数,初始化 state 对象或给自定义方法绑定 this
- getDerivedStateFromProps:接收到新的属性想去修改 state
- render:纯函数,只返回需要渲染的东西
- componentDidMount: 组件装载之后调用,此时可以获取到DOM节点并操作
- 更新阶段
- getDerivedStateFromProps: 此方法在更新个挂载阶段都可能会调用
- shouldComponentUpdate: 有两个参数nextProps和nextState,表示新的属性和变化之后的state
,
返回一个布尔值,true:
会触发重新渲染,false:
不会触发重新渲染,默认返回true - render: 更新阶段也会触发此生命周期
- getSnapshotBeforeUpdate:在render之后,componentDidUpdate之前调用,有两个参数prevProps和prevState,表示之前的属性和之前的state,这个函数有一个返回值,会作为第三个参数传给componentDidUpdate
,
此生命周期必须与componentDidUpdate搭配使用 - componentDidUpdate:有三个参数prevProps,prevState,snapshot,都表示之前的
- 卸载阶段
componentWillUnmount: 当组件被卸载或者销毁了就会调用
· Vue 和 React 的区别(使用和原理上都说一下)
- 响应式原理不同
Vue
Vue依赖收集,自动优化
,数据可变。- Vue递归监听data的所有属性,直接修改。
- 当数据改变时,自动找到引用组件重新渲染。
React
-
React基于状态机,手动优化,
数据不可变,需要setState驱动新的State替换老的State。 - 当数据改变时,以组件为根目录,默认全部重新渲染
- diff算法不同
两者流程思维上是类似的,都是基于两个假设(使得算法复杂度降为O(n)):
- 不同的组件产生不同的 DOM 结构。当type不相同时,对应DOM操作就是直接销毁老的DOM,创建新的DOM。
- 同一层次的一组子节点,可以通过唯一的 key 区分。
但两者源码实现上有区别:
Vue基于snabbdom库,它有较好的速度以及模块机制。Vue Diff使用双向链表,边对比,边更新DOM。
React主要使用diff队列保存需要更新哪些DOM,得到patch树,再统一操作批量更新DOM
。
- 事件机制不同
Vue
Vue原生事件使用标准Web事件
- Vue组件自定义事件机制,是父子组件通信基础
- Vue合理利用了snabbdom库的模块插件
React
React原生事件被包装
,所有事件都冒泡到顶层document监听,然后在这里合成事件下发。基于这套,可以跨端使用事件机制,而不是和Web DOM强绑定。- React组件上无事件,父子组件通信使用props
· c/c++和 js 区别
- C++是静态语言,js是动态语言
- C++是编译型语言,js是解释型语言
- C++有指针,js无指针
- js是函数式编程语言,C++不是
- C++的继承是基于类的,js的继承基于原型
· 进程间通信
- 本地进程之间的通信方式(IPC)
- 消息传递
- 信号量
- 共享内存
- 远程过程调用
- 远程进程之间的通信方式
套接字(ip地址:端口号):标识网络的进程
· HTTP1.0和HTTP2.0的区别、HTTP和HTTPS的区别
- HTTP1.0和HTTP2.0的区别
- HTTP/2采用二进制格式而非文本格式
- HTTP/2是完全多路复用的,而非有序并阻塞的——只需一个连接即可实现并行
- 使用报头压缩,HTTP/2降低了开销
- HTTP/2让服务器可以将响应主动“推送”到客户端缓存中
- HTTP和HTTPS的区别
- https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
- http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
- http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
- http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
· 为什么说组合优于继承
可以极大程度地减少子系统具体实现之间的相互依赖
· CSS 格式化上下文
- BFC:块级格式化上下文
一个元素具有 BFC,内部子元素再怎么变化,都不会影响外部的元素。这是BFC最核心的特点
应用场景:
- 防止垂直方向margin重叠
- 创建自适应两栏布局
- 清除浮动(解决浮动元素父元素高度塌陷)
- IFC:行内格式上下文
- GFC:网格布局格式化上下文(Grids网格布局)
- FFC:自适应格式上下文(flex布局)
· 清除浮动的四种方法
- 给浮动的元素的祖先元素加上高度
- clear:both;
- 隔墙法与内墙法
隔墙法 该方法通过div.cl h16这堵墙将两个父类分隔,弥补了clear:both方法中margin无效的情况,可以通过设置墙的高度来控制间隙
<div></div>
<div class="cl h16"></div>
<div></div>
.cl{
clear: both;
}
.hl6{
height: 16px;
}
内墙法 顾名思义,将墙修在了父类里面
<div>
<p></p>
<div class="cl h16"></div>
</div>
父类加overflow:hidden;,将超出父类的部分隐藏
· Vue双向绑定
- 实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。
- 实现一个订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图。
- 实现一个解析器Compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器
· 实现三列布局的四种方法
- 浮动布局(float)
- 定位布局(position)
- 圣杯布局
- 双飞翼布局
· React 虚拟DOM
虚拟DOM就是一个JS对象,通过对象的方式来表示DOM结构,React在内存中维护一个跟真实DOM一样的虚拟DOM树,再改动完组件后,会再生成一个新的虚拟DOM,React会将新的虚拟DOM和原来的虚拟DOM进行对比,找出两个DOM树的不同的地方(diff),然后在真实DOM上更新diff,提高渲染速度。
- diff算法的原理
虚拟DOM树发生变化后,生成DOM树更新补丁的方式、它通过对比新旧两颗虚拟DOM树的变更差异,将更新补丁作用于真实DOM,以最小的成本完成试图更新
- 具体流程
- 真实DOM首先映射为虚拟DOM
- 当虚拟DOM发生变化时,根据变化计算生成
patch
,这个patch
就是一个结构化的数据,包含了增加、更新、删除等操作。 - 根据patch去更新真是DOM,反馈到用户页面上
· React中的key是什么
key是React用于追踪哪些列表中元素被修改、被添加、被移除的辅助标识。在开发中,我们要保证每个元素的key在其同级的元素具有唯一性。
diff算法中会借助key来判断该元素是新创建的还是被移动而来的元素,从而减少不必要的渲染。
- key具有唯一性
- 尽量不要用数组中的index作为key
- 不要再render的时候使用随机数或其它操作给元素加上不稳定的key,因为这样造成的性能开销比不加key更多
· 虚拟DOM和真实DOM的区别
- 虚拟dom(VNode),假的,不是真实的dom
- 虚拟DOM不会进行排版与重绘操作 虚拟DOM就是把真实DOM转换为Javascript代码
- 真实的DOM在浏览器通过dom.api操作的,复杂的对象
- 虚拟DOM:可以通过this.$slots.default查看
- 真实的dom是一个对象,它的属性非常多,在浏览器中做dom操作,会比较消耗性能
- 虚拟dom是一个对象,它的属性相比较于真实的dom就比较少---用少量的属性描述一个dom,无法在浏览器中直接显示
· React组件之间的8种通讯方式
· 手写JS观察者模式
当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)比如,当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式
- 观察者(订阅者) –
Watcher
updata()
当事件发生时,具体要做的事情
- 发布者 –
Dep
subs
数组存储所有的观察者addSub()
添加观察者notify
当事件发生,循环所有subs中的观察者的updata方法
//发布者
class Dep {
constructor () {
this.subs = []
}
addSub (sub) {
if(sub && sub.updata){
this.subs.push(sub)
}
}
notify () {
this.subs.forEach((element)=>{
element.updata()
})
}
}
//观察者
class watch {
updata() {
console.log('观察者')
}
}
const dep = new Dep()
dep.addSub (new watch())
dep.notify()
· 代码题(查找数组中某个数的下标)(二分查找/折半查找)
- 前提条件
二分查找要求数组数据必须采用顺序存储结构有序排列。
- 原理
首先,假设数组中元素是按升序排列,将数组中间位置的数据与查找数据比较,如果两者相等,则查找成功;否则利用中间位置记录将数组分成前、后两个子数组,如果中间位置数据大于查找数据,则进一步查找前子数组,否则进一步查找后子数组。重复以上过程,直到找到满足条件的数据,则表示查找成功,直到子数组不存在为止,表示查找不成功。
import Java.util.Scanner;
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int[] nums = new int[9];
//初始化数组,随机100以内的数
for (int i = 0; i < nums.length; i++) {
nums[i]=(int) (Math.random()*100);
}
System.out.println("初始数组为:");
for (int i : nums) {
System.out.print(i+"\t");
}
//二分查找需要一个有顺序的数组,所以对初始化的数组进行排序
for (int i = 0; i < nums.length-1; i++) {
for (int j = 0; j < nums.length-i-1; j++) {
if (nums[j]>nums[j+1]) {
int temp=nums[j];
nums[j]=nums[j+1];
nums[j+1]=temp;
}
}
}
System.out.println("\n排序后数组为:");
for (int i = 0; i < nums.length; i++) {
System.out.print(nums[i]+"\t");
}
//二分查找
//最小下标
int minIndex=0;
//最大下标
int maxIndex=nums.length-1;
//中间下标
int centerIndex=(minIndex+maxIndex)/2;
//查找元素
System.out.print("\n请输入需要查找的元素:");
int findNum=scan.nextInt();
while (true) {
if (nums[centerIndex]>findNum) {
//当查找目标小于中间值时,此时目标值在左边,此时最大下标为中间下标-1
maxIndex=centerIndex-1;
} else if (nums[centerIndex]<findNum) {
//当查找目标大于中间值时,此时目标值在右边,此时最小下标为中间下标+1
minIndex=centerIndex+1;
}else {
System.out.println("查找元素"+findNum+"的下标为:"+centerIndex);
break;
}
if (minIndex>maxIndex) {
//当最小下标大于最大下标时,说明找不到目标值
System.out.println("查找元素的下标不存在");
break;
}
//每次查找后边界都会发生变化,所以就重新更新中间下标
centerIndex=(minIndex+maxIndex)/2;
}
// 关闭扫描,释放资源
scan.close();
}
· 代码题(三数之和)(双指针法)
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> list = new LinkedList<>();
Arrays.sort(nums);
for (int i = 0; i < nums.length - 2; i++) {
if(nums[i] > 0) {
break;
}
if(i > 0 && nums[i] == nums[i - 1]) {
continue;
}
int left = i + 1;
int right = nums.length - 1;
while(left < right) {
if(left > i + 1 && nums[left] == nums[left - 1]) {
left++;
continue;
}
if(right < nums.length - 1 && nums[right] == nums[right + 1]) {
right--;
continue;
}
int target = nums[i] + nums[left++] + nums[right--];
if(target > 0) {
right--;
} else if(target < 0) {
left++;
} else {
List<Integer> temp = new LinkedList<>();
temp.add(nums[i]); temp.add(nums[left]); temp.add(nums[right]);
list.add(temp);
}
}
}
return list;
}
· 代码题(任务队列)
设计一个简单的任务队列, 要求分别在 1,3,4 秒后打印出 "1", "2", "3"
async function one() {
setTimeout(function() {
console.log(1);
}, 1000);}
async function two() {
setTimeout(function() {
console.log(2);
}, 3000);}
async function three() {
setTimeout(function() {
console.log(3);
}, 4000);}
async function run() {
var p1 = await one();
var p2 = await two();
var p3 = await three();
}
run();
· 代码题(找出n个元素数组中重复次数最多的数)
public class Solution {
public int majorityElement(int[] nums) {
int count = 0;
int temp = 0;
for(int i= 0;i<nums.length;i++){
if(count == 0){
temp = nums[i];
count++;
}else{
if(temp == nums[i])
count++;
else
count--;
}
}
return temp;
}
}
· 代码题(字符数组全排列)
public static void main(String[] args) {
char[] chars = {'1','2','3','4'};
List<String> result = new ArrayList<>();
result = stringFullPermutation(chars,0,result);
for(String s : result)
System.out.println(s);
}
private static List<String> stringFullPermutation(char[] chars,int i,List<String> res){
if(i == chars.length)
return res;
List<String> result = new ArrayList<>();
if(i == 0){
result.add(String.valueOf(chars[i]));
}
else {
for(String s : res){
for(int j = 0;j <= s.length();j ++){
result.add(s.substring(0,j) + chars[i] + s.substring(j,s.length()));
}
}
}
return stringFullPermutation(chars,i + 1,result);
}
· 代码题(顺时针打印二维数组)
package 多维数组与矩阵;
/**
* 输入:
* 1 2 3 4
* 5 6 7 8
* 9 10 11 12
* 13 14 15 16
* 输出:1 2 3 4 8 12 16 15 14 13 9 5 6 7 11 10
*/
public class 顺时针打印二维数组 {
public static void main(String[] args) {
int[][] arr={
{1,2,3,4},
{5,6,7,8},
{9,10,11,12},
{13,14,15,16},
};
f(arr);
}
static void f(int[][] arr) {
//定义左上角和右下角的下标
int leftUpRow = 0, leftUpCol = 0, rightDownRow = arr.length - 1, rightDownCol = arr[0].length - 1;
while (leftUpRow<=rightDownRow&&leftUpCol<=rightDownCol) {
int r = leftUpRow, c = leftUpCol;
//上面一条边
while (c <= rightDownCol) {
System.out.print(arr[r][c++] + " ");
}
//恢复
c = rightDownCol;
r++;
//右面一条边
while (r <= rightDownRow) {
System.out.print(arr[r++][c] + " ");
}
//恢复
r = rightDownRow;
c--;
//最下面一条边
while (c >= leftUpCol) {
System.out.print(arr[r][c--] + " ");
}
//恢复
c = leftUpCol;
r--;
//最左边一条边
while (r > leftUpRow) {
System.out.print(arr[r--][c] + " ");
}
leftUpRow++;
leftUpCol++;
rightDownRow--;
rightDownCol--;
}
}
}