目录
5.对BFC规范(块级格式化上下文:block formatting context)的理解?
16.一图搞懂javascript中的this与call/apply/bind的6中关系
29.ES6中的Set、WeakSet、Map、WeakMap数据结构
10.Vue 中 methods,computed, watch 的区别
2.请写出在vue 中使用promise 封装项目api 接口的思路?
HTML&CSS
盒模型与BFC
1. 什么是盒子模型?
在我们HTML页面中,每一个元素都可以被看作一个盒子,而这个盒子由:内容区(content)、填充区(padding)、边框区(border)、外边界区(margin)四部分组成。
2.盒子模型有哪两种
标准模式下: 一个块的总宽度(页面中占的宽度)= width + margin(左右) + padding(左右) + border(左右)
怪异模式下: 一个块的总宽度= width + margin(左右)(即width已经包含了padding和border值)(IE浏览器)
3.标准和怪异模型的转换
box-sizing:content-box; 将采用标准模式的盒子模型标准
box-sizing:border-box; 将采用怪异模式的盒子模型标准
box-sizing:inherit; 规定应从父元素继承 box-sizing 属性的值。
4.JS盒模型
怎么获取和设置box的内容宽高
IE: dom.currentStyle.width/height
非IE: window.getComputedStyle(dom).width/height
5.对BFC规范(块级格式化上下文:block formatting context)的理解?
BFC 就是“块级格式化上下文”的意思,创建了 BFC 的元素就是一个独立的盒子,不过只有块圾盒子可以参与创建 BFC, 它规定了内部的块圾盒子如何布局,并且与这个独立盒子里的布局不受外部影响,当然它也不会影响到外面的元素。
6.瀑布流原理
瀑布流又称瀑布流式布局,是比较流行的一种网站页面布局方式 视觉表现为参差不齐的多栏布局 瀑布流可以有多列 每一个单元格的高度可以不相同但是宽度必须一样 排列的方式是从左往右排列 哪一列现在的总高度最小 就优先排序把单元格放在这一列 这样排完所有的单元格后 就可以保证每一列的总高度都相差不大
7.h5 和css3 的新特性
h5 每个人有每个人的理解,我的理解呢h5 呢并不是新增一些标签和样式更多的是里面新增的一些功能例如重力感应,他可以让我们感知当前手机的状态,可以帮助我们完成手机摇一摇,监听当前我们步数,而且 h5 中为了挺高页面性能,页面元素的变大,不在是元素本身的大小变化,而是一种视觉上的效果,从而减少了 dom 操作,防止了页面的重绘
- flex布局
- 水平/垂直居中
JavaScript
- 原型与原型链
1. prototype
每个函数都有一个prototype属性,被称为显示原型
2._ _proto_ _
每个实例对象都会有_ _proto_ _属性,其被称为隐式原型
每一个实例对象的隐式原型_ _proto_ _属性指向自身构造函数的显式原型prototype
3. constructor
每个prototype原型都有一个constructor属性,指向它关联的构造函数。
4. 原型链
获取对象属性时,如果对象本身没有这个属性,那就会去他的原型__proto__上去找,如果还查不到,就去找原型的原型,一直找到最顶层(Object.prototype)为止。Object.prototype对象也有__proto__属性值为null。
- 作用域&&*变量&&作用域链
作用域的种类:
全局作用域
函数作用域
块级作用域(es6新增)
➡️js中首先有一个最外层的作用域,全局作用域;
➡️js中可以通过函数来创建一个独立作用域称为函数作用域,函数可以嵌套,所以作用域也 可以嵌套;
➡️es6中新增了块级作用域(大括号,比如:if{},for(){},while(){}…);
*变量的概念:
当前作用域没有定义的变量
一个变量在当前作用域没有被定义,但被使用了
向上级作用域,一层一层一次寻找,直至找到为止
如果全局作用域都没找到,则报错xx is not defined
作用域链
*变量的向上级作用域一层一层查找,直到找到为止,最高找到全局作用域,就形成了作用域链。
- 闭包
闭包:
定义:当一个函数的返回值是另外一个函数,而返回的那个函数如果调用了其父函数的内部 变量,且返回的那个函数在外部被执行,就产生了闭包.
闭包的三个特性
1:函数套函数
2:内部函数可以直接访问外部函数的内部变量或参数
3:变量或参数不会被垃圾回收机制回收
闭包的优点:
1:变量长期驻扎在内存中
2:避免全局变量的污染
3:私有成员的存在
闭包的缺点
常驻内存 增大内存的使用量 使用不当会造成内存的泄露.
闭包的两种写法:
1:
function a () { var num=1;
function b () {
alert(num)
}
return b;//把函数 b 返回给函数 a;
}
alert(a())//弹出函数 a, 值是函数 b;
2:
function a () { var num=1;
return function b () {
//把函数 b 返回给函数 a;
alert(num=num+2)
}
}
alert(a())//弹出函数 a, 值是函数 b;
调用方式:
//1:直接调用
//2:通过赋值在调用
4.垃圾回收机制
浏览器的 Javascript 具有自动垃圾回收机制(GC:Garbage Collecation),也就是说,执行环境会负责管理代码执行过程中使用的内存。其原理是:垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。但是这个过程不是实时的,因为其开销比较大并且GC时停止响应其他操作,所以垃圾回收器会按照固定的时间间隔周期性的执行。
5.什么是内存泄漏?
程序的运行需要内存。只要程序提出要求,操作系统或者运行时(runtime)就必须供给内存。
对于持续运行的服务进程(daemon),必须及时释放不再用到的内存。否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃。
6.Vue 中的内存泄漏问题
- 如果在mounted/created钩子中使用 JS 绑定了DOM/BOM对象中的事件,需要在 beforeDestroy中做对应解绑处理;
- 如果在 mounted/created钩子中使用了第三方库初始化,需要在 beforeDestroy中做对应销毁处理(一般用不到,因为很多时候都是直接全局 Vue.use);
- 如果组件中使用了 setInterval,需要在 beforeDestroy中做对应销毁处理;
7.js的数据类型、堆栈存储、多数据类型计算
js数据类型有哪些
基本数据类型(值类型): Number、String、Boolean、Undefined、Null、Symbol(es6新增独一无二的值) 和 BigInt(es10新增);
引用数据类型: Object。包含Object、Array、 function、Date、RegExp。
基本数据类型与栈内存
JS中的基础数据类型,这些值都有固定的大小,往往都保存在栈内存中(闭包除外),由系统自动分配存储空间。我们可以直接操作保存在栈内存空间的值,因此基础数据类型都是按值访问 。 基础数据类型:Number String Null Undefined Boolean
引用数据类型与堆内存
JS的引用数据类型,比如数组Array,它们值的大小是不固定的。引用数据类型的值是保存在堆内存中的对象。JS不允许直接访问堆内存中的位置,因此我们不能直接操作对象的堆内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。因此,引用类型的值都是按引用访问的。这里的引用,我们可以粗浅地理解为保存在栈内存中的一个地址,该地址与堆内存的实际值相关联。
- 递归手写深拷贝
https://blog.csdn.net/weixin_43638968/article/details/109243067
- js数据类型判断
四种方法
typeof、instanceof、constructor、Object.prototype.toString.call()、jquery.type()
1 .typeof
基本数据类型中:Number,String,Boolean,undefined 以及引用数据类型中Function ,可以使用typeof检测数据类型,分别返回对应的数据类型小写字符。
另:用typeof检测构造函数创建的Number,String,Boolean都返回object
基本数据类型中:null 。引用数据类型中的:Array,Object,Date,RegExp。不可以用typeof检测。都会返回小写的object
2 . instanceof
除了使用typeof来判断,还可以使用instanceof。instanceof运算符需要指定一个构造函数,或者说指定一个特定的类型,它用来判断这个构造函数的原型是否在给定对象的原型链上。
基本数据类型中:Number,String,Boolean。字面量值不可以用instanceof检测,但是构造函数创建的值可以
3 .constructor
constructor是prototype对象上的属性,指向构造函数。根据实例对象寻找属性的顺序,若实例对象上没有实例属性或方法时,就去原型链上寻找,因此,实例对象也是能使用constructor属性的。
4 . 使用Object.prototype.toString.call()检测对象类型
可以通过toString() 来获取每个对象的类型。为了每个对象都能通过 Object.prototype.toString() 来检测,需要以 Function.prototype.call() 或者 Function.prototype.apply() 的形式来调用,传递要检查的对象作为第一个参数,称为thisArg。
使用Object.prototype.toString.call()的方式来判断一个变量的类型是最准确的方法。
5 .无敌万能的方法:jquery.type()
如果对象是undefined或null,则返回相应的“undefined”或“null”。
- JavaScript数组方法总结
参看如下链接:
https://www.cnblogs.com/cauliflower/p/11267809.html
https://www.cnblogs.com/zyfeng/p/10541133.html
push()
可以接受一个或者多个参数,将参数追加到数组的尾部,返回添加后的数组的长度,原数组会发生改变。
pop()
从数组尾部删除一个元素,返回这个被删除的元素,原数组发生改变。
unshift()
可以接受一个或者多个参数,将参数放到数组的头部,返回添加后的数组的长度,原数组会发生改变。
shift()
从数组头部删除一个元素,返回这个被删除的元素,原数组发生改变。
slice()
截取类 如果不传参数,会返回原数组;如果一个参数,从该参数表示的索引开始截取,直至数组结束,返回这个截取数组,原数组不变;两个参数,从第一个参数对应的索引开始截取,到第二个参数对应的索引结束,但不包括第二个参数对应的索引上值,原数组不改变;最多接受两个参数。
splice()
截取类 没有参数,返回空数组,原数组不变;一个参数,从该参数表示的索引位开始截取,直至数组结束,返回截取的 数组,原数组改变;两个参数,第一个参数表示开始截取的索引位,第二个参数表示截取的长度,返回截取的 数组,原数组改变;三个或者更多参数,第三个及以后的参数表示要从截取位插入的值。
reverse()
不接受参数,数组翻转。
sort()
排序。
arr.sort(function(a,b){
return a-b; //从小到大排序
return b-a; //从大到小排序
});
join()
参数来拼接;分隔符。
concat()
将参数放入原数组后返回,原数组本身不变,如果参数是数组,将值提出来。
isArray()
判断是否是数组。
toString()
数组转字符串。
ES5新增的数组方法
2个索引方法:indexOf() 和 lastIndexOf();
indexOf()
从前往后遍历,返回item在数组中的索引位,如果没有返回-1;通常用来判断数组中有没有某个元素。可以接收两个参数,第一个参数是要查找的项,第二个参数是查找起点位置的索引。
lastIndexOf()
与indexOf一样,区别是从后往前找。
5个迭代方法:forEach()、map()、filter()、some()、every();
forEach()
forEach方法与map方法很相似,也是对数组的所有成员依次执行参数函数。但是,forEach方法不返回值,只用来操作数据。这就是说,如果数组遍历的目的是为了得到返回值,那么使用map方法,否则使用forEach方法;forEach的用法与map方法一致,参数是一个函数,该函数同样接受三个参数:当前值、当前位置、整个数组。
map()
将数组的所有成员依次传入参数函数,然后把每一次的执行结果组成一个新数组返回;map方法接受一个函数作为参数。该函数调用时,map方法向它传入三个参数:当前成员、当前位置和数组本身。
filter()
用于过滤数组成员,满足条件的成员组成一个新数组返回;它的参数是一个函数,所有数组成员依次执行该函数,返回结果为true的成员组成一个新数组返回。该方法不会改变原数组;可以接受三个参数:当前成员,当前位置和整个数组。
some()
该方法对数组中的每一项运行给定函数,如果该函数对任何一项返回 true,则返回true。(some方法会在数组中任一项执行函数返回true之后,不在进行循环。)
every()
该方法对数组中的每一项运行给定函数,如果该函数对每一项都返回 true,则返回true。
2个归并方法:reduce()、reduceRight();
reduce()
依次处理数组的每个成员,最终累计为一个值。reduce是从左到右处理(从第一个成员到最后一个成员)。参数是一个函数,该函数接受以下两个参数:1累积变量,默认为数组的第一个成员;2当前变量,默认为数组的第二个成员。
reduceRight()
从右往左。
ES6新增的数组方法
Array.from()
用于类似数组的对象(即有length属性的对象)和可遍历对象转为真正的数组。
let json ={
'0':'hello',
'1':'123',
'2':'panda',
length:3
}
let arr = Array.from(json); console.log(arr);//打印:["hello", "123", "panda"]
Array.of()
将一组值转变为数组。
let arr1 = Array.of('你好','hello'); console.log(arr1);//["你好", "hello"]
find()和findIndex()
用于找出第一个符合条件的数组成员。参数是个回调函数,所有数组成员依次执行该回调函数,直到找到第一个返回值为true的成员,然后返回该成员。如果没有符合条件的成员,就返回undefined;可以接收3个参数,依次为当前值、当前位置、原数组。
fill()
使用fill()方法给定值填充数组。
如:new Array(3).fill(7);//[7,7,7]
可以接收第二个和第三个参数,用于指定填充的起始位置和结束位置(不包括结束位置)。
let arr3 = [0,1,2,3,4,5,6,7]; arr3.fill('error',2,3); console.log(arr3);//[0,1,"error",3,4,5,6,7]
遍历数组的方法:
entries()、values()、keys()
这三个方法都是返回一个遍历器对象,可用for...of循环遍历,唯一区别:keys()是对键名的遍历、values()对键值的遍历、entries()是对键值对的遍历。
for(let item of ['a','b'].keys()){
consloe.log(item);
//0
//1
}for(let item of ['a','b'].values()){
consloe.log(item);
//'a'
//'b'
}let arr4 = [0,1];for(let item of arr4.entries()){
console.log(item);
// [0, 0]
// [1, 1]
}
如果不用for...of进行遍历,可用使用next()方法手动跳到下一个值。
let arr5 =['a','b','c']let entries = arr5.entries();
console.log(entries.next().value);//[0, "a"]
console.log(entries.next().value);//[1, "b"]
console.log(entries.next().value);//[2, "c"]
console.log(entries.next().value);//undefined
copyWithin()
在数组内部,将指定位置的成员复制到其他位置(会覆盖原有成员),改变原数组。
该函数有三个参数。target:目的起始位置。start:复制源的起始位置(从0开始),可以省略,可以是负数。end:复制源的结束位置,可以省略,可以是负数,实际结束位置是end-1。
const arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];arr1.copyWithin(1, 3, 6);//把第3个元素(从0开始)到第5个元素,复制并覆盖到以第1个位置开始的地方。console.log('%s', JSON.stringify(arr1))//
[1,4,5,6,5,6,7,8,9,10,11]
start和end都是可以省略。start省略表示从0开始,end省略表示数组的长度值。目标的位置不够的,能覆盖多少就覆盖多少。
const arr2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]arr2.copyWithin(3)console.log('%s', JSON.stringify(arr2))//[1,2,3,1,2,3,4,5,6,7,8]
start和end都可以是负数,负数表示从右边数过来第几个(从-1开始)。start小于end,两者为负数时也是。
const arr3 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]arr3.copyWithin(3, -3, -2)console.log('%s', JSON.stringify(arr3))
end永远大于end(取值必须是start右边的),end小于start(取start左边的值)时会返回原数组。
ES7新增的数组方法
includes()
表示某个数组是否包含给定的值,如果包含则返回 true,否则返回false。可以接收两个参数:要搜索的值和搜索的开始索引。当第二个参数被传入时,该方法会从索引处开始往后搜索(默认索引值为0)。若搜索值在数组中存在则返回true,否则返回false。
['a', 'b', 'c', 'd'].includes('b')
// true
['a', 'b', 'c', 'd'].includes('b', 1)
// true
['a', 'b', 'c', 'd'].includes('b', 2)
// false
tips:includes与indexOf的区别是:前者返回布尔值(利于if条件判断),后者返回数值。
- js数组去重
参考地址:https://blog.csdn.net/weixin_43638968/article/details/107519628
1、 ES6-set
使用ES6中的set是最简单的去重方法
2、利用Map数据结构去重
创建一个空Map数据结构,遍历需要去重的数组,把数组的每一个元素作为key存到Map中。由于Map中不会出现相同的key值,所以最终得到的就是去重后的结果。
3、 利用递归去重
4、 forEach + indexOf
5、 filter+indexOf
6、 forEach + includes
7、 reduce + includes
8、 嵌套循环+splice
9、 hash+hasOwnProperty+JSON.stringify(终级版)
12.js中数组排序(冒泡、快速、插入)
参考地址:https://blog.csdn.net/weixin_43638968/article/details/108500572
1.冒泡排序法
2. 插入排序法(插队排序)
将要排序的数组分成两部分,每次从后面的部分取出索引最小的元素插入到前一部分的适当位置
3.快速排序
实现思路是,将一个数组的排序问题看成是两个小数组的排序问题,以一个数为基准(中间的数),比基准小的放到左边,比基准大的放到右边,而每个小的数组又可以继续看成更小的两个数组,一直递归下去,直到数组长度大小最大为2。
13.深拷贝和浅拷贝
深拷贝和浅拷贝的区别
- 浅拷贝:
将原对象或原数组的引用直接赋给新对象,新数组,新对象/数组只是原对象的一个引用
- 深拷贝:
创建一个新的对象和数组,将原对象的各项属性的“值”(数组的所有元素)拷贝过来,是“值”而不是“引用”
为什么要使用深拷贝?
我们希望在改变新的数组(对象)的时候,不改变原数组(对象)
内存模型
JS内存空间分为栈(stack)、堆(heap)、池(一般也会归类为栈中)。 其中栈存放变量,堆存放复杂对象,池存放常量。
基本数据类型与栈内存
JS中的基础数据类型,这些值都有固定的大小,往往都保存在栈内存中(闭包除外),由系统自动分配存储空间。我们可以直接操作保存在栈内存空间的值,因此基础数据类型都是按值访问 。 基础数据类型:Number String Null Undefined Boolean
引用数据类型与堆内存
JS的引用数据类型,比如数组Array,它们值的大小是不固定的。引用数据类型的值是保存在堆内存中的对象。JS不允许直接访问堆内存中的位置,因此我们不能直接操作对象的堆内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。因此,引用类型的值都是按引用访问的。这里的引用,我们可以粗浅地理解为保存在栈内存中的一个地址,该地址与堆内存的实际值相关联。
- for···in和for···of的区别
首先一句话:(for···in取key,for··of取value)
①从遍历数组角度来说,for···in遍历出来的是key(即下标),for···of遍历出来的是value(即数组的值);
②从遍历字符串的角度来说,同数组一样。
③从遍历对象的角度来说,for···in会遍历出来的为对象的key,但for···of会直接报错。
④如果要使用for…of遍历普通对象,需要配合Object.keys()一起使用。
- null和undefined区别
相同性:
在JavaScript中,null 和 undefined 几乎相等
在 if 语句中 null 和 undefined 都会转为false两者用相等运算符比较也是相等
null和undefined区别:
null表示没有对象,可能将来要赋值一个对象,即该处不应该有值
1) 作为函数的参数,表示该函数的参数不是对象
2) 作为对象原型链的终点
undefined表示缺少值,即此处应该有值,但没有定义
1)定义了形参,没有传实参,显示undefined
2)对象属性名不存在时,显示undefined
3)函数没有写返回值,即没有写return,拿到的是undefined
4)写了return,但没有赋值,拿到的是undefined
16.一图搞懂javascript中的this与call/apply/bind的6中关系
参考地址:https://blog.csdn.net/weixin_43638968/article/details/107523660
总结:
在浏览器里,在全局范围内this 指向window对象;
在函数中,this永远指向最后调用他的那个对象;
构造函数中,this指向new出来的那个新的对象;
call、apply、bind中的this被强绑定在指定的那个对象上;
箭头函数中this比较特殊,箭头函数this为父作用域的this,不是调用时的this.要知道前四种方式,都是调用时确定,也就是动态的,而箭头函数的this指向是静态的,声明的时候就确定了下来;
apply、call、bind都是js给函数内置的一些API,调用他们可以为函数指定this的执行,同时也可以传参。
- 事件绑定、事件传播、事件捕获、事件冒泡、自定义事件
DOM事件三种级别
DOM0级事件
DOM0 级时间分两种,一是直接在标签内直接添加执行语句,二是定义执行函数。
DOM2 级事件
第一个参数:事件名称
第二个参数:执行函数
第三个参数:指定冒泡还是捕获,默认是false,冒泡。
DOM3 级事件
同DOM2级一样,只不过添加了更多的事件类型,鼠标事件、键盘事件。
DOM事件两种类型
事件类型分两种:事件捕获、事件冒泡。
DOM事件的事件流(事件传播)
事件流就是,事件传播过程。
DOM完整的事件流包括三个阶段:事件捕获阶段、目标阶段和事件冒泡阶段。
事件通过捕获到达目标元素,这个时候就是目标阶段。从目标节点元素将事件上传到根节点的过程就是第三个阶段,冒泡阶段。
对应上图自己体会体会。
事件捕获的具体流程
当事件发生在 DOM元素上时,该事件并不完全发生在那个元素上。在捕获阶段,事件从window开始,之后是document对象,一直到触发事件的元素。
事件冒泡的具体过程
当事件发生在DOM元素上时,该事件并不完全发生在那个元素上。在冒泡阶段,事件冒泡,或者事件发生在它的父代,祖父母,直到到达window为止。
我们也可以通过 new Event()自定义事件
18.js中事件委托
事件委托,又名事件代理。事件委托就是利用事件冒泡,就是把子元素的事件都绑定到父元素上。如果子元素阻止了事件冒泡,那么委托也就没法实现了。
好处:
提高性能,减少了事件绑定,从而减少内存占用
19.原生Ajax的创建过程
1.创建xhr 核心对象
var xhr=new XMLHttpRequest();
2.调用open 准备发送
参数一:请求方式
参数二: 请求地址
参数三:true异步,false 同步
xhr.open('post','http://www.baidu.com/api/search',true)
3.如果是post请求,必须设置请求头。
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
4.调用send 发送请求 (如果不需要参数,就写null)
xhr.send('user=tom&age=10&sex=女')
5.监听异步回调 onreadystatechange
判断readyState 为4 表示请求完成
判断status 状态码 为 200 表示接口请求成功
responeseText 为相应数据。字符串类型。
20.跨域(jsonp,vue)
1、理解跨域的概念:协议、域名、端口都相同才同域,否则都是跨域
2、出于安全考虑,服务器不允许 ajax 跨域获取数据,但是可以跨域获取文件内容,所以基于这一点,可以动态创建 script 标签,使用标签的 src 属性访问 js 文件的形式获取 js 脚本,并且这个 js 脚本中的内容是函数调用,该函数调用的参数是服务器返回的数据,为了获取这里的参数数据,需要事先在页面中定义回调函数,在回调函数中处理服务器返回的数据,这就是解决跨域问题的jsonp原理。
跨域的解决方式有哪些?
跨域的解决方案目前有三种主流解决方案:
➡jsonp
jsonp 实现原理:主要是利用动态创建 script 标签请求后端接口地址,然后传递 callback 参数,后端接收 callback,后端经过数据处理,返回 callback 函数调用的形式,callback 中的参数就是 json
优点:浏览器兼容性好,
缺点:只支持 get 请求方式
➡代理(前端代理和后端通常通过 nginx 实现反向代理)
➡CORS
CORS 全称叫跨域资源共享,主要是后台工程师设置后端代码来达到前端跨域请求的
21.sessionStorage,localStorage,cookie 的区别
一、 cookie:能存储内容较小,在4k左右,一般用作保存用户登录状态、记住密码,记住账号使用。不清除的话会一直存在,可以设置过期时间自动清除
二、 localStorage:可保存内容在5M左右,不会自动清除,除非手动进行删除。
三、 sessionStorage保存在当前会话中,会话结束sessionStorage失效。会话一般是在关闭页面或者关闭浏览器失效。
<--------------------------------------------------------------------------------------------->
1.cookie 的优点:具有极高的扩展性和可用性
1.通过良好的编程,控制保存在 cookie 中的 session 对象的大小。
2.通过加密和安全传输技术,减少 cookie 被破解的可能性。
3.只有在 cookie 中存放不敏感的数据,即使被盗取也不会有很大的损失。
4.控制 cookie 的生命期,使之不会永远有效
2.cookie 的缺点:
1.cookie 的长度和数量的限制。每个 domain 最多只能有 20 条 cookie,每个cookie 长度不能超过 4KB。否则会被截掉。
2.安全性问题。如果 cookie 被人拦掉了,那个人就可以获取到所有 session
信息。加密的话也不起什么作用
3.有些状态不可能保存在客户端
3.sessionStorage、Cookie 共同点:都是保存在浏览器端,且同源的。
22.Let const var 的区别
是否存在变量提升?
var声明的变量存在变量 提升即变量可以在声明之前调用,值为undefined。
let和const不存在变量提升。即它们所声明的变量一定要在声明后使用,则报ReferenceError错。
是否存在暂时性死区
let和const存在暂时性死区。即只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。
在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”
是否允许重复声明
var允许重复声明变量。
let和const在同一作用域不允许重复声明变量。
是否存在块级作用域
var不存在块级作用域。 let和const存在块级作用域。
ES5中作用域有:全局作用域、函数作用域。没有块作用域的概念。
ES6中新增了块级作用域。块作用域由{ }包括,if语句和for语句里面的{ }也属于块作用域。
是否能修改声明的变量
var和let可以。const声明一个只读的常量。一旦声明,常量的值就不能改变。
23.Es6解构赋值
解构赋值语法是一种 Javascript 表达式。通过解构赋值, 可以将属性/值从对象/数组中取出,赋值给其他变量。
应用场景:默认值,交换变量,将剩余数组赋给一个变量
24.箭头函数与普通函数的区别
1. 箭头函数是匿名函数,不能作为构造函数,不能使用new
2.箭头函数内没有arguments,可以用展开运算符...解决
3.箭头函数的this,始终指向父级上下文(箭头函数的this取决于定义位置父级的上下文,跟使用位置没关系,普通函数this指向调用的那个对象)
4.箭头函数不能通过call() 、 apply() 、bind()方法直接修改它的this指向。(call、aaply、bind会默认忽略第一个参数,但是可以正常传参)
- 箭头函数没有原型属性
25继承
Es5中的类:
ES5中如果要生成一个对象实例,需要先定义一个构造函数,然后通过new操作符来完成。
Es6中的类:
ES6引入了class(类)这个概念,通过class关键字可以定义类。该关键字的出现使得javascript在对象写法上更加清晰,更像是一种面向对象的语言。
变量提升:
class不存在变量提升,所以需要先定义再使用。因为ES6不会把类的声明提升到代码头部,但是ES5就不一样,ES5存在变量提升,可以先使用,然后再定义。
Es5中继承:(组合继承:原型链继承+借用构造函数)
(1):原型链继承:父类的实例作为子类的原型
(2):借用构造函数:在子类内,使用call()调用父类方法,并将父类的this修改为子类的this.相当于是把父类的实例属性复制了一份放到子类的函数内.
(3):组合继承:既能调用父类实例属性,又能调用父类原型属性
Es6中class继承:
先定义一个父类, 再定义了一个子类,我们可以称它为子类,子类通过extends关键字,继承了父类的所有属性和方法。
super作为函数调用时,代表父类的构造函数。ES6 要求,子类的构造函数必须执行一次super函数。子类的构造函数之中的super(),代表调用父类的构造函数。这是必须的,否则 JavaScript 引擎会报错。注意,super虽然代表了父类的构造函数,但是返回的是子类j的实例,即super内部的this指的是子类
26.Promise
Promise是异步编程的一种解决方案,解决了地狱回调的问题。
➡简单说,Promise就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。
➡从语法上说,promise 是一个对象,从它可以获取异步操作的的最终状态(成功或失败)。
➡Promise是一个构造函数,对外提供统一的 API,自己身上有all、reject、resolve等方法,原型上有then、catch等方法。
Promise对象的状态不受外界影响,有三种状态:
分别是 pending 初始状态、fulfilled 成功状态、rejected 失败状态,只有异步操作的结果可以决定当前是哪一种状态,其他任何操作都无法改变这个状态
Promise的状态一旦改变,就不会再变,任何时候都可以得到这个结果,状态不可以逆,只能由 pending变成fulfilled或者由pending变成rejected
Promise使用new创建一个promise,该函数的两个参数分别是resolve和reject。这两个函数就是就是回调函数。
resolve函数的作用:在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
27.async和await
Async 和 await 是一种同步的写法,但还是异步的操作,两个内容还是必须同时去写才会生效不然的话也是不会好使
而且 await 的话有一个不错的作用就是可以等到你的数据加载过来以后才会去运行下边的 js 内容
而且 await 接收的对象必须还是个 promise 对象
如果是接收数据的时候就可以直接用一句话来接收数据值
所以这个 async await 我的主要应用是在数据的接收,和异步问题的处理,主要是还是解决不同执行时机下的异步问题!
28.es6中Generator函数
1.Generator 函数的定义
语法上,Generator 函数是一个状态机,封装了多个内部状态。
形式上,Generator是一个函数。不同于普通函数,是可以暂停执行的,所以函数名之前要加星号,以示区别。
整个Generator函数就是一个封装的异步任务,或者说是异步任务的容器,异步操作需要暂停的地方,都用yield语句。
function 关键字和函数之间有一个星号(*),且内部使用yield表达式,定义不同的内部状态。
调用Generator函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象。
29.ES6中的Set、WeakSet、Map、WeakMap数据结构
Set
Set本身是一个构造函数,用来生成Set数据结构,类似数组Array,但是成员的值都是唯一的,没有重复的值
属性方法
size: 返回set实例的成员总数
add(value): 添加某个值,返回Set本身
delete(value): 删除某个值,返回是否删除成功的boolean has(value): 判断是否包含某个值,返回一个boolean
clear():清空Set的所有成员,没有返回值
WeakSet
结构和Set类似,但是有写法区别
1、只能存储对象,其他类型的值不行
2、存储的对象是弱引用,也就是说垃圾回收机制不考虑WeakSet对该对象的引用。换种说法如果一个对象都不再被引用,那么垃圾回收机制会自动回收该对象所占用的内存,不会考虑该对象是否存在于WeakSet中。因此WeakSet是不可遍历的
属性方法
add(value)方法
delete(value)方法
has(value)方法
不支持clear、size方法
如果增加其他类型的数值,则会报错
Map
类似于对象,是键值对的集合,但是key的范围不局限与字符串。各种类型均可以作为key
属性方法
set(key,value): 添加一个键值对
get(key): 获取一个key对应的value值
size: 返回Map结构的成员总数
has(key):判断某个键是否存在Map结构中,返回boolean delete(key): 删除某个指定的键值,返回boolean
clear(): 清除Map结构所有的成员
WeakMap
和Map类似,区别就是键只能是object类型
键名是对象的弱引用,因此所对应的对象可能会被自动回收,如果对象被回收后,WeakMap自动移除对应的键值对,WeakSet有助于防止内存泄露
属性方法
get(key)
set(key,value)
has(key)
delete(key)
VUE
1.为什么data必须是一个函数
如果不是一个函数的话,每个组件的实例的data都是同一个引用数据,当该组件作为公共组件共享使用,一个地方的data更改,所有的data一起改变。如果data是一个函数,每个实例的data都在闭包当中,就不会各自影响了
2.vue常用的指令
v-model 多用于表单元素实现双向数据绑定
v-for 格式: v-for="(item,index) in/of 数组json" 循环数组或json
v-show 显示内容 ,通过display=block/none来控制元素隐藏出现
v-hide 隐藏内容 同上
v-if 显示与隐藏 (dom元素的删除添加 同angular中的ng-if 默认值为false)
v-else-if 必须和v-if连用
v-else 必须和v-if连用 不能单独使用 否则报错 模板编译错误
v-bind 动态绑定 作用: 及时对页面的数据进行更改
v-on:click 给标签绑定函数,可以缩写为@,例如绑定一个点击函数 函数必须写在methods里面
v-text 解析文本
v-html 解析html标签
v-bind:class 三种绑定方法 1、对象型 ‘{red:isred}’ 2、三元型 ‘isred?“red”:“blue”’ 3、数组型 ‘[{red:“isred”},{blue:“isblue”}]’
v-once 进入页面时 只渲染一次 不在进行渲染
v-cloak 防止闪烁 该属性需配合 样式使用
v-pre 把标签内部的元素原位输出
3.v-if与v-show的区别
相同点
都可以动态控制着dom元素的显示隐藏
区别
v-if: 控制DOM元素的显示隐藏是将DOM元素整个添加或删除;
v-show: 控制DOM 的显示隐藏是为DOM元素添加css的样式display,设置none或者是block,DOM元素是还存在的
性能对比
v-if有更高的切换消耗;
v-show有更高的初始渲染消耗
使用场景
v-if适合运营条件不大可能改变的场景下;
v-show适合频繁切换;
4.vue的生命周期
总共分为8个阶段:
创建前/后,载入前/后,更新前/后,销毁前/后。
创建前/后:
在beforeCreated阶段,vue实例的挂载元素$el和数据对象 data 都为undefined,还未初始化。在 created阶段,vue实例的数据对象data有了,$el还没有。
载入前/后:
在beforeMount阶段,vue实例的$el和data都初始化了,但还是挂载之前为虚拟的dom节点,data.message还未替换。在mounted阶段,vue实例挂载完成,data.message成功渲染。
更新前/后:
当data变化时,会触发beforeUpdate和updated方法。
销毁前/后:
在destroy阶段,对data的改变不会再触发周期函数,说明此时vue实例已经解除了事件监听以及和dom的绑定,但是dom结构依然存在。destroyed阶段,组件销毁
5.vue的双向数据绑定
vue.js是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调来渲染视图。
主要分为三部分:
➡observer主要是负责对Vue数据进行数据劫持,使其数据拥有get和set方法
➡compile指令解析器负责绑定数据和指令,绑定试图更新方法
➡watcher是Observer和Compile之间通信的桥梁负责数据监听,当数据发生改变通知订阅者,调用视图更新函数更新视图
Object.defineProperty()方法有何作用
作用:Object.defineProperty方法会直接在对象上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象。在vue中通过getter和setter函数来实现双向绑定;语法:他有三个参数object,propName,descriptor
obj 要在其上定义属性的对象。
prop 要定义或修改的属性的名称。
descriptor 将被定义或修改的属性描述符。
6.虚拟dom
Dom:
利用js描述元素与元素的关系,用普通js对象来描述DOM结构,因为不是真实DOM,所以称之为虚拟DOM。好处是可以高效快速的渲染DOM,提高浏览器性能。
Diff:
diff算法就是进行虚拟节点对比,并返回一个patch对象,用来存储两个节点不同的地方,最后用patch记录的消息去局部更新Dom。简单来说就是比较新旧节点,一边比较一边给真实的DOM打补丁。
虚拟dom官方回答:
virtual DOM 虚拟DOM,用普通js对象来描述DOM结构,因为不是真实DOM,所以称之为虚拟DOM。
虚拟 dom 是相对于浏览器所渲染出来的真实 dom而言的,在react,vue等技术出现之前,我们要改变页面展示的内容只能通过遍历查询 dom树的方式找到需要修改的 dom 然后修改样式行为或者结构,来达到更新 ui 的目的。
这种方式相当消耗计算资源,因为每次查询 dom 几乎都需要遍历整颗 dom 树,如果建立一个与 dom 树对应的虚拟 dom 对象( js 对象),以对象嵌套的方式来表示 dom 树及其层级结构,那么每次 dom 的更改就变成了对 js 对象的属性的增删改查,这样一来查找 js 对象的属性变化要比查询 dom 树的性能开销小。
7.组件通信
父传子:
在父组件的子组件标签上绑定一个属性,挂载要传输的变量。在子组件中通过props来接受数据,props可以是数组也可以是对象,接受的数据可以直接使用 props:["属性名"] props:{属性名:数据类型}
子传父:
父组件通过绑定自定义事件,接受子组件传递过来的参数。子组件通过$emit触发父组件上的自定义事件,发送参数
兄弟组件传值:
通过main.js初始化一个全局的$bus,在发送事件的一方通过$bus.$emit(“事件名”,传递的参数信息)发送,在接收事件的一方通过$bus.$on("事件名",参数)接收传递的事件
8.Vuex
- vuex :
是一个专为vue.js开发的状态管理器,采用集中式存储的所有组件状态
五个属性: state、getters、mutations、actions、module
state属性: 存放状态,例如你要存放的数据
getters: 类似于共享属性,可以通过this.$store.getters来获取存放在- state里面的数据
mutations: 唯一能改变state的状态就是通过提交mutations来改变,this.$store.commit()
actions: 异步的mutations,可以通过dispatch来分发从而改变state
modules:store 的子模块,为了开发大型项目,方便状态管理而使用的。
2.数据持久化
vuex里面存放的数据,页面一经刷新会丢失:
解决办法:
存放在localStorage或者sessionStorage里面,进入页面时判断是否丢失,丢失再去localStorage或者sessionStorage里面取;
在vuex中可以通过安装vuex-persistedstate 插件,进行配置就行
9.vue监听和深度监听watch
watch可以让我们监控一个值的变化,从而做出相应的反应。
deep:代表深度监控,不仅监控person变化,也监控person中属性变化。
handler:就是以前的监控处理函数。
通过v.name和v.age获取对象中具体的值。
10.Vue 中 methods,computed, watch 的区别
computed具有缓存性,依赖于属性值,只有属性发生改变的时候才会重新调用
method是没有缓存的,只用调用,就会执行
watch没有缓存性 监听属性 属性值只要发生变化就会执行 可以利用他的特性做一些异步的操作
11.mvvm和mvc
mvvm
概念:MVVM是 Model-View-ViewModel 的缩写,分别对应着:数据,视图,视图模型。Model是我们应用中的数据模型,View是我们的UI视图层,通过ViewModle,可以把我们Modle中的数据映射到View视图上,同时,在View层修改了一些数据,也会反应更新我们的Modle。简单理解就是双向数据绑定,即当数据发生变化的时候,视图也就发生变化,当视图发生变化的时候,数据也会跟着同步变化。
MVC模式概要(典型的框架有angular.js)
1 . 即Model、View、Controller即数据模型、视图、控制器。
View:视图层;
Model:业务数据层;
Controller: 控制器。接收View层传递过来的指令,选取Model层对应的数据,进行相应操作。
12.vue中的事件修饰符、
.stop 阻止事件冒泡
.capture -事件捕获
.self 只是监听触发改元素的事件
.once - 只触发一次
.prevent - 阻止默认事件
13.自定义组件
我用vue开发的所有项目,都是采用组件化的思想开发的。一般我在搭建项目的时候,会创建一个views目录和一个commen目录和一个feature目录,views目录中放页面级的组件,commen中放公共组件(如:head(公共头组件),foot(公共底部组件)等),feature目录内放功能组件(如:swiper(轮播功能组件),tabbar(切换功能组件)、list(上拉加载更多功能组件))
首先,组件可以提升整个项目的开发效率。能够把页面抽象成多个相对独立的模块,解决了我们传统项目开发:效率低、难维护、复用性低等问题。
使用Vue.extend方法创建一个组件,然后使用Vue.component方法注册组件。但是我们一般用脚手架开发项目,每个 .vue单文件就是一个组件。在另一组件import 导入,并在components中注册,子组件需要数据,可以在props中接受定义。而子组件修改好数据后,想把数据传递给父组件。可以采用emit方法。
14.自定义指令
自定义指令分为:全局自定义指令,局部自定义指令。
使用Vue.directive('focus',{bind(el,binding){},inserted(){}})进行全局自定义指令
参数1 :指令的名称
参数2: 是一个对象,这个对象身上,有钩子函数.
【全局指令】
使用 Vue.diretive()来全局注册指令。
【局部指令】
bind(){} 只调用一次,指令第一次绑定到元素时调用
inserted(){}被绑定元素插入父节点时调用
update(){}被绑定元素所在的模板更新时调用,而不论绑定值是否变化
componentUpdated(){}被绑定元素所在模板完成一次更新周期时调用
unbind(){}只调用一次, 指令与元素解绑时调用
15.路由守卫
一、全局路由守卫
二、组件路由守卫
三、路由独享守卫
to:即将要进入的路由对象;from:当前导航正要离开的路由;next:进行管道中的下一个钩子,必须调用,他里面的参数为false就终止执行,为true的话就继续执行,里面是一个路由路径的话跳转至指定的路由;
16.keep-alive
<keep-alive> 标签:
是Vue的内置组件,能在组件切换过程中将状态保留在内存中,取消组件的销毁函数,防止重复渲染DOM。
➡当用它包裹 <router-view> 时,会缓存不活动的组件实例,而不是销毁它们。
➡和 <transition> 相似,它自身不会渲染一个 DOM 元素。
➡使用 <keep-alive> 组件后即可使用 activated() 和 deactivated() 这两个生命周期函数
应用场景:
例如我们将某个列表类组件内容滑动到第100条位置,那么我们在切换到一个组件后再次切换回到该组件,该组件的位置状态依旧会保持在第100条列表处
被keep-alive包裹的动态组件或router-view会缓存不活动的实例,再次被调用这些被缓存的实例会被再次复用,对于我们的某些不是需要实时更新的页面来说大大减少了性能上的消耗,不需要再次发送HTTP请求,但是同样也存在一个问题就是被keep-alive包裹的组件我们请求获取的数据不会再重新渲染页面,这也就出现了例如我们使用动态路由做匹配的话页面只会保持第一次请求数据的渲染结果,所以需要我们在特定的情况下强制刷新某些组件
17.slot
参考地址:https://blog.csdn.net/weixin_43638968/article/details/103923899
1 、slot 基本用法
插槽指允许将自定义的组件像普通标签一样插入内容
2、具名插槽
给具体的插槽命名,并在使用的时候传入插槽的名称
3 、作用域插槽
将定义插槽的变量作用域到使用插槽中
总结
v-slot的出现是为了代替原有的slot和slot-scope
简化了一些复杂的语法。
一句话概括就是v-slot :后边是插槽名称,=后边是组件内部绑定作用域值的映射
18.vue3.0 与 vue2.0 的区别
1、性能提升
更小巧,更快速;支持摇树优化;支持跨组件渲染;支持自定义渲染器。
2、API 变动
除渲染函数API和 scoped-slot 语法之外,其余均保持不变或者将通过另外构建一个兼容包来兼容
- 重写虚拟 DOM 随着虚拟 DOM 重写,减少运行时开销。
项目相关
1.Axios 拦截做过哪些
Axios 拦截分为请求拦截和响应拦截,请求拦截就是在你请求的时候会进行触发!只要是你发送一个 axios 请求就会触发!所以我们主要用它做我们的loading加载和数据的权限验证,包括我们所有的数据预加载也可以实现,响应拦截主要是我们在loading加载和做所有数据加载需要整体的结束,这个时候的结束就需要在数据马上发给前端的时候进行隐藏和结束,包括我们的请求头的设置,后端数据已经发送过来的时候,我们为了确保请求头的传递就必须在看看header 里面是否有你需要的请求,如果有的话,再次进行设置!
2.请写出在vue 中使用promise 封装项目api 接口的思路?
axios 封装了原生的 XHR,让我们发送请求更为简单我们要对 axios 进行进一步的封装 构赋 vue-cli 项目的目录如上,我们在原有的目录基础上新建 api 与 utils文件夹,utils 里新建 request.js 文件,在 request.js 中做了三件事
- 创建 axios,设置 baseURL 与超时时间
- 拦截请求
- 拦截响应
- 路由拦截
网络
1.常见的状态码
① 200:请求成功,浏览器会把响应体内容(通常是html)显示在浏览器中;
② 404:(客户端问题)请求的资源没有找到
400: 语义有误,当前请求无法被服务器理解。
401: 当前请求需要用户验证
403: 服务器已经理解请求,但是拒绝执行它。
③ 500:(服务端问题)请求资源找到了,但服务器内部发生了不可预期的错误;
④ 301/302/303:(网站搬家了,跳转)重定向
⑤ 304: Not Modified,代表上次的文档已经被缓存了,还可以继续使用。如果你不想使用本地缓存可以用Ctrl+F5 强制刷新页面
2.输入url到页面发生了什么
1、浏览器的地址栏输入URL并按下回车。
2、浏览器查找当前URL是否存在缓存,并比较缓存是否过期。
3、DNS解析URL对应的IP。
4、根据IP建立TCP连接(三次握手)。
5、HTTP发起请求。
6、服务器处理请求,浏览器接收HTTP响应。
7、渲染页面,构建DOM树。
8、关闭TCP连接(四次挥手)。
***三次握手:
➡第一次握手客户端向服务端发送连接请求报文段
➡第二次握手服务端收到连接请求报文段后,如果同意连接,则会发送一个应答
➡第三次握手当客户端收到连接同意的应答后,还要向服务端发送一个确认报文段,表示: 服务端发来的连接同意应答已经成功收到。
为什么连接建立需要三次握手,而不是两次握手?
防止失效的连接请求报文段被服务端接收,从而产生错误。
***TCP 四次挥手:
➡第一次挥手若 A 认为数据发送完成,则它需要向 B 发送连接释放请求。该请求只有报文头
➡第二次挥手B 收到连接释放请求后,会通知相应的应用程序,告诉它 A 向 B这个方向的连接已经释放
➡第三次挥手当 B 向 A 发完所有数据后,向 A 发送连接释放请求
➡第四次挥手A 收到释放请求后,向 B 发送确认应答
性能相关
- 图片懒加载原理
什么是图片懒加载?
当打开一个有很多图片的页面时,先只加载页面上可视区域的图片,等滚动到页面下面时,再加载所需的图片。这就是图片懒加载。
减少或延迟请求数,缓解浏览器的压力,增强用户体验。
图片懒加载的基本原理
1、设置图片src属性为同一张图片,同时自定义一个data-src属性来存储图片的真实 地址
2、 页面初始化显示的时候或者浏览器发生滚动的时候判断图片是否在视野中
3、 当图片在视野中时,通过js自动改变该区域的图片的src属性为真实地址
2.前端优化中的路由懒加载
写法参考地址:https://blog.csdn.net/weixin_43638968/article/details/109093613
懒加载:
也叫延迟加载,即在需要的时候进行加载,随用随载。 使用懒加载的原因: vue 是单页面应用,使用webpcak打包后的文件很大,会使进入首页时,加载的资源过多,页面会出现白屏的情况,不利于用户体验。运用懒加载后,就可以按需加载页面,提高用户体验
- 函数防抖和函数节流
参考地址:https://blog.csdn.net/weixin_43638968/article/details/107567601
函数防抖(debounce)
在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。
函数节流(throttle)
规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。
- vue-cli4打包优化(webapck优化)
一、去除生产环境sourceMap
二、对资源文件进行压缩
三、图片压缩
四、去除console.log打印
五、只打包改变的文件
六、开启分析打包日志
七、完整配置
工具
1.git基础
Git是一个版本管理控制系统,它可以在任何时间点,将文档的状态作为更新记录保存起来,也可以在任何时间点,将更新记录恢复回来。
Git基本工作流程:在工作区中把操作的文件通过git add . 转到暂存区中,暂存区中把临时存放的修改文件通过git commit –m 描述信息提交到仓库
基本操作:
➡git init 初始化git仓库、git status 查看文件状态、git add 文件列表 存到暂存区
➡git commit -m 提交信息 向仓库中提交代码、git log 查看提交记录
➡git checkout 文件名:用暂存区中的文件覆盖工作目录中的文件
➡git rm --cached 文件名:将文件从暂存区中删除
➡git reset --hard commitID:将 git 仓库中指定的更新记录恢复出来,并且覆盖暂存区和工作目录
➡git branch 查看分支、
➡git branch 分支名称 创建分支、
➡git checkout 分支名称 切换分支
➡git merge 来源分支 合并分支(备注:必须在master分支上才能合并develop分支)
➡git branch -d 分支名称 删除分支(分支被合并后才允许删除)(-D 强制删除)
➡git stash 存储临时改动、git stash pop 恢复改动
➡git push 地址 分支名称提交到远程仓库
➡git pull 远程仓库地址 分支名称拉取远程仓库中最新的版本