一、js实现冒泡排序
// 封装成函数
function bubble(arr){
if(arr instanceof Array && arr.length > 1){
//外层循环,控制趟数,每一次找到一个最大值
for (var i = 0; i < arr.length - 1; i++) {
// 内层循环,控制比较的次数,并且判断两个数的大小
for (var j = 0; j < arr.length - 1 - i; j++) {
// 如果前面的数大,放到后面(从小到大的冒泡排序)
if (arr[j] > arr[j + 1]) {
var temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr; // 将执行完的结果返回就可以
}
}
var arr = [3,5,1,2,9,8,4,5,3,4];
console.log(bubble(arr)); // [1, 2, 3, 3, 4, 4, 5, 5, 8, 9]
二、js定义变量有几种方式?有什么区别?
1.js定义变量的方式:var、let和const
2.var、let和const的区别:
①var声明的变量会挂载在window上,而let和const声明的变量不会
var a = 100;
console.log(a,window.a); // 100 100
let b = 10;
console.log(b,window.b); // 10 undefined
const c = 1;
console.log(c,window.c); // 1 undefined
②var声明变量存在变量提升,let和const不存在变量提升
console.log(a); // undefined ===> a已声明还没赋值,默认得到undefined值
var a = 100;
console.log(b); // 报错:b is not defined ===> 找不到b这个变量
let b = 10;
console.log(c); // 报错:c is not defined ===> 找不到c这个变量
const c = 10;
③let和const声明形成块作用域,而var声明的变量作用域为函数作用域或全局作用域
if(1){
var a = 100;
let b = 10;
const c = 1;
}
console.log(a); // 100
console.log(b) // 报错:b is not defined ===> 找不到b这个变量
console.log(c) // 报错:c is not defined ===> 找不到c这个变量
④同一作用域下let和const不能声明同名变量,而var可以
var a = 100;
console.log(a); // 100
var a = 10;
console.log(a); // 10
let a = 100;
let a = 10;
// 控制台报错:Identifier 'a' has already been declared ===> 标识符a已经被声明了。
⑤const一旦声明必须赋值,不能使用null占位;声明后不能再修改;如果声明的是复合类型数据,可以修改其属性
const a;//报错
const a = 100; //一旦声明必须赋值,不能使用null占位
const a = 100;
a = 10;//报错,声明后不能再修改
const obj = {a:100};
obj.name = 'apple';
obj.a = 10000;
console.log(obj); // {a:10000,name:'apple'},如果声明的是复合类型数据,可以修改其属性
⑥暂存死区
var a = 100;
if(1){
a = 10;
//在当前块作用域中存在a使用let/const声明的情况下,给a赋值10时,只会在当前作用域找变量a,
// 而这时,还未到声明时候,所以控制台Error:a is not defined
let a = 1;
}
三、闭包?
- 各种专业文献的闭包定义都非常抽象,我的理解是:闭包就是能够读取其他函数内部变量的函数。由于在javascript中,只有函数内部的子函数才能读取局部变量,所以说,闭包可以简单理解成“定义在一个函数内部的函数”。
- 所以,在本质上,闭包是将函数内部和函数外部连接起来的桥梁。
- 闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中,不会在父方法调用后被自动清除。
闭包的使用场景:
①return一个函数
function a(){
var m=3 ;
function b(){
console.log(m);
console.log('string');
};
return b;
}
var result=a();
result(); //3 'string'
②函数作为参数
var a = '林一一'
function foo(){
var a = 'foo'
function fo(){
console.log(a)
}
return fo
}
function f(p){
var a = 'f'
p()
}
f(foo())
/* 输出
* foo
* 使用 return fo 返回回来,fo() 就是闭包,f(foo()) 执行的参数就是函数 fo,因为 fo() 中的 a 的上级作用域就是函数foo(),所以输出就是foo
/
③所有回调函数都是闭包
window.name = '林一一'
setTimeout(function timeHandler(){
console.log(window.name);
}, 100)
四、原型链?如果要给对象添加一个自定义方法属性/方法怎么实现?
1.原型
- 每个对象都有_proto_属性,并且指向它的原型对象
- 每个构造函数都有它的prototype原型对象
- prototype原型对象里的constructor指向它的构造函数
- new一个构造函数会形成它的实例对象
通过代码来看:
function Person(name,age){
this.name=name;
this.age=age;
}
var p1=new Person('小满',18);
var p2=new Person('大雪',20);
console.log(p1,p2)
console.log(Person.prototype);
console.log(p1.__proto__);
console.log(p1.__proto__===Person.prototype);
console.log(p2.__proto__===Person.prototype);
console.log(p1.__proto__===p2.__proto__);
console.log(p1.__proto__.constructor);
console.log(Person.prototype.constructor);
2.原型链
- 概念:每个对象都可以有一个原型,这个原型还可以有它自己的原型,以此类推,形成一个原型链。
- 什么时候用:当我们查找特定属性的时候,我们先去这个对象里去找,如果没有的话就去它的原型对象里面去,如果还是没有的话再去原型对象的原型对象里去寻找…这个操作就是被委托在整个原型链上。
- 如果没有找到会一直找下去吗:原型链是有终点的,不会一直找下去。当
Object.prototype.__proto__ === null
时,查找结束,返回undefined。
3.利用prototype给对象添加一个自定义方法属性/方法
Array.prototype.duplicator = function() {
let s = this.concat(this);
return s;
}
let t = [1,2,3,4,5].duplicator();
console.log(t);// [1,2,3,4,5,1,2,3,4,5]
五、tcp三次握手?知道tcp长连接吗?抓过包吗?QQ发消息用的是什么协议?
1.三次握手
1)第一次握手:建立连接时,客户端发送syn包(syn=x)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编(Synchronize
Sequence Numbers)。
2)第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;
3)第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
拓展:四次挥手
1)客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
2)服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
3)客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
4)服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
5)客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
6)服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
2.长连接与短连接
1)长连接:
- client方与server方先建立连接,连接建立后不断开,然后再进行报文发送和接收。
- 这种方式下由于通讯连接一直存在。此种方式常用于P2P通信。
- 有些服务需要长时间连接到服务器,比如CMPP,一般需要自己做在线维持。
2)短连接:
- Client方与server每进行一次报文收发交易时才进行通讯连接,交易完毕后立即断开连接。
- 此方式常用于一点对多点通讯。C/S通信。
- 比如http的,只是连接、请求、关闭,过程时间较短,服务器若是一段时间内没有收到请求即可关闭连接。
3.抓过包,发现QQ主要使用的是UDP协议发送聊天消息。
六、计算机网络结构?
计算机网络体系结构分为3种:OSI体系结构(七层),TCP/IP体系结构(四层),五层体系结构。
- OSI体系结构: 概念清楚,理论也比较完整,但是它既复杂又不实用。
- TCP/IP体系结构:TCP/IP是一个四层体系结构,得到了广泛的运用。
- 五层体系结构:为了方便学习,折中OSI体系结构和TCP/IP体系结构,综合二者的优点,这样既简洁,又能将概念讲清楚。
七、HTTP协议了解吗?
1.简介:
- HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。
- HTTP是一个基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。HTTP使用面向连接的TCP作为传输层协议,HTTP本身无连接。
- HTTP是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。
2.http和https区别
HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure Sockets Layer)协议用于对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。简单来说,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。
HTTPS和HTTP的区别主要如下:
总的来说: HTTPS=SSL+HTTP
①https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
②http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
③http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。(这个只是默认端口不一样,实际上端口是可以改的)
④http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
3.http1.0 1.1 2.0的区别
(1)在http1.0中,当建立连接后,客户端发送一个请求,服务器端返回一个信息后就关闭连接,当浏览器下次请求的时候又要建立连接,显然这种不断建立连接的方式,会造成很多问题。
(2)在http1.1中,引入了持续连接的概念,通过这种连接,浏览器可以建立一个连接之后,发送请求并得到返回信息,然后继续发送请求再次等到返回信息,也就是说客户端可以连续发送多个请求,而不用等待每一个响应的到来。
(3)http2.0与http1.1相比,主要区别包括:
- http2.0采用二进制格式而非文本格式
- http2.0是完全多路复用的,而非有序并阻塞的——只需一个连接即可实现并行
- http2.0使用报头压缩,降低了开销
- http2.0让服务器可以将响应主动“推送”到客户端缓存中
4.http协议中有那些请求方式?
- GET:用于请求访问已经被URI(统一资源标识符)识别的资源,可以通过URL传参给服务器
- POST:用于传输信息给服务器,主要功能与GET方法类似,但一般推荐使用POST方式
- PUT:传输文件,报文主体中包含文件内容,保存到对应URI位置。
- DELETE:删除文件,与PUT方法相反,删除对应URI位置的文件。
- HEAD:获得报文首部,与GET方法类似,只是不返回报文主体,一般用于验证URI是否有效
- OPTIONS:查询相应URI支持的HTTP方法。
拓展:增删改查
(增查改删)
(CRUD)
(create-retrieve-update-delete)
(POST-GET-PUT-DELETE)
5.http常见状态码
- 200(成功)服务器已成功处理了请求。通常,这表示服务器提供了请求的网页。如果是对您的 robots.txt 文件显示此状态码,则表示Googlebot 已成功检索到该文件。
- 304(未修改)自从上次请求后,请求的网页未修改过。服务器返回此响应时,不会返回网页内容。
- 400(错误请求)服务器不理解请求的语法。
- 404(未找到)服务器找不到请求的网页。例如,对于服务器上不存在的网页经常会返回此代码。
- 500(服务器内部错误)服务器遇到错误,无法完成请求。
八、vue的生命周期?
Vue实例需要经过创建、初始化数据、编译模板、挂载DOM、渲染、更新、渲染、卸载等一系列过程,这个过程就是Vue的生命周期,Vue中提供的钩子函数有beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed。
九、computed和methods的区别?
(1)首先最明显的不同就是调用的时候,methods要加上();
(2)我们可以使用 methods 来替代computed,效果上两个都是一样的,但是 computed 是基于它的依赖缓存,只有相关依赖发生改变时才会重新取值;而使用methods ,在重新渲染的时候,函数总会重新调用执行。
拓展1:watch
watch侦听器是侦听一个特定的值,当该值变化时执行特定的函数。例如分页组件中,我们可以监听当前页码,当页码变化时执行对应的获取数据的函数。
拓展2:什么情况下分别使用computed、watch、methods
(1)数据量大,需要缓存的时候用computed;每次确实需要重新加载,不需要缓存时用methods。
(2)尽量用computed计算属性来监视数据的变化,因为它本身就这个特性,用watch没有computed“自动”,手动设置使代码变复杂。
(3)虽然计算属性在大多数情况下是非常适合的,但是在有些情况下我们需要自定义一个watcher,在数据变化时来执行异步操作,这时watch是非常有用的。
十、栈和队列的区别?在js中有对应吗?数组方法有哪些?
1.栈:后进先出;队列:先进先出
2.(1)JS为数组提供了方法可以实现类似入栈出栈功能:入栈push()、 出栈pop()
(2)JS为数组提供了方法可以实现队列功能:入队unshift()、 出队pop()
3.数组方法:
1)Array.length
返回或设置一个数组中的元素个数
设置 length 属性的值来截断任何数组
2)Array.from()
对伪数组或可迭代对象(包括arguments Array,Map,Set,String…)转换成数组对象
语法 Array.from(arrayLike, mapFn, thisArg)
- arrayLike:想要转换成数组的伪数组对象或可迭代对象。
- mapFn (可选参数):如果指定了该参数,新数组中的每个元素会执行该回调函数。
- thisArg (可选参数):可选参数,执行回调函数 mapFn 时 this对象。
- 返回值:一个新的数组实例
3)Array.isArray()
用于确定传递的值是否是一个 Array
Array.isArray([]) => true;
Array.isArray({}) => false;
4)Array.of()
Array.of(7); // [7]
Array.of(1, 2, 3); // [1, 2, 3]
Array(7); // [ , , , , , , ]
Array(1, 2, 3); // [1, 2, 3]
5)concat()
用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。
var arr1 = ['a', 'b', 'c'];
var arr2 = ['d', 'e', 'f'];
var arr3 = ['f'];
var arr4 = arr1.concat(arr2,arr3);
// arr4 is a new array [ "a", "b", "c", "d", "e", "f" ]
6)forEach()
方法对数组的每个元素执行一次提供的函数
array.forEach(callback(currentValue, index, array){
//do something
}, this)
7)indexOf()
返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1
let a = [2, 9, 7, 8, 9];
a.indexOf(2); // 0
a.indexOf(6); // -1
a.indexOf(7); // 2
a.indexOf(8); // 3
a.indexOf(9); // 1
if (a.indexOf(3) === -1) {
// 数组中不包含3
}
8)map()
创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果
9)pop()
从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度
let a = [1, 2, 3];
a.length; // 3
a.pop(); // 3
a.length; // 2
10)push()
将一个或多个元素添加到数组的末尾
11)toString()
返回一个字符串,表示指定的数组及其元素
12)unshift()
将一个或多个元素添加到数组的开头,并返回新数组的长度
13)reverse
方法将数组中元素的位置颠倒
14)shift()
从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度
15)slice
返回一个从开始到结束(不包括结束)选择的数组的一部分到一个新数组对象
16)some()
测试数组中的某些元素是否通过由提供的函数实现的测试。
const isBiggerThan10 = (element, index, array) => {
return element > 10;
}
[2, 5, 8, 1, 4].some(isBiggerThan10);
// false
[12, 5, 8, 1, 4].some(isBiggerThan10);
// true
17)sort()
对数组的元素进行排序,并返回数组
18)splice()
通过删除现有元素和/或添加新元素来更改一个数组的内容
19)join()
将数组(或一个类数组对象)的所有元素连接到一个字符串中
let a = ['Wind', 'Rain', 'Fire'];
a.join();
// 默认为 ","
// 'Wind,Rain,Fire'
20)toLocaleString()
返回一个字符串表示数组中的元素。数组中的元素将使用各自的 toLocaleString 方法转成字符串,这些字符串将使用一个特定语言环境的字符串(例如一个逗号 “,”)隔开
var number = 1337;
var date = new Date();
var myArr = [number, date, "foo"];
var str = myArr.toLocaleString();
console.log(str);
// 输出 "1337,2017/8/13 下午8:32:24,foo"
// 假定运行在中文(zh-CN)环境,北京时区
21)includes()
用来判断一个数组是否包含一个指定的值,返回 true或 false
let a = [1, 2, 3];
a.includes(2);
// true
a.includes(4);
// false
22)reduce()
累加器和数组中的每个元素(从左到右)应用一个函数
var total = [0, 1, 2, 3].reduce(function(sum, value) {
return sum + value;
}, 0);
// total is 6
23)lastIndexOf()
返回指定元素(也即有效的 JavaScript 值或变量)在数组中的最后一个的索引,如果不存在则返回 -1。从数组的后面向前查找第一个出现位置的索引
24)copyWithin(target, start, end)
浅复制数组的一部分到同一数组中的另一个位置
25)every(callback)
方法测试数组的所有元素是否都通过了指定函数的测试
26)fill()
用一个固定值填充一个数组中从起始索引到终止索引内的全部元素
// arr.fill(value, start, end)
ar numbers = [1, 2, 3]
numbers.fill(1);
// results in [1, 1, 1]
27)filter()
创建一个新数组, 其包含通过所提供函数实现的测试的所有元素
var arr= [1,10,20,30]
var brr = arr.filter((item)=>{
return item>10;
})
//[20,30]
28)find()
返回数组中满足提供的测试函数的第一个元素的值
function isBigEnough(element) {
return element >= 15;
}
[12, 5, 8, 130, 44].find(isBigEnough); // 130
29)findIndex()
返回数组中满足提供的测试函数的第一个元素的索引
function isBigEnough(element) {
return element >= 15;
}
[12, 5, 8, 130, 44].findIndex(isBigEnough);
//'3'
30)reduceRight()
接受一个函数作为累加器(accumulator)和数组的每个值(从右到左)将其减少为单个值。