在线访问手册:
https://hanxueqing.github.io/Web-Front-end-Interview-Q-A/
github地址:
https://github.com/Hanxueqing/Web-Front-end-Interview-Q-A
原型链
JavaScript原型,原型链 ? 有什么特点?
* 原型对象也是普通的对象,是对象一个自带隐式的 proto 属性,原型也有可能有自己的原型,如果一个原型对象的原型不为null的话,我们就称之为原型链。
* 原型链是由一些用来继承和共享属性的对象组成的(有限的)对象链。
* JavaScript的数据对象有那些属性值?
writable:这个属性的值是否可以改。
configurable:这个属性的配置是否可以删除,修改。
enumerable:这个属性是否能在for…in循环中遍历出来或在Object.keys中列举出来。
value:属性值。
* 当我们需要一个属性的时,Javascript引擎会先看当前对象中是否有这个属性, 如果没有的话,就会查找他的Prototype对象是否有这个属性。
function clone(proto) {
function Dummy() { }
Dummy.prototype = proto;
Dummy.prototype.constructor = Dummy;
return new Dummy(); //等价于Object.create(Person);
}
function object(old) {
function F() {};
F.prototype = old;
return new F();
}
var newObj = object(oldObject);
Javascript如何实现继承?
原型链继承,借用构造函数继承,组合继承,寄生式继承,寄生组合继承
数组
for-of和for-in的区别
遍历数组的时候:
for-of通过key直接拿数组里面的值
for-in通过key拿数组的索引
遍历对象的时候:
for-in通过key拿属性值
for-of不可以遍历对象
遍历对象的方法,for of为什么不能遍历对象?
for-of可以遍历的:arr/map/string,因为他们中实现了Symbol.iterator属性
for of如何遍历对象
注意:for-of目前js实现的对象有array,string,argument以及后面更高级的set,Map
当我们遍历对象的时候可以使用for-in,不过这种遍历方式会把原型上的属性和方法也给遍历出来,当然我们可以通过hasOwnProperty来过滤掉除了实例对象的数据,但是for-of在object对象上暂时没有实现,但是我们可以通过Symbol.iterator给对象添加这个属性,我们就可以使用for-of了,代码如下:
var p = {
name:'kevin',
age:2,
sex:'male'
}
Object.defineProperty(p,Symbol.iterator,{
enumberable:false,
configurable:false,
writable:false,
value:function(){
var _this = this;
var nowIndex = -1;
var key = Object.keys(_this);
return {
next:function(){
nowIndex++;
return {
value:_this[key[nowIndex]],
done:(nowIndex+1>key.length)
}
}
}
}
})
//这样的话就可以直接通过for-of来遍历对象了
for(var i of p){
console.log(i) //kevin,2,male
}
for-in遍历数组存在的问题(for-in更适合遍历对象)
var myArray = [1,2,4,5,6,7]
myArray.name = "杀马特"
Array.prototype.method = function(){
console.log("length",this.length)
}
for(var index in myArray){
console.log("myArray",myArray[index])
}
使用for in 也可以遍历数组,但是会存在以下问题:
index索引为字符串型数字,不能直接进行几何运算
遍历顺序有可能不是按照实际数组的内部顺序
使用for in会遍历数组所有的可枚举属性,包括原型。例如上栗的原型方法method和name属性 所以for in更适合遍历对象,不要使用for in遍历数组。
使用for-of遍历数组:
//for-of遍历数组
var myArray = [1, 2, 4, 5, 6, 7]
myArray.name = "杀马特"
Array.prototype.method = function () {
console.log("length", this.length)
}
for (var index of myArray) {//这里的index输出的是value而不是索引
console.log("index", index)
}
for循环和forEach循环 哪个可以停止
for循环可以通过break停止,forEach一般是没有办法终止跳出。
如何实现forEach的终止跳出?
思路1 break 不能用,不可行,执行后会报错!
var arr = ['a','b','c']
arr.forEach((item,index) => {
if(item === 'b') break
console.log(item)
})
思路2 return false 会跳出当前的遍历执行==》a,c
var arr = ['a','b','c']
arr.forEach((item,index) => {
if(item === 'b') return false
console.log(item)
})
思路3 try...catch 语句跳出异常 //a exit done
try {
arr.forEach((item,index) => {
if(item === 'b') throw new Error('exist')
console.log(item)
})
} catch (e) {
if(e.message=='exist'){
console.log(e.message)
throw e
}
} finally {
console.log('done')
}
程序最后可以终止退出循环,所以使用try...catch通过抛出异常的方式来终止程序继续执行是可行。
arr.map arr.filter arr.reduce的作用分别是?
【arr.map】
遍历数组通常使用for循环,ES5的话也可以使用forEach,ES5具有遍历数组功能的还有map、filter、some、every、reduce、reduceRight等,只不过他们的返回结果不一样。但是使用foreach遍历数组的话,使用break不能中断循环,使用return也不能返回到外层函数
arr.map循环遍历,并且将符合条件的元素放入一个新数组返回
var arr = [1,2,3]
let b = [];
//map循环遍历,并且返回一个新数组
let a = arr.map(item=>{
if(item>2){
b.push(item)
return b
}
})
console.log(b) //返回符合条件的新数据[3]
因为react中没有v-for指令,所以循环渲染的时候需要用到arr.map方法来渲染视图
【arr.filter】
arr.filter 过滤器 可以过滤掉不符合要求的元素,内部返回false就直接过滤掉了,将符合条件的数据返回,也是不影响原数组。
//过滤器
arr = arr.filter(item=>{
return item>2
})
console.log(arr) //过滤出来符合条件的数据[3]
【arr.reduce】
reduce为数组中每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元素,接收四个参数:初始值(或者上一次回调函数的返回值),当前元素值,当前索引,调用reduce的数组。reduce方法可以搞定的东西for循环或者forEach方法有时候也可以搞定。
var newArr = [4,5,6,7]
newArr.reduce((res,currentValue,index,arr)=>{//第一个参数为一个回调函数
console.log(res,currentValue,index) //4,5,1
})
//res:默认为第一个值(4),currentValue:当前值(5),index:当前值的索引(1)
let result = newArr.reduce((res,currentValue,index,arr)=>{
console.log(res,currentValue) //第一次:4,5;第二次:9,6;第三次:15,7
return res+currentValue//第一次:9;第二次:15;第三次:22;最后结果为22
},10)//第二个参数为一个初始值,这里给它赋值为10
console.log(result)//现在res就变成了10,currentValue变成了4,依次累加,10+22 初始值+累加值
Filter、forEach、map、reduce之间的区别联系?
作用
every():对数组中的每一项运行给定函数,如果该函数对每一项都返回 true ,则返回 true。
some():对数组中的每一项运行给定函数,如果该函数对任一项返回 true ,则返回 true
filter():对数组中的每一项运行给定函数,返回该函数会返回 true 的项组成的数组。
forEach():对数组中的每一项运行给定函数。这个方法没有返回值。
map():对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。
相同点
他们的参数都一样:
- 在每一项上运行的函数(该函数有三个参数)
- 函数第一个参数:数组项的值
- 函数第二个参数:数组项的索引
- 函数第三个参数:数组对象本身
- 运行该函数的作用域对象——影响this的值(可选)
区别
filter()、forEach()、map()、some()、every()都是对数组的每一项调用函数进行处理。
区别:
– some()、every()的返回值 :true / false
– filter()、map()的返回值 :一个新数组
– forEach()无返回值。
使用filter()、forEach()、map()、some()、every()都不改变原数组。
参考:js小记:filter()、forEach()、map()、reduce()、reduceRight()的区别
https://blog.csdn.net/b954960630/article/details/81432881
splice()
splice(index,len,[item]) 注释:该方法会改变原始数组。
//删除起始下标为1,长度为1的一个值(len设置1,如果为0,则数组不变)
var arr = ['a','b','c','d'];
arr.splice(1,1);
console.log(arr); //['a','c','d'];
//替换起始下标为1,长度为1的一个值为‘ttt’,len设置的1
var arr = ['a','b','c','d'];
arr.splice(1,1,'ttt');
console.log(arr); //['a','ttt','c','d']
var arr = ['a','b','c','d'];
arr.splice(1,0,'ttt');
console.log(arr); //['a','ttt','b','c','d'] 表示在下标为1处添加一项'ttt'
slice()
slice()方法可以基于当前数组获取指定区域元素 [start, end)
格式:数组.slice(start, end);
参数:start和end都是下标,[start, end),获取指定范围内的元素,生成新数组。(原数组是不会改变的。)
返回值:提取出来元素组成的新数组。
split()和join()的区别
join函数获取一批字符串,然后用分隔符字符串将它们连接起来,从而返回一个字符串。
split()函数获取一个字符串,然后在分隔符处将其断开,从而返回一批字符串。
但是,这两个函数之间的区别在于join可以使用任何分割字符串将多个字符串连接起来,而split()只能使用一个字符分隔符将字符串断开。
简单地说,如果你用split(),是把一串字符串(根据某个分隔符)分成若干个元素存放在一个数组里。而join是把数组中的字符串连接成一个长串,可以大体上认为是split的逆操作。
push、pop、shift、unshift
push()
方法可以在数组的末属添加一个或多个元素shift()
方法把数组中的第一个元素删除unshift()
方法可以在数组的前端添加一个或多个元素pop()
方法把数组中的最后一个元素删除
参考:数组的push()、pop()、shift()和unshift()方法
https://blog.csdn.net/qwe502763576/article/details/79055682
数组的方法
解析链接www.qq.com?name=jack&age=18&id=100,获取其中的查询参数,并格式为js对象的代码:如: {name:”jack”,age:18,id:100}
var str = "www.qq.com?name=jack&age=18";
var str2 = str.substring(str.lastIndexOf(“?")+1); //索引从后往前找
var arr = str2.split(“&”);//通过&符号去切割 切割成如下形式[‘name = jack’,’age=18’,'id=100']
var json = {};
for(var i=0;i<arr.length;i++){ //循环遍历数组的长度
var newArr = arr[i].split(“=“); //拿到每一个字符串 拿到之后通过split进行切割 [‘name’,’jack’]
json[newArr[0]] = newArr[1]; // key名=value
}
考查知识点:数组截取方法/数组中字符串操作/对象的中括号会不会用
对象的、数组的、字符串的相关的使用的比较多的方法
下列代码的运行结果是:
var array1 = [1,2];
var array2 = array1; // 数组指向的是同一个地址
array1[0] = array2[1]; // 把array[1]拿出来放在array1[0]的位置变成2,2
array2.push(3);
console.log(array1);// [2,2,3]
console.log(array2);// [2,2,3]
var arr = [1,2]
arr2 = arr;//arr2和arr1指向的是同一块内存空间
arr2.push(3)
console.log(arr) //[1,2,3]
var arr = [1,2]
arr2 = arr
arr = [1,2,3] //arr 指向了新的内存空间
arr2.push(4)
console.log(arr)//[1,2,3]
console.log(arr2)//[1,2,4]
var a = {n:1}
var b = a;
a.x = a = {n:2}
console.log(a); //a赋值了一个新的地址 输出{n:2}
console.log(b); //b指向的是a原来的地址 输出{n:1,x:{n:2}}
JS数组去重
- 利用splice方法
var arr = [10, 20, 30, 40, 50, 40, 30, 20, 20, 20, 20, 10];
function norepeat(arr){
for(var i = 0; i < arr.length - 1; i++){
for(var j = i + 1; j < arr.length; j++){
if(arr[i] === arr[j]){
arr.splice(j,1);
j--
}
}
}
}
alert(arr);//10,20,30,40,50,40,30,20,20,20,20,10
norepeat(arr);
alert(arr);//10,20,30,40,50
- 倒序删除
//倒序删除
function norepeat(arr){
for(var i = arr.length -1; i > 0; i--){
for(var j = i - 1;j >= 0; j--){
if(arr[i] === arr[j]){
arr.splice(j,1);
}
}
}
}
alert(arr);//10,20,30,40,50,40,30,20,20,20,20,10
norepeat(arr);
arr.reverse();
alert(arr);//10,20,30,40,50
- 利用对象的属性不会重复这一特性,校验数组元素是否重复
function unique(arr){
var obj = {};
var result = [];
for(var i=0;i<arr.length;i++){
if(!obj[arr[i]]){
result.push(arr[i]);
obj[arr[i]] = true; //将每个数作为key,属性值值为true,当key名相同时,判断属性值是否为false,不为false则不push进数组
}
}
return result;
}
-
ES6中新增了数据类型set,set的一个最大的特点就是数据不重复。Set函数可以接受一个数组(或类数组对象)作为参数来初始化,利用该特性也能做到给数组去重。
Set集合是默认去重复的,但前提是两个添加的元素严格相等,所以5和"5"不相等,两个new出来的字符串不相等。
下面展示了一种极为精巧的数组去重的方法
var newarr = [...new Set(array)];
//拓展 固定写法
alert(arr);//10,20,30,40,50,40,30,20,20,20,20,10
//下述写法的值,就是去重以后的数组。
arr = [...new Set(arr)];
alert(arr);//10,20,30,40,50
跨域
什么是跨域
由于 Javascript 同源策略的存在使得一个源中加载来自其它源中资源的行为受到了限制。即会出现跨域请求禁止。
通俗一点说就是如果存在协议、域名、端口或者子域名不同服务端,或一者为IP地址,一者为域名地址(在跨域问题上,域仅仅是通过“ url的首部 ”来识别而不会去尝试判断相同的IP地址对应着两个域或者两个域是否同属同一个IP),之中任意服务端旗下的客户端发起请求其它服务端资源的访问行动都是跨域的,而浏览器为了安全问题一般都限制了跨域访问,也就是不允许跨域请求资源。
同源策略
同协议
同域名
同端口号
解决跨域问题的主流方案是什么
Jsonp 利用script标签发起get请求不会出现跨域禁止的特点实现
window.name+iframe 借助中介属性window.name实现
html5的 postMessage 主要侧重于前端通讯,不同域下页面之间的数据传递
Cors需要服务器设置header:Access-Control-Allow-Origin
Nginx反向代理 可以不需要目标服务器配合,不过需要Nginx中转服务器,用于转发请求(服务端之间的资源请求不会有跨域限制)
参考:Nginx反向代理、CORS、JSONP等跨域请求解决方法总结
https://blog.csdn.net/diudiu5201/article/details/54808142
cors实现请求跨域
https://blog.csdn.net/badmoonc/article/details/82706246
JQuery跨域方式
- 修改ajax的请求头(尽量不要用)
改成"Access-control-Allow-Origin"
通过PHP文件作为中转站,进行间接跨源(爬虫)
JSONP跨域
JSONP原理
动态创建script标签,src属性连接接口地址,callback参数就是服务器返回给我们的数据。
缺点:jsonp只支持get请求方式,post方式不支持。
JSONP跨域的流程
在资源加载进来之前定义好一个函数,这个函数接收一个参数(数据),函数里面利用这个参数做一些事情。
然后需要的时候通过script标签加载对应的远程文件资源。
当远程文件加载进来的时候,就会去执行我们前面定义好的函数,并且把数据当做这个函数中的参数传进去。
JSONP跨域的缺点
它只支持GET请求而不支持POST等其它类型的HTTP请求;
它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题
json文件不能用jsonp方法。
AJAX
什么是AJAX
AJAX是一种用于快速创建动态网页的技术,通过在后台与服务器进行少量数据交换,AJAX可以实现网页实现异步更新,这意味着可以在不加载整个网页的情况下,对网页的某部分进行更新。
传统的网页(不使用AJAX)如果需要更新内容,必须重载整个网页。
AJAX的原理
思路:先解释异步,再解释ajax如何使用
Ajax的原理简单来说通过XmlHttpRequest对象来向服务器发异步请求,从服务器获得数据,然后用javascript来操作DOM而更新页面。这其中最关键的一步就是从服务器获得请求数据。要清楚这个过程和原理,我们必须对 XMLHttpRequest有所了解。
XMLHttpRequest是ajax的核心机制,它是在IE5中首先引入的,是一种支持异步请求的技术。简单的说,也就是javascript可以及时向服务器提出请求和处理响应,而不阻塞用户。达到无刷新的效果。
AJAX的优点
AJAX不是新的编程语言,而是一种使用现有标准的新方法。
AJAX最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。
AJAX不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上运行。
AJAX的缺点
AJAX干掉了Back和History功能,即对浏览器机制的破坏。 在动态更新页面的情况下,用户无法回到前一个页面状态,因为浏览器仅能记忆历史记录中的静态页面。一个被完整读入的页面与一个已经被动态修改过的页面之间的差别非常微妙;用户通常会希望单击后退按钮能够取消他们的前一次操作,但是在Ajax应用程序中,这将无法实现。
安全问题技术同时也对IT企业带来了新的安全威胁,ajax技术就如同对企业数据建立了一个直接通道。这使得开发者在不经意间会暴露比以前更多的数据和服务器逻辑。ajax的逻辑可以对客户端的安全扫描技术隐藏起来,允许黑客从远端服务器上建立新的攻击。还有ajax也难以避免一些已知的安全弱点,诸如跨站点脚步攻击、SQL注入攻击和基于credentials的安全漏洞等。
对搜索引擎的支持比较弱。如果使用不当,AJAX会增大网络数据的流量,从而降低整个系统的性能。
AJAX状态码说明
1**:请求收到,继续处理
2**:操作成功收到,分析、接受
3**:完成此请求必须进一步处理
4**:请求包含一个错误语法或不能完成
5**:服务器执行一个完全有效请求失败
AJAX的流程
- 声明Ajax对象xhr,创建 XMLHttpRequest 实例
IE8以后才有如下声明的方式
var xhr = new XMLHttpRequest();
XMLHttpRequest()是AJAX的原生对象
- 一旦新建实例,就可以使用
open()
方法发出 HTTP 请求。
填写请求信息,发出 HTTP 请求
第一个参数:请求方式:get/post
第二个参数:url(统一资源定位符)
第三个参数:true/false true异步,false同步
xhr.open('GET', 'http://www.example.com/page.php', true);
- 向服务器发送请求
xhr.send(),正在发送请求
- 等待数据响应,接收服务器传回的数据
xhr.onreadystatechange = function(){}
在回调函数中进行请求状态readyState的监控
当 readyState 等于 4 且状态为 200 时,表示响应内容解析完成,可以在客户端调用了。
返回的内容:
responseText:返回以文本形式存放的内容
responseXML:返回XML形式的内容
- 更新网页数据
AJAX中的GET和POST请求
get一般用来进行查询操作,url地址有长度限制,请求的参数都暴露在url地址当中,如果传递中文参数,需要自己进行编码操作,安全性较低。
post请求方式主要用来提交数据,没有数据长度的限制,提交的数据内容存在于http请求体中,数据不会暴漏在url地址中。
GET 还是 POST?
与 POST 相比,GET 更简单也更快,并且在大部分情况下都能用。
然而,在以下情况中,请使用 POST 请求:
- 无法使用缓存文件(更新服务器上的文件或数据库)
- 向服务器发送大量数据(POST 没有数据量限制)
- 发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠
AJAX如何实现跨域
理解跨域的概念:协议、域名、端口都相同才同域,否则都是跨域。
出于安全考虑,服务器不允许ajax跨域获取数据,但是可以跨域获取文件内容,所以基于这一点,可以动态创建script标签,使用标签的src属性访问js文件的形式获取js脚本,并且这个js脚本中的内容是函数调用,该函数调用的参数是服务器返回的数据,为了获取这里的参数数据,需要事先在页面中定义回调函数,在回调函数中处理服务器返回的数据,这就是解决跨域问题的主流解决方案。
一个页面从输入URL到页面加载显示完成,这个过程中都发生了什么?
分为4个步骤:
当发送一个 URL 请求时,不管这个 URL 是 Web 页面的 URL 还是 Web 页面上每个资源的 URL,浏览器都会开启一个线程来处理这个请求,同时在远程 DNS 服务器上启动一个 DNS 查询。这能使浏览器获得请求对应的 IP 地址。
浏览器与远程 Web 服务器通过 TCP 三次握手协商来建立一个 TCP/IP 连接。该握手包括一个同步报文,一个同步-应答报文和一个应答报文,这三个报文在 浏览器和服务器之间传递。该握手首先由客户端尝试建立起通信,而后服务器应答并接受客户端的请求,最后由客户端发出该请求已经被接受的报文。
一旦 TCP/IP 连接建立,浏览器会通过该连接向远程服务器发送 HTTP 的 GET 请求。远程服务器找到资源并使用 HTTP 响应返回该资源,值为 200 的 HTTP 响应状态表示一个正确的响应。
此时,Web 服务器提供资源服务,客户端开始下载资源。
从用户输入URL,到浏览器呈现给用户页面,经过了什么过程?
用户输入URL,浏览器获取到URL
浏览器(应用层)进行DNS解析(如果输入的是IP地址,此步骤省略)
根据解析出的IP地址+端口,浏览器(应用层)发起HTTP请求,请求中携带(请求头header(也可细分为请求行和请求头)、请求体body),
header包含:
请求的方法(get、post、put..)
协议(http、https、ftp、sftp...)
目标url(具体的请求路径已经文件名)
一些必要信息(缓存、cookie之类)
body包含:
请求的内容
请求到达传输层,tcp协议为传输报文提供可靠的字节流传输服务,它通过三次握手等手段来保证传输过程中的安全可靠。通过对大块数据的分割成一个个报文段的方式提供给大量数据的便携传输。
到网络层, 网络层通过ARP寻址得到接收方的Mac地址,IP协议把在传输层被分割成一个个数据包传送接收方。
数据到达数据链路层,请求阶段完成
接收方在数据链路层收到数据包之后,层层传递到应用层,接收方应用程序就获得到请求报文。
接收方收到发送方的HTTP请求之后,进行请求文件资源(如HTML页面)的寻找并响应报文。
发送方收到响应报文后,如果报文中的状态码表示请求成功,则接受返回的资源(如HTML文件),进行页面渲染。
fetch
什么是fetch?
fetch号称是AJAX的替代品,是在ES6出现的,使用了ES6中的promise对象。Fetch是基于promise设计的。Fetch的代码结构比起ajax简单多了,参数有点像jQuery ajax。但是,一定记住fetch不是ajax的进一步封装,而是原生js,没有使用XMLHttpRequest对象。
try {
let response = await fetch(url);
let data = response.json();
console.log(data);
} catch(e) {
console.log("Oops, error", e);
}
fetch的优点
- 符合关注分离,没有将输入、输出和用事件来跟踪的状态混杂在一个对象里。
- 更好更方便的写法
fetch的优势
- 语法简洁,更加语义化
- 基于标准 Promise 实现,支持 async/await
- 同构方便,使用 isomorphic-fetch
- 更加底层,提供的API丰富(request, response)
- 脱离了XHR,是ES规范里新的实现方式
fetch的劣势
- fetch只对网络请求报错,对400,500都当做成功的请求,服务器返回 400,500 错误码时并不会 reject,只有网络错误这些导致请求不能完成时,fetch 才会被 reject。
- fetch默认不会带cookie,需要添加配置项: fetch(url, {credentials: 'include'})
- fetch不支持abort,不支持超时控制,使用setTimeout及Promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行,造成了流量的浪费
- fetch没有办法原生监测请求的进度,而XHR可以
axios
axios是什么
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中,主要是用于向后台发起请求的。
axios的用途
(1)向后台发送ajax请求数据。
(2) axios可以支持高并发请求,可以同时请求多个接口。
(3) axios可以防止CSRF/XSRF(跨站请求伪造)钓鱼网站。
(4)axios提供了拦截器、catch捕获。
封装axios
Get
import axios from 'axios'
export default ({url,data})=>{
return axios.get(url,{
params:data
})
}
Post
import axios from 'axios'
import qs from 'querystring'
export default ({url,data})=>{
return axios.post(url,qs.stringify(data))
}
利用axios实现数据请求
引入axios之后就可以在实例/组件上挂载一个$http属性,用这个就可以进行数据交互了。
提供了一个get方法,执行this.$http.get().then()可以异步执行,拿到promise对象的值
axios如何防止CSRF(跨站攻击)
在a页面登录注册成功后时候跳转到b页面,b页面可以获取你的cookie信息,把你的用户信息拦截到,然后恶意向别的网站散发一些邮件或者其他的东西,当你使用axios之后,就可以防止这种攻击,a页面存储的时候往cookie添加一个唯一个key,b页面如果是别的域名是获取不到这个key的,钓鱼网站就是把b页面这个域名篡改到别的位置去了,所以它无法获取到key值,也就拿不到用户信息,这样可以防止钓鱼网站的威胁。
如何在项目中通过路由守卫来实现登录拦截?讲一个在项目中使用路由守卫的案例?
拦截器的工作流程:
第一步:路由拦截
首先在定义路由的时候就需要多添加一个自定义字段requireAuth
,用于判断该路由的访问是否需要登录。如果用户已经登录,则顺利进入路由, 否则就进入登录页面。
const routes = [
{
path: '/',
name: '/',
component: Index
},
{
path: '/repository',
name: 'repository',
meta: {
requireAuth: true, // 添加该字段,表示进入这个路由是需要登录的
},
component: Repository
},
{
path: '/login',
name: 'login',
component: Login
}
];
定义完路由后,我们主要是利用vue-router
提供的钩子函数beforeEach()
对路由进行判断。
router.beforeEach((to, from, next) => {
if (to.meta.requireAuth) { // 判断该路由是否需要登录权限
if (store.state.token) { // 通过vuex state获取当前的token是否存在
next();
}
else {
next({
path: '/login',
query: {redirect: to.fullPath} // 将跳转的路由path作为参数,登录成功后跳转到该路由
})
}
}
else {
next();
}
})
其中,to.meta
中是我们自定义的数据,其中就包括我们刚刚定义的requireAuth
字段。通过这个字段来判断该路由是否需要登录权限。需要的话,同时当前应用不存在token,则跳转到登录页面,进行登录。登录成功后跳转到目标路由。
登录拦截到这里就结束了吗?并没有。这种方式只是简单的前端路由控制,并不能真正阻止用户访问需要登录权限的路由。还有一种情况便是:当前token失效了,但是token依然保存在本地。这时候你去访问需要登录权限的路由时,实际上应该让用户重新登录。
这时候就需要结合 http 拦截器 + 后端接口返回的http 状态码来判断。(具体方法见下一题目)
axios+vue如何在前端实现登录拦截?(路由拦截、http拦截)
登录流程控制中,根据本地是否存在token判断用户的登录情况,但是即使token存在,也有可能token是过期的,所以在每次的请求头中携带token,后台根据携带的token判断用户的登录情况,并返回给我们对应的状态码,而后我们可以在响应拦截器中,根据状态码进行一些统一的操作。
// 先导入vuex,因为我们要使用到里面的状态对象
// vuex的路径根据自己的路径去写
import store from '@/store/index';
// 请求拦截器axios.interceptors.request.use( //axios的一个方法,用于请求之前拦截
config => {
// 每次发送请求之前判断vuex中是否存在token
// 如果存在,则统一在http请求的header都加上token,这样后台根据token判断你的登录情况
// 即使本地存在token,也有可能token是过期的,所以在响应拦截器中要对返回状态进行判断
const token = store.state.token;
token && (config.headers.Authorization = token); (Authorization是后端自定义的名字) // 判断vuex中是否存在token,如果存在的话,则在每个http请求时header都加上token
return config; //再把这个请求发送给我们的后台
},
error => {
return Promise.error(error); //如果有错误直接返回报错的相关内容
})
如果后台传递过来的状态码是失效状态,就需要在response响应拦截器中清除本地token和清空vuex中token对象,通过commit触发mutations方法来同步更改vuex中的状态,清空token之后跳转到登录页面,让用户重新登录。
// 清除token
localStorage.removeItem('token');
store.commit('loginSuccess', null);
// 跳转登录页面,并将要浏览的页面fullPath传过去,登录成功后跳转需要访问的页面
setTimeout(() => {
router.replace({
path: '/login',
query: {
redirect: router.currentRoute.fullPath
}
});
}, 1000);
完整方法见:vue中Axios的封装和API接口的管理
https://juejin.im/post/5b55c118f265da0f6f1aa354
vue+axios 前端实现登录拦截(路由拦截、http拦截)
https://www.cnblogs.com/guoxianglei/p/7084506.html
一个axios的简单教程
https://www.jianshu.com/p/13cf01cdb81f
axios与fetch、ajax区别?
(1)fetch和axios差不多,都是基于promise的,fetch没有封装xhr(ajax的原生对象),是新的语法,默认不传cookie,也监听不到请求的进度,axios可以监听到请求的进度。
(2)axios返回一个promise对象,拿到对象之后再去.then可以拿到resolve之后的内容。
axios.get(url,{params:{}}).then(res=>{}).catch(err=>{});
axios.post(url,{}).then(res=>).catch(err=>{});
(3)ajax主要是利用callback回调函数的形式。
JQuery中的ajax:$.ajax({url,data,success(){}}) 在回调函数获取数据
axios 和 ajax 的使用方法基本一样,只有个别参数不同;
axios({
url: 'http://jsonplaceholder.typicode.com/users',
method: 'get',
responseType: 'json', // 默认的
data: {
//'a': 1,
//'b': 2,
}
}).then(function (response) {
console.log(response);
console.log(response.data);
}).catch(function (error) {
console.log(error);
})
$.ajax({
url: 'http://jsonplaceholder.typicode.com/users',
type: 'get',
dataType: 'json',
data: {
//'a': 1,
//'b': 2,
},
success: function (response) {
console.log(response);
}
})
参考:ajax和axios、fetch的区别
https://www.jianshu.com/p/8bc48f8fde75
JSON
什么是 JSON ?
- JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation)
- JSON 是轻量级的文本数据交换格式
- JSON 独立于语言:JSON 使用 Javascript语法来描述数据对象,但是 JSON 仍然独立于语言和平台。JSON 解析器和 JSON 库支持许多不同的编程语言。 目前非常多的动态(PHP,JSP,.NET)编程语言都支持JSON。
- JSON 具有自我描述性,更易理解
与 XML 相同之处
- JSON 是纯文本
- JSON 具有"自我描述性"(人类可读)
- JSON 具有层级结构(值中存在值)
- JSON 可通过 JavaScript 进行解析
- JSON 数据可使用 AJAX 进行传输
与 XML 不同之处
- 没有结束标签
- 更短
- 读写的速度更快
- 能够使用内建的 JavaScript eval() 方法进行解析
- 使用数组
- 不使用保留字
为什么使用 JSON?
对于 AJAX 应用程序来说,JSON 比 XML 更快更易使用:
使用 XML
- 读取 XML 文档
- 使用 XML DOM 来循环遍历文档
- 读取值并存储在变量中
使用 JSON
- 读取 JSON 字符串
- 用 eval() 处理 JSON 字符串
js中想要把json字符串转化为js对象的方式
JSON.parse()
eval()
假设我们有一个json字符串
var str = '{"friends":[{"name":"梅梅","age":29,"sex":"女"},' +
'{"name":"李华","age":18,"sex":"男"}]' +
'}';
使用 JSON.parse()方法来转化
var str = '{"friends":[{"name":"梅梅","age":29,"sex":"女"},' +
'{"name":"李华","age":18,"sex":"男"}]' +
'}';
JSON.parse(str);
使用 eval()方法传化:
var str = '{"friends":[{"name":"梅梅","age":29,"sex":"女"},' +
'{"name":"李华","age":18,"sex":"男"}]' +
'}';
eval('('+str+')');
注意点: 通过 eval 来转化,如果返回的字符串内容是一个数组,可以直接转化,如果返回的字符串内容是一个对象,必须在字符串的前后加上()
当字符串内容是一个数组时 ,eval()的转化方式:
var arr = '[{"name":"唐老鸭","sex":"男"},{"name":"红太狼","sex":"女"}]';
eval(arr);
JSON.parse() 和 eval() 的区别
eval方法不会去检查给的字符串是否符合json的格式,而JSON.parse解析不满足json格式的字符串时,会报错。
如果给的字符串中存在js代码eval也会一并执行,比如下面的代码段:
var str1 = '{"log":alert("我被会执行的")}';
eval("("+str1+")");
执行该代码片段,会将 alert 语句作为js代码来执行,如果我们在开发中建议使用JSON.parse来转化,这样可以避免很多隐患,比如,我们访问第三方提供的接口,返回的串中包含 window.location.href这样的内容,那么执行了就会跳转到不明网站,好危险,所以最好还是使用JSON.parse()来解析。
JSON.parse()
JSON 通常用于与服务端交换数据。
在接收服务器数据时一般是字符串。
我们可以使用 JSON.parse() 方法将数据转换为 JavaScript 对象。
JSON.parse(text[, reviver])
JSON.stringify()
JSON 通常用于与服务端交换数据。
在向服务器发送数据时一般是字符串。
我们可以使用 JSON.stringify() 方法将 JavaScript 对象转换为字符串。
JSON.stringify(value[, replacer[, space]])
Promise
同步异步函数的区别
同步:阻塞,当前程序必须等前面一个程序执行完毕以后,才能够执行。
异步:非阻塞,前一个程序是否执行完毕,不影响后面程序的执行。
promise、async、await
async函数返回的是一个 Promise 对象,可以使用 then 方法添加回调函数,async 函数内部 return 语句返回的值,会成为 then 方法回调函数的参数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
await只能用在异步函数中,使用await必须要先使用async,命令后面返回的是Promise对象,运行结果可能是rejected,所以最好把await命令放在try...catch代码块中。
Promise的概念
我理解的Promise就是一套为处理异步情况的方法。先创建一个promise对象来注册一个委托,其中包括委托成功及失败后的处理函数。然后基于这种表述方式,来将promise应用到各种异步处理的情况中。
promise写法案例
var promise = getAsyncPromise('fileA.txt');
promise.then( function(result){
// 成功时的处理办法
}).catch( function(error){
// 失败时的处理办法
})
// 返回一个promise对象
promise三个状态
pending(进行中)、fulfilled(已成功)和rejected(已失败)
Promise 实际就是一个对象, 从它可以获得异步操作的消息,Promise 对象有三种状态,pending(进行中)、fulfilled(已成功)和rejected(已失败)。Promise 的状态一旦改变之后,就不会在发生任何变化,将回调函数变成了链式调用。
Promise的设计思想是,所有异步任务都返回一个Promise实例。Promise实例有一个then方法,用来指定下一步的回调函数。
总的来说,传统的回调函数写法使得代码混成一团,变得横向发展而不是向下发展。Promise就是解决这个问题,使得异步流程可以写成同步流程。
Promise构造函数
JavaScript 提供原生的Promise
构造函数,用来生成 Promise 实例。
var promise = new Promise(function (resolve, reject) {
// ...
if (/* 异步操作成功 */){
resolve(value);
} else { /* 异步操作失败 */
reject(new Error());
}
});
上面代码中,Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由JavaScript引擎提供,不用自己实现。
resolve函数的作用是,将Promise实例的状态从"未完成"变为"成功"(即从pending变为fulfilled),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去。reject函数的作用是,将Promise实例的状态从"未完成"变为"失败"(即从pending变为rejected),在异步失败时调用,并将异步操作报出的错误,作为参数传递出去。
Promise的优缺点
Promise 的优点在于,让回调函数变成了规范的链式写法,程序流程可以看得很清楚。它有一整套接口,可以实现许多强大的功能,比如同时执行多个异步操作,等到它们的状态都改变以后,再执行一个回调函数;再比如,为多个回调函数中抛出的错误,统一指定处理方法等等。
而且,Promise 还有一个传统写法没有的好处:它的状态一旦改变,无论何时查询,都能得到这个状态。这意味着,无论何时为 Promise 实例添加回调函数,该函数都能正确执行。所以,你不用担心是否错过了某个事件或信号。如果是传统写法,通过监听事件来执行回调函数,一旦错过了事件,再添加回调函数是不会执行的。
Promise 的缺点是,编写的难度比传统写法高,而且阅读代码也不是一眼可以看懂。你只会看到一堆then
,必须自己在then
的回调函数里面理清逻辑。
Promise的优点
promise优点主要解决回调地狱问题,使得原本的多层级的嵌套代码,变成了链式调用,让代码更清晰,减少嵌套数。
Promise的缺点
首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
Promise的all方法
all方法是promise是类上自带的方法,并发读取,失败一个都失败了,时间只是一个读取的时间
第一个参数 传递的是数组,数组装的是一个个promise对象
调用后会再次返回一个promise实例
最好的写法
Promise.all([read('./name.txt'),read('./age.txt')]).then(([name,age])=>{
//data就是promise执行成功的结果类型是数组
console.log({name,age});
}).catch((err)=>{
console.log(err)
})
Promise的race方法
Promise.race ([promise1,promise2..])
当参数里的任意一个promise成功或失败后,该函数就会返回,并使用这个promise对象的值进行resolve或reject
let promise1 = ajax({method:'GET',url:'/x.json'});
let promise2 = ajax({method:'GET',url:'/y.json'});
Promise.all([promise1,promise2]).then(function(){
console.log('两个promise都执行完成了')
});
Promise.race([promise1,promise2]).then(function(){
console.log('有一个promise先执行完成了')
})
参考:简单理解Promise
https://www.jianshu.com/p/c8f9fba03df9
存储
本地存储(Local Storage )和cookies(储存在用户本地终端上的数据)之间的区别是什么?
Cookies:服务器和客户端都可以访问;大小只有4KB左右;有有效期,过期后将会删除;
本地存储:只有本地浏览器端可访问数据,服务器不能访问本地存储,直到故意通过POST或者GET的通道发送到服务器;每个域5MB;没有过期数据,它将保留直到用户从浏览器清除或者使用Javascript代码移除。
cookie 4kb 随http请求发送到服务端 后端可以帮助前端设置cookie
session 放在服务端 一般存放用户比较重要的信息
(token 令牌 token ==> cookie /localstorage) vuex
localStorage 本地存储(h5的新特性 draggable canvas svg)
5M 纯粹在本地客户端 多个标签页共享数据
sessionStorage 会话级别的存储
往本地页面中存值的方法(localStorage.setItem(key,value))
讲一下cookie、sessionstorage、localstorage
相同点:都存储在客户端
不同点:
1.存储大小
- cookie数据大小不能超过4k。
- sessionStorage和localStorage 虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大。
2.有效时间
- localStorage 存储持久数据,浏览器关闭后数据不丢失除非主动删除数据;
- sessionStorage 数据在当前浏览器窗口关闭后自动删除。
- cookie 设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭
3.数据与服务器之间的交互方式
- cookie的数据会自动的传递到服务器,服务器端也可以写cookie到客户端
- sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存。
定义一个对象,里面包含用户名、电话,然后将其存入localStorage的代码
var json = {username:"张三",phone:17650246248}
for(var key in json){
localStorage.setItem(key,json[key]);
}
cookie和session的区别
保持状态:cookie保存在浏览器端,session保存在服务器端
使用方式:如果不在浏览器中设置过期时间,cookie被保存在内存中,生命周期随浏览器的关闭而结束,这种cookie简称会话cookie。如果在浏览器中设置了cookie的过期时间,cookie被保存在硬盘中,关闭浏览器后,cookie数据仍然存在,直到过期时间结束才消失。
存储内容:cookie只能保存字符串类型,以文本的方式;session通过类似与Hashtable的数据结构来保存,能支持任何类型的对象(session中可含有多个对象)
存储的大小:cookie:单个cookie保存的数据不能超过4kb;session大小没有限制。
安全性:cookie:针对cookie所存在的攻击:Cookie欺骗,Cookie截获;session的安全性大于cookie。
应用场景:
cookie:(1)判断用户是否登陆过网站,以便下次登录时能够实现自动登录(或者记住密码)。如果我们删除cookie,则每次登录必须从新填写登录的相关信息。
(2)保存上次登录的时间等信息。
(3)保存上次查看的页面
(4)浏览计数
session:Session用于保存每个用户的专用信息,变量的值保存在服务器端,通过SessionID来区分不同的客户。
(1)网上商城中的购物车
(2)保存用户登录信息
(3)将某些数据放入session中,供同一用户的不同页面使用
(4)防止用户非法登录
-
缺点:
cookie:
(1)大小受限
(2)用户可以操作(禁用)cookie,使功能受限
(3)安全性较低
(4)有些状态不可能保存在客户端。
(5)每次访问都要传送cookie给服务器,浪费带宽。
(6)cookie数据有路径(path)的概念,可以限制cookie只属于某个路径。
session:
(1)Session保存的东西越多,就越占用服务器内存,对于用户在线人数较多的网站,服务器的内存压力会比较大。
(2)依赖于cookie(sessionID保存在cookie),如果禁用cookie,则要使用URL重写,不安全
(3)创建Session变量有很大的随意性,可随时调用,不需要开发者做精确地处理,所以,过度使用session变量将会导致代码不可读而且不好维护。
WebStorage
WebStorage的目的是克服由cookie所带来的一些限制,当数据需要被严格控制在客户端时,不需要持续的将数据发回服务器。
WebStorage两个主要目标:
(1)提供一种在cookie之外存储会话数据的路径。
(2)提供一种存储大量可以跨会话存在的数据的机制。
Localstroage与SessionStorage存储的区别
HTML5的WebStorage提供了两种API:localStorage(本地存储)和sessionStorage(会话存储)。
- 生命周期:localStorage:localStorage的生命周期是永久的,关闭页面或浏览器之后localStorage中的数据也不会消失。
localStorage除非主动删除数据,否则数据永远不会消失。
sessionStorage的生命周期是在仅在当前会话下有效。sessionStorage引入了一个“浏览器窗口”的概念,sessionStorage是在同源的窗口中始终存在的数据。只要这个浏览器窗口没有关闭,即使刷新页面或者进入同源另一个页面,数据依然存在。但是sessionStorage在关闭了浏览器窗口后就会被销毁。同时独立的打开同一个窗口同一个页面,sessionStorage也是不一样的。
存储大小:localStorage和sessionStorage的存储数据大小一般都是:5MB
存储位置:localStorage和sessionStorage都保存在客户端,不与服务器进行交互通信。
存储内容类型:localStorage和sessionStorage只能存储字符串类型,对于复杂的对象可以使用ECMAScript提供的JSON对象的stringify和parse来处理
获取方式:localStorage:window.localStorage;;sessionStorage:window.sessionStorage;。
应用场景:localStoragese:常用于长期登录(+判断用户是否已登录),适合长期保存在本地的数据。sessionStorage:敏感账号一次性登录;
WebStorage的优点
存储空间更大:cookie为4KB,而WebStorage是5MB;
节省网络流量:WebStorage不会传送到服务器,存储在本地的数据可以直接获取,也不会像cookie一样美词请求都会传送到服务器,所以减少了客户端和服务器端的交互,节省了网络流量;
对于那种只需要在用户浏览一组页面期间保存而关闭浏览器后就可以丢弃的数据,sessionStorage会非常方便;
快速显示:有的数据存储在WebStorage上,再加上浏览器本身的缓存。获取数据时可以从本地获取会比从服务器端获取快得多,所以速度更快;
安全性:WebStorage不会随着HTTP header发送到服务器端,所以安全性相对于cookie来说比较高一些,不会担心截获,但是仍然存在伪造问题;
WebStorage提供了一些方法,数据操作比cookie方便;
WebStorage的方法
setItem (key, value) —— 保存数据,以键值对的方式储存信息。
getItem (key) —— 获取数据,将键值传入,即可获取到对应的value值。
removeItem (key) —— 删除单个数据,根据键值移除对应的信息。
clear () —— 删除所有的数据
key (index) —— 获取某个索引的key
Token
token是"令牌"的意思,服务端生成一串字符串,作为客户端请求的一段标识。用户登录的时候生成一个token,并将token返回客户端,客户端将收到的token放在cookie里,下次用户向服务端发送请求的时候,服务端只需要对比token。
作用:进行身份验证,避免表单重复提交。
在ajax请求后台时token添加到哪里
在ajax请求的标头中加Token
1 function GetDateForServiceCustomer(userId) {
2 $.ajax({
3 url: 'http://*******/api/orders',
4 data: {
5 currUserId: userId,
6 type: 1
7 },
8 beforeSend: function(request) {
9 request.setRequestHeader("Authorization", token);
10 },
11 dataType: 'JSON',
12 async: false,//请求是否异步,默认为异步
13 type: 'GET',
14 success: function (list) {
15 },
16 error: function () {
17 }
18 });
19 }
参考:在ajax请求后台时在请求标头RequestHeader加token
https://www.cnblogs.com/zfdcp-028/p/6374632.html
协议
HTTPS与HTTP的一些区别
- HTTPS协议需要到CA申请证书,一般免费证书很少,需要交费。
- HTTP协议运行在TCP之上,所有传输的内容都是明文,HTTPS运行在SSL/TLS之上,SSL/TLS运行在TCP之上,所有传输的内容都经过加密的。
- HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
- HTTPS可以有效的防止运营商劫持,解决了防劫持的一个大问题。
HTTP1.0和HTTP2.0有什么区别
新的二进制格式(Binary Format),HTTP1.x的解析是基于文本。基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认0和1的组合。基于这种考虑HTTP2.0的协议解析决定采用二进制格式,实现方便且健壮。
多路复用(MultiPlexing),即连接共享,即每一个request都是是用作连接共享机制的。一个request对应一个id,这样一个连接上可以有多个request,每个连接的request可以随机的混杂在一起,接收方可以根据request的 id将request再归属到各自不同的服务端请求里面。
header压缩,如上文中所言,对前面提到过HTTP1.x的header带有大量信息,而且每次都要重复发送,HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小。
服务端推送(server push),同SPDY一样,HTTP2.0也具有server push功能。
参考:HTTP1.0、HTTP1.1 和 HTTP2.0 的区别
https://www.cnblogs.com/heluan/p/8620312.html
Websocket
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。
HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
WebSocket特点
(1)建立在 TCP 协议之上,服务器端的实现比较容易。
(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
(3)数据格式比较轻量,性能开销小,通信高效。
(4)可以发送文本,也可以发送二进制数据。
(5)没有同源限制,客户端可以与任意服务器通信。
(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
scoket套接字编程
网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。
如果将http比作轿车的话,那么socket就相当于发动机。
websocket跟 socket 的区别
软件通信有七层结构,下三层结构偏向与数据通信,上三层更偏向于数据处理,中间的传输层则是连接上三层与下三层之间的桥梁,每一层都做不同的工作,上层协议依赖与下层协议。基于这个通信结构的概念。
Socket 其实并不是一个协议,是应用层与 TCP/IP 协议族通信的中间软件抽象层,它是一组接口。当两台主机通信时,让 Socket 去组织数据,以符合指定的协议。TCP 连接则更依靠于底层的 IP 协议,IP 协议的连接则依赖于链路层等更低层次。
WebSocket 则是一个典型的应用层协议。
总的来说:Socket 是传输控制层协议,WebSocket 是应用层协议。
webScoket如何兼容低浏览器
- Adobe Flash Socket
- ActiveX HTMLFile (IE)
- 基于 multipart 编码发送 XHR
- 基于长轮询的 XHR
参考:WebSocket解释及如何兼容低版本浏览器
https://www.cnblogs.com/pengc/p/8718380.html
模块化开发
模块、函数、组件分别是什么?
模块:在webpack中,通过import引入的文件叫做模块。(js/css/png)
函数:是一些功能的合集。
组件:指的是页面的某一部分。
类是什么?类被编译成什么?
类:
class Banner extends React.Component{ //es6写法
}
ES5:通过模块定义类 ES6中定义的类也被编译成这样
function Banner(){ //构造函数
}
组件化与模块化的区别,或者说怎么实现组件化开发,模块化开发
组件化:针对的是页面中的整个完整的功能模块,划分成浏览器可以识别的每个模块,例如头部Header、底部Footer、Banner。优点:代码复用、便于维护。
模块化:就是系统功能分离或独立的功能部分的方法,一般指的是单一的某个东西,例如:js、css
模块化开发的规范
(1)commonJS:自上而下同步进行 会阻塞 用在服务端
使用模块:const 变量名 = require("包名字")
声明模块:module.exports = {
对外的名字:函数
}
(2)AMD:异步加载文件 不会阻塞 用在浏览器端
define方法用于定义模块,RequireJS要求每个模块放在一个单独的文件里。
使用模块:require("模块名",function(模块对象)){
return 模块对象.函数();//回调函数
}
(3)CMD规范 中国人发明的,ECMA6来了就废弃了。
amd和cmd的区别
amd是require.js上的一个规范,cmd是sea.js的一个规范。
其实CMD与AMD规范并没什么本质的区别,区别在于他们对依赖模块的执行时机处理不同。虽然两者都是异步加载模块,但是AMD依赖前置,js可以方便知道依赖模块是谁,要依赖什么js那就先加载进来,至于你要依赖这些js来干吗得先等着,等我加载完了资源再商量;而CMD就近依赖,需要使用这个依赖模块时,我再加载进来用。
这就好比什么呢?就好像我今晚要看5集三国演义。AMD是先打开五个窗口,分别是1~5集,都缓冲着先,反正等下每集我都要看的;CMD则是先打开第一集的窗口,等到我第一集看完了,想看第二集了,就再跳转到第二集。
现在使用频率最高的,也是大家公认的好的模块化规范,是CommonJS。 后端(node.js)
require.js
实现了AMD规范的JavaScript工具库
RequireJS的基本思想是,通过define方法,将代码定义为模块;通过require方法,实现代码的模块加载。
require.js的诞生,就是为了解决这两个问题:
(1)实现了js文件的异步加载,避免网页失去响应
(2)管理模块之间的依赖性,便于代码的编写和维护
common.js
CommonJS就很简单了,一个js文件要输出去,只需使用module.export={xxx:你要输出的内容},而在另外一个js中,你要引用什么,就通过var xxxx=require("xxxx")引用进来就行了,这玩意并不是异步加载模块,而是同步一次性加载出来。
common.js与require.js与sea.js的区别:
配置模块化开发
<1>在头部引入
<script scr = "js/require.js" defer async = "true" data-main = "js/main"></script>
main.js管理当前页面的js文件,每一个页面都有自己的不同名字的main.js文件.
路径以dist文件为主
在外部修改文件,不允许修改dist中的文件,gulp会自动同步修改.
<2>在main中通过require.config配置当前页面需要的模块路径
配置模块的依赖路径:
shim:{
//由于jquery-cookie是基于jquery库封装的,所以要先引入jquery再引入jquery-cookie
"jquery-cookie":['jquery'],
//声明一下,不是AMD规范的模块
"parabola":{
exports:"_"
}
}
<3>编写CSS样式/JS代码
编写JS代码:
先封装成函数
再通过对象的形式对外暴露
define(["jquery"],function($){
function slide(){
$(function(){
function show(){
console.log("hello world");
}
return{
slide:slide,
show:show
}
})
<4>在main.js中加载
//配置当前整个项目所有模块的路径
require.config({
paths:{
"slide":"slide"
},
require(["slide"],function(slide){
slide.show();
slide.slide();
})