1.js数据类型有哪些?两种类型的区别?
基本数据类型:String,Number,Boolean,Null,Undefined,Symbol
引用数据类型:Object,Array,Function,Data,Math,Map,Set,RegExp(正则表达式)
区别:两者的存放地址不同
基本数据类型存放在栈中,占用内存小,空间固定,且经常被使用。
引用数据类型存放在堆中,占用内存大,空间不固定;引用数据类型在栈中存放指针。指针指向堆
中该实体的起始地址。
2.数据类型的监测方法有哪些?
typeof(常用于判断基本数据类型,因为对象、数组和Null都返回的Object);
console.log(typeof '温情'); // string
console.log(typeof 111); // number
console.log(typeof true); // boolean
console.log(typeof undefined); // undefined
console.log(typeof function () { }); // function
console.log(typeof {}); // object
console.log(typeof [1,2,3]); // object
console.log(typeof null); // object
instanceof;
用于判断引用数据类型,检测构造函数的prototype属性是否出现在某个实例对象的原型链上,有
则返回true,否则返回false,就是判断对象属于什么类型;
let arr = [];
console.log(arr instanceof Array); // true
console.log(arr instanceof Object); // true
let obj = {};
console.log(obj instanceof Array); // false
console.log(obj instanceof Object); // true
let date = new Date();
console.log(date instanceof Date); // true
console.log(date instanceof Object); // true
console.log(date instanceof Array); // false
object.prototype.toString.call();
返回一个“[object XXX]”格式的字符串,XXX就是具体的数据类型
function getType(value) {
if(typeof value !== 'object') {
return typeof value;
} else {
return Object.prototype.toString.call(value).split(' ')[1].slice(0, -1);
}
};
console.log( getType() ); // undefined
console.log( getType(null) ); // Null
console.log( getType(123) ); // number
console.log( getType('温情') ); // string
console.log( getType({}) ); // Object
console.log( getType([]) ); // Array
console.log( getType(new Date) ); // Date
console.log( getType(new RegExp) ); // RegExp
console.log( getType(new Function) ); // function
3.null和undefined的区别?
两个数据类型都是基本数据类型,并且都有一个值,null和undefined。
undefined的意思是未定义,比如说只是声明了一个变量但是为定义,那么就是undefined,
null是一个空对象。
4.如何实现深拷贝和浅拷贝?
浅拷贝:
object.assign()的第一个参数是目标对象,后面的参数是源对象,这个方法会将目标对象
和源对象进行合并,如果目标对象和源对象中出现同一个键名的话,源对象中的值会覆盖掉目标对
象中的值。
扩展运算符,会将多个数组进行合并,并且同样的值也不会被覆盖掉。
深拷贝:
利用JSON.stringify():先利用JSON.stringify()将js对象转化为字符串,再利用JSON.parse()方
法将字符串还原为js对象赋值给一个新的对象,这样就完成了深拷贝。但是如果拷贝的对象当中有
函数,undefined,Symbol的话,数据就会消失。
利用函数库lodash里的_.cloneDeep()方法进行深拷贝
5.如何判断一个对象是一个空对象?
1)利用JSON.stringify() 方法:
if(JSON.stringify(obj)=='{}') 如果为真的话就返回空对象
(2)object.keys(obj)方法:
if(object.keys(obj).length<=0) 如果为真就是空对象
6.箭头函数与普通函数的区别?
箭头函数比普通函数更加简洁。
箭头函数没有自己的this,其this指向与外层函数的this指向相同。
箭头函数不能当作构造函数使用。
箭头函数没有prototype,arguments属性。
call(),apply(),bind()方法不能改变箭头函数中this的指向。
7.数组原生的方法?
数组转换为字符串:toString(),tolocalString(),join()
末尾操作:pop()删除最后一个元素,push()在末尾增加一个元素
首位操作:shift()删除第一个元素,unshift()在守卫增加一个元素
重排序和翻转数组:resverse(),sort()
合并数组:concat()
数组截取(浅拷贝)方法:slice(),不会改变原数组
数组修改:splice(),改变了原数组
数组归并方法:reduce()
8.forEach和map的区别?
forEach方法,数组中的每一个元素都会执行提供的函数,会改变原数组,没有返回值。
map方法,会返回一个新数组,不会改变原数组,新数组里面的值是原数组中的元素执行提供函数
所返回的值
9.for与forEach区别?
1.for循环可以使用break跳出循环,但forEach不能。
2.for循环可以控制循环起点(i初始化的数字决定循环的起点),forEach只能默认从索引0开
始
3.for循环过程中支持修改索引(修改 i),但forEach做不到(底层控制index自增,无法左右
它)
10.Promise的方法?
(1)then()方法:
可以接收两个回调函数作为参数,第一个回调函数当Promise对象的状态变为Resolved的时候调
用,第二个回调函数当Promise对象的状态变为Rejected的时候调用。第二个参数可以省略,
then()方法返回的是一个新的Promise实例,因此可以使用then()的链式调用。
(2)catch()方法:
可以接收一个回调函数作为参数,当Promise对象的状态变为rejected的时候调用,相当于then()方
法的第二个回调函数。
(3)all()方法:
可以接收一个数组作为参数,数组中的每一个元素都是一个Promise对象,只有当数组中的每一个
Promise对象的状态都变为Resolved的时候,all()的状态才会变为Resolved。只要有一个Promise
对象的状态是Rejected,all()的状态就是Rejected。
当所有的子Promise对象执行完,all()的状态为resolved的时候,返回值是一个数组,数组按顺序输
出每一个Promise对象resolve()的返回值。当有任何一个Promise失败,返回值就是第一个失败的
Promise对象的结果。
(4)race()方法:
race()方法和all()方法一样,接受的是一个数组,数组里面的元素都是Promise对象,与all()不同的
是,根据子Promise对象第一个返回的状态来决定race()方法的状态。如果第一个Promise的状态变
成了resolved,那么race()自身的转台就变成了resolved,反之则是rejected
(5)finally()方法:
不管最后的状态是什么,都要执行finally()方法。
11.对闭包的理解?
闭包就是指有权访问另一个函数作用域中变量的函数。创建闭包最常见的方式就是在一个函数中创
建另一个函数,创建的函数可以 访问当前函数中的变量。
优点:
可以重复使用变量,并且不会造成变量污染。
缺点:
会引起内存泄漏(解决:在退出函数之前,将不使用的局部变量全部删除)
12.对作用域和作用域链的理解?
作用域:
(1)全局作用域:
在最外面函数和最外面函数外层声明的变量可以称为全局变量。
被定义但是没有被声明的变量也被称为全局变量。
window身上的属性也被称为全局变量。
(2)局部作用域:
一般在函数内部声明的变量被称为局部变量,局部作用域是分层级的,内层的可以访问外层的,反之则不行。
(3)块级作用域:
ES6新增的let和const关键字,可以生命块级作用域,一般在一个代码块中使用。
let和const声明的变量没有变量提升,不可以被重复声明。
块级作用域一般在循环中使用,可以把循环的变量限制在循环内部。
作用域链:
在当前作用域里查找变量,如果查找不到的话就到父级作用域查找,还查找不到的话就依次向上一
级作用域查找变量,知道查找到window身上即可,这一层层的关系就叫做作用域链。
12.对象继承的方式有哪些?
(1)构造函数的继承(call/apply):call和apply可以改变某个函数的this指向,父类:Animal,子类:Cat,
在子类构造函数中Animal.call(this)
(2)原型继承:子类原型=父类实例 Cat.prototype=new Animal()
优点:可以继承 父类的所有 (自身+原型)
缺点:不可以 动态的给 父类 构造函数 传参数
<script>
// 父类
function Father(name, sex) {
this.name = name
this.sex = sex
}
Father.prototype.say = function() {
console.log('会说四川话了')
}
// 定义子类
function Son() {}
Son.prototype = new Father('王', '男')
var one = new Son()
console.log(one, one.name, one.sex)
one.say()
</script>
(3)类继承:extends,super(可以继承父类构造函数) class Son extends Father{}
(4)组合继承
14.ES6新特性有哪些?
(1)const/let/var
相同点:三者都可以用于声明变量
不同点:
let
特性:
1.let 不能重复声明变量,var 可以重复声明变量;
2.let 定义变量,可先声明,后赋值
3.使用let定义的边量存在块级作用域,只能在{ }中使用
const
特性:
1.const 用于定义常量 ,需要在声明的同时赋值
2.使用let定义的边量存在块级作用域,只能在{ }中使用
var
特性:
1.var定义的变量,在全局范围内都可以使用,但是容易造成全局变量的污染,用于全局作用
域,不推荐
2.全局作用域的理解:在全局范围内都可以使用
3.利用 var 所定义的变量,利用声明方式定义的函数,存在“声明提升”的现象(在 JS 引擎中
执行 JS 时的预解析过程)。在执行 JS 代码时,会先将所有使用 var 所定义的变量的声明部分提
升到对应作用域的最顶部(赋值部分仍保留在原位置)。对于函数,如果是使用声明方式定义的函
数(function 函数名(){} 这种方式),会将函数整体提升到对应作用域的最顶部,对于函数表达式
方式定义的函数,只提升声明部分,不提升函数主体。
(2)结构赋值
ES6允许按照一定模式从数组和对象中提取值,对变量进行赋值
数组的结构
const Web = ['苹果', '华为', '小米']
let [tool1, tool2, tool3] = Web
console.log('tool1-----', tool1) // 苹果
console.log('tool2-----', tool2) // 华为
console.log('tool3-----', tool3) // 小米
对象的结构
const liMing = {
name: 'liMing',
age: '22',
tell: function(){
console.log(`hello world`)
}
}
let {name, age, tell} = liMing
console.log(name) // 'liMing'
console.log(age) // '22'
console.log(tell) // f(){...}
tell() // hello world
(3)模板字符串
特性:
``(反引号)内容中可以直接出现换行符
可以使用${exprision}进行变量的拼接
<html>
<body>
<h2>JavaScript 模板字面量</h2>
<p>模板字面量允许字符串中的变量:</p>
<p id="demo"></p>
<p>Internet Explorer 不支持模板字面量。</p>
<script>
let firstName = "Bill";
let lastName = "Gates";
let text = `Welcome ${firstName}, ${lastName}!`;
document.getElementById("demo").innerHTML = text;
</script>
</body>
</html>
(4)简化对象的写法
ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法(在属性名和变量名相同
的情况下),这样的书写更加简洁
let name = 'LiMing'
let tell = function(){
console.log('I am LiMing')
}
const liMing = {
name,
tell,
sayHi(){
console.log('hello')
}
}
// 等效于
// const liMing = {
// name: name,
// tell: tell,
// sayHi: function(){
// console.log('hello')
// }
// }
(5)箭头函数
是普通函数的简写形式,没有自己的 this、arguments、super、new.target 等
箭头函数与普通函数的区别:
1.箭头函数没有自己的this,在普通函数中,this总是指向调用它的对象
2.箭头函数不用用于构造函数
let Person = (name, age)=>{
this.name = name
this.age = age
}
let meInfo = new Person('xiaojian', 18) // 报错:Uncaught TypeError: Person is not a constructor
3.箭头函数不能使用arguments变量,但是可以使用....rest
let fn = ()=>{
console.log(arguments)
}
fn(1, 2, 3) // 报错:Uncaught ReferenceError: arguments is not defined
let fn2 = (...rest)=>{
console.log(...rest)
}
fn2('a','b','c') // a b c
4.箭头函数不具有prototype原型对象,不具有super,不具有new.target
5.箭头函数的简写形式
当形参有且只有一个的时候,可以省略();
当代码体只有一条语句的时候,可以省略{},此时return必须省略,而且语句的执行结果就是
函数的返回值;
// 当只有一个参数时,圆括号是可选的:
const abc = functiong(a){
return a*a
}
// ==>
const abc a => {
return a*a
}
const add = function(a, b) {
return a + b
}
// ==>
const add = (a, b) => {
return a + b
}
// ==>
const add = (a, b) => a + b
(6)函数参数的默认值设置
ES6允许给函数参数赋初始值
function add(a, b, c=10){ // 具有默认值的参数,一般位置要靠后
return a + b + c
}
console.log(add(1,2,))
(7)rest参数
ES6引入rest参数,用于获取函数的实参,用来代替arguments
// ES5获取实参的方式
function printStudent(){
console.log(arguments) // arguments为一个对象
}
printStudent('xiaojian','xiaohua')
// ES6获取实参的方式
function printFriend(friend1, friend2, ...rest){ // rest参数必须放在形参列表最后,否则会报错
console.log(friend1)
console.log(friend2)
console.log(rest) // 得到一个数组,可以使用数组api
}
printFriend('小猫','小狗','兔子','鸭子')
// 小猫
// 小狗
// ['兔子','鸭子']
(8)展开运算符
...
能将「数组」转为逗号分隔的「参数序列」
说明:虽然形式与rest参数类似,但是rest参数是用在函数定义时的形参位置,扩展运算符是用在
函数实际调用时的实参位置
const STUDENTS = ['小明','小芳','小红']
function printStudent(){
console.log(arguments)
}
printStudent(STUDENTS) // 参数为一个数组,数组内包含3个元素
printStudent(...STUDENTS) // 参数为3个元素
(9)数组方法
常用数组方法如下:
1)indexOf(searchElement):查找数组中 searchElement 元素第一次出现的索引(下标),
如果不存在返回 -1 ;
2) lastIndexOf(search):查找数组中 search 元素最后一次出现的索引,如果不存在返回 -1;
3) forEach(callback) -- 遍历迭代数组 的每个元素,在遍历每个元素时,调用 callback 执行函数体
语句块。没有返回值
4) map(callback) -- 遍历迭代数组的每个元素,返回一个新数组,新数组中的元素值是 callback 函
数中的返回值
5) filter(callback) -- 过滤筛选数组元素,返回一个新数组,新数组中的元素值为满足 callback 函数
条件的对应元素值
6) some(callback) -- 判断数组中是否存在满足 callback 条件的元素,返回布尔值
7) every(callback) -- 判断数组中是否每一个元素都满足 callback 条件,返回布尔值
8) find(callback) -- 查找数组中满足 callback 条件的第一个元素,返回查找到的元素值,找不到返
回 undefined
9) findIndex(callback) -- 查找数组中满足 callback 条件的第一个元素的下标,返回下标值,找不到
返回 -1
以上几个方法的参数 callback 为函数结构:
function(currentValue, index, array) {
// currentValue 表示当前遍历到的元素值
// index 表示当前遍历到的元素下标
// array 表示调用当前方法的数组
}
10) reduce(callback, init) 从前向后遍历
reduce方法主要实现归并(累计),callback 结构:
function(result, currentValue, index, array) {
// result 中保存累计结果
// currentValue 表示当前遍历到的元素值
// index 表示当前遍历到的元素下标
// array 表示调用当前方法的数组
}
init 参数为 callback 函数中 result 的初始值,如果不传递 init 参数,使用数组的第一个元素作为 result 的初始值。
11)includes(val):判断在数组中是否存在参数表示的元素,返回 boolean 值。如果包含则返回
true,否则返回false。
12)Array.isArray():判断参数是否为数组(面试经常提问如何判断一个表达式是否为数组)
13)数组扁平化
15.数组运用?
1)数组降序
// 降序
// 使用数组的sort方法
var numberArray = [3,6,2,4,1,5]
numberArray.sort(function(a,b){return b-a})
console.log(numberArray); // 6,5,4,3,2,1
// 降序
const arr= [95, 87, 62, 92, 73, 93,60, 59]
for(let i = 0; i < arr.length - 1 ; i++) {
for(let j = 0; j < arr.length - 1 -i; j++) {
if(arr[j] < arr[j+1]) {
const temp = arr[j]
arr[j] = arr[j+1]
arr[j+1] = temp
}
}
}
console.log('排序后:', arr)
// 升序
// 使用数组的sort方法
var newArray = [3,6,2,4,1,5]
newArray.sort(function(a,b){return a-b})
console.log(newArray); //1,2,3,4,5,6
2)数组去重
// 方法一
const array = [8, 5, 2, 8, 7, 4, 3, 8, 9, 2, 4, 7, 6, 3, 5, 4, 9, 6, 2]
const abc = []
array.forEach(function(curr) {
if(abc.indexOf(curr) === -1) {
abc.push(curr)
}
})
console.log('去重后:', abc)
//方法二
const array = [8, 5, 2, 8, 7, 4, 3, 8, 9, 2, 4, 7, 6, 3, 5, 4, 9, 6, 2]
const abc = []
array.forEach(function(ccc) {
// if(abc.includes(ccc) === false) {
// abc.push(ccc)
// }
if(!abc.includes(ccc)) {
abc.push(ccc)
}
})
console.log('去重后:', abc)
// 方法三
const array = [8, 5, 2, 8, 7, 4, 3, 8, 9, 2, 4, 7, 6, 3, 5, 4, 9, 6, 2]
const abc = array.reduce(function(result, curr) {
if(!result.includes(curr)) {
result.push(curr)
}
return result
}, [])
console.log('去重后:', abc)
3)数组扁平化
const arr = [1, [2, 3, [4, 5]]]
//第一种方法:使用数组和字符串的方法先把多维数组转化为字符串,在用字符串的split以指定的付号
//转化为数组,在通过数组的map方法把元素进行类型转换形成新的数组
function flatten(arr) {
return arr.toString().split(',').map(function(item) {
return Number(item)
})
}
//第二种方法:使用数组的join(',')将数组转换字符串,再用字符串的split(',')方法将字符串转化为数组
//再通过数组的map方法把元素进行遍历,通过Number把遍历的返回值转化为数字
function flatten(arr) {
return arr.join(',').split(',').map(function(item) {
return Number(item)
})
}
//使用reduce + 递归 实现数组的扁平化
function flatten(arr) {
return arr.reduce((result, item) => {
return result.concat(Array.isArray(item) ? flatten(item) : item )
}, [])
}
const flatten = (arr) => {
let result = [];
for (let i = 0; i < arr.length; i++) {
if (Array.isArray(arr[i])) {
result = result.concat(flatten(arr[i]));
} else {
result.push(arr[i]);
}
}
return result;
};
console.log(flatten(arr))
4)一维数组转化为转化为特定的几维数组
<script>
var fromArr = [1,2,3,4,5,6,7,8,9,10,11];
var mountOfLine = 5;
var data = fun(fromArr,mountOfLine);
console.log('data ',data)
/**
*
* @param fromArr 需要转换的一维数组(源数组)
* @param mountOfEachLine 目标数组内的子数组元素个数(例:若要的到这样的结构 -> [[1,2],[3,4],[5,6],[7,8],[9,10]] , 则mountOfLine = 2)
* @returns newArr 数据结构样式 -> [[1,2],[3,4],[5,6],[7,8],[9,10]]
*/
function fun(fromArr,mountOfEachLine) {
//装二维数组的容器(目标数组)
let newArr = [];
//源数组元素个数
let len = fromArr.length;
//计算出指定好的 mountOfLine 源数组能分成几个子数组
let lineNum = len % mountOfEachLine == 0 ? len / mountOfEachLine : Math.ceil(len / mountOfEachLine);
//将源数组的元素拿出来,放到新数组 newArr 容器内
for (let i = 0; i < lineNum; i++) {
let tempElement = fromArr.slice(i*mountOfEachLine,(i+1)*mountOfEachLine);
newArr.push(tempElement);
}
return newArr;
}
</script>
16.什么是同步任务?什么是异步任务?
同步任务是指在主线程上排队执行的任务,只有前一个任务执行完毕,才能继续执行下一个任务。
异步任务指的是,不进入主线程、而进入"任务队列"的任务,只有等主线程任务执行完毕,"任务队
列"的任务才会进入主线程执行。
异步任务又任务宏任务(settimeout,setInterval )和微任务(promise, async await)
执行顺序:先执行同步任务,再执行当前所有的微任务,然后执行一个宏任务,然后再执行所有的
微任务。再执行一个宏任务。再执行所有的微任务·······依次类推到执行结束。
17.JavaScript原型,原型链的理解?
原型
原型分为隐式原型_proto_和显示原型prototype,每个对象都有他的隐式原型_proto_,指向
它构造函数的显示原型prototype.
无论何时,只要创建一个函数,就会为这个函数添加一个prototype属性,这个属性指向原型
对象。
构造函数的prototype指向原型对象,原型对象有一个constructor属性指向构造函数,每个构
都有一个造函数的实例_proto_属性,这个属性也指向原型对象
原型链:
每个对象都有 __proto__ 属性,这个属性指向原型对象,当想访问对象的一个属性时,如果
这个对象本身没有这个属性就会通过__proto__属性 查找,原型对象也是对象,每个对象又有自己
的 __proto__ 属性,所以就会一直这样查找上去,直到找到这个属性,这就是原型链的概念。
原型链就是对象沿着 __proto__ 这条链逐步向上搜索,最顶层是 Object,Object
的 __proto__ 是 null
18.http状态码的了解?
2开头表示请求成功
200表示一切正常
3开头的便是重定向
301表示永久重定向
302表示临时重定向
4开头表示客户端错误
400表示请求报文中存在语法错误
401请求授权失败
403表示跨域
404表示请求资源不存在
5开头表示服务器端错误
500表示服务器在执行请求时发生了错误
19.call apply和bind的区别
共同点:
都是用来改变函数的this对象的指向的。
第一个参数都是this要指向的对象。
都可以参加后续参数传参
不同点:
bind是返回对应函数,便于稍后调试,apply, call则是立即调用
apply和call功能一样,只是传入的参数列表形式不同,call需要把参数按顺序传递进去,而
apply则是要把参数放在数组,伪数组里
使用示例:
apply:
apply接受两个参数,第一个参数是this的指向,第二个参数是函数接受的参数,以数组的形式传入,且当第一个参数为null、undefined的时候,默认指向window(在浏览器中),使用apply方法改变this指向后原函数会立即执行,且此方法只是临时改变thi指向一次。
var name="martin";
var obj={
name:"lucy",
say:function(year,place){
console.log(this.name+" is "+year+" born from "+place);
}
};
var say=obj.say;
setTimeout(function(){
say.apply(obj,["1996","China"])
} ,0);
//lucy is 1996 born from China,this改变指向了obj
say("1996","China")
//martin is 1996 born from China,this指向window,说明apply只是临时改变一次this指向
call:
call方法的第一个参数也是this的指向,后面传入的是一个参数列表(注意和apply传参的区别)。当一个参数为null或undefined的时候,表示指向window(在浏览器中),和apply一样,call也只是临时改变一次this指向,并立即执行。
var arr=[1,10,5,8,3];
console.log(Math.max.call(null,arr[0],arr[1],arr[2],arr[3],arr[4])); //10
bind:
Function.prototype.bind=function () {
var _this=this;
var context=arguments[0];
var arg=[].slice.call(arguments,1);
return function(){
arg=[].concat.apply(arg,arguments);
_this.apply(context,arg);
}
};
20.输入url到页面打开都做了什么事情?
输入url;
DNS解析;
TCP握手;
HTTP请求;
HTTP响应返回数据;
浏览器解析并渲染页面
20.this指向问题?
1.任何情况下直接在script中写入的this都是window。
2.普通函数中的this 非严格模式:this指向window, 严格模式时:this指向undefined。
function fn() {
console.log(this); // window
}
fn(); // window.fn(),此处默认省略window
3.箭头函数没有自己的this,其this指向与外层函数的this指向相同。
4.对象中的this
对象属性的this 指向对象外上下文环境的this
对象方法(普通函数)中的this,指向当前对象(谁执行该方法,this就指向谁)
5.回调函数的this指向
1)setTimeout, setInterval回调函数不管是否是严格模式都会指向window
2) 通过在含糊内执行当前回调函数 非严格模式:this指向window, 严格模式: this指向
undefined
3) 递归函数中的this 非严格模式:this指向window, 严格模式: this指向undefined,
4) 使用arguments执行函数时this指向arguments
5) 事件中的回调函数, this指向事件侦听的对象的对象(e.currentTarget)
6、在ES6的类中this的指向
构造函数中的this指向实例当前所产生的的新的实例对象
类中实例化方法中的this指向谁执行该方法,this指向谁
类中静态方法中this执行该类或者该类的构造函数
类中实例化箭头方法,this仍然指向当前类实例化的实例对象
7.ES5的原型对象中this的指向
在原型的方法中,this指向实例化当前构造函数的实例化对象
三种改变this指向的方式
函数名.call(this,...)this写谁就指谁
函数名.apply(this, [参数1,参数2,...])this写谁就指向谁
函数名.bind(this, 1, 2, 3)this写谁就指向谁
8.构造函数中this指向问题
// 构造函数中this指向问题
function Person(age, name) {
this.age = age;
this.name = name
console.log(this) // 此处 this 分别指向 Person 的实例对象 p1 p2
}
var p1 = new Person(18, 'zs')
var p2 = new Person(18, 'ww')
21.cookie, localStorage,sessionStorage 的区别?
cookie
可存储的数据有限,一般为4KB左右,超出的数据会被忽略
localStorage
数据永久保存,除非用户手动清理客户端缓存,一般为5M
seesionStorage
数据保存当前回话,刷新页面数据不会被清理,结束会话(关闭浏览器,关闭页面,跳转页
面)数据失效,一般5M左右,个浏览器的存储空间有差异,同页面不同窗口数据不会共享
22.函数的节流和防抖?
节流是指当一个事件触发的时候,为防止事件的连续频繁触发,设置定时器,达到一种一段事件内只触
发一次的效果,在当前事件内不会再次触发,当前事件结束以后,再次触发才有效.
function thro(cb,wait){
let timeOut
return function(){
if(timeOut) return
timeOut = setTimeout(function(){
cb()
clearTimeout(timeOut)
timeOut = null
},wait)
}
}
防抖是指当一个事件触发的时候, 为防止频繁触发事件, 设置定时器,以达到一种 频繁触发期间不处
理, 只有当最后一次连续触发结束以后才处理
function debounce(cb,wait){
let timer
return function(){
clearTimeout(timer)
timer = setTimeout(()=>cb(),wait)
}
}
23.设计模式有哪些?
共23种设计模式,介绍其中6种应用较为广泛的模式。
发布订阅模式:该设计模式可以大大降低程序模块之间的耦合度,便于更加灵活的扩展和维护
单例模式:该设计模式保证一个类只有一个实例,并提供一个访问它的全局访问点
工厂模式:该模式定义一个用于创建对象的接口,这个接口由子类决定实例化哪一个类,该
模式使一个类的实例化延迟到子类,而子类可以重写接口方法以便创建的时候指定自己的对象类型
装饰者模式:该模式能够在不改变对象自身的基础上,在程序运行期间给对象动态的添加方
法或者属性,与继承相比,装饰者模式是一种更轻便灵活的做法
中介者模式:观察者模式通过维护一堆列表来管理对象间的多对多关系,中介者模式通过统
一接口来维护一对多的关系,且通信者之间不需要知道彼此的关系,只需要约定好API即可
代理模式:为其他对象提供一种代理以控制对这个对象的方法,代理模式使得代理对象控制
具体对象的引用,代理几乎可以是任何对象:文件,资源,内存中的对象或者是一些难以复制的东
西
24.http的理解?
HTTP 协议是超文本传输协议,是客户端浏览器或其他程序“请求”与 Web 服务器响应之间的应用
层通信协议。
HTTPS主要是由HTTP+SSL构建的可进行加密传输、身份认证的一种安全通信通道。
25.http与https的区别?
1)https协议需要到ca申请整数,一般免费整数较少,因而需要一定的费用
2)http是超文本传输协议,信息是明文传输,而https则是具有安全性的ssl加密传输协议
3)http和https使用的是完全不同的连接方式,用的端口号也不一样,前者是80,后者是443
4)http的连接很简单,是无状态的;https协议是由ssl+http协议构建的可进行加密传输,身份认证
的网络协议,比http协议安全
26.什么是三次挥手和四次握手?
三次握手是网络客户端跟网络服务器之间建立连接,并进行通信的过程。相当于客户端和服务器之
间你来我往的3个步骤。
第一次握手是建立连接,客户端发送连接请求报文,并传送规定的数据包;
第二次握手是服务器表示接收到连接请求报文,并回传规定的数据包;
第三次握手是客户端街道服务器回传的数据包后,给服务器再次发送数据包。
四次挥手表示当前这次连接请求已经结束,要断开这次连接
第一次挥手是客户端对服务器发起断开请求;
第二次挥手是服务器表示接收到这次断开请求;
第三次挥手是服务器表示已经断开连接;
第四次挥手是客户端断开连接。