1.什么是回流?什么情况会触发回流?
浏览器会把 html 解析成dom,把css解析成cssom,dom+cssom合在一起就是 render tree
回流:当render tree 中 '几何元素发生改变的时候',浏览器重新渲染 dom 元素的过程。
几何元素:元素的结构、尺寸或位置、元素内容变化、字体大小等
2.什么是重绘?什么情况会触发重绘?
当元素的样式发生改变(颜色、背景等),但不会影响布局改动,浏览器会将新样式进行渲染,这个过程是重绘
3.说出深拷贝的三种方式
1.利用递归函数实现深拷贝
function deepClone(obj){
let objClone = Array.isArray(obj)?[]:{};
if(obj && typeof obj==="object" ){
for (key in obj){
if (obj.hasOwnProperty(key)){ //判断ojb子元素是否为对象,如果是,递归复制
if(obj[key]&&typeof obj[key] ==="object"){
objClone[key] = deepClone(obj[key]);
}else{ //如果不是,简单复制
objClone[key] = obj[key];
}
}
}
} return objClone;
}
let a=[1,2,3,4]
b=deepClone(a);
a[0]=2;
console.log(a,b);
2.利用JSON对象的parse和stringify
function deepClone(obj){
let _obj = JSON.stringify(obj),
objClone = JSON.parse(_obj);
return objClone
}
let a=[0,1,[2,3],4],
b=deepClone(a);
a[0]=1;
a[2][0]=1;
console.log(a,b);
3.利用 lodash 的 _.cloneDeep()
/**
* 判断是否是基本数据类型
* @param value
*/function isPrimitive(value){ return (typeof value === 'string'|| typeof value ==='number'|| typeof value === 'symbol' || typeof value === 'boolean')}/**
* 判断是否是一个js对象
* @param value
*/function isObject(value){ return Object.prototype.toString.call(value) === "[objectObject]"}/**
* 深拷贝一个值
* @param value
*/function cloneDeep(value){ // 记录被拷贝的值,避免循环引用的出现
let memo = {}; function baseClone(value){
let res; // 如果是基本数据类型,则直接返回`
if (isPrimitive(value)){return value; // 如果是引用数据类型,我们浅拷贝一个新值来代替原来的值 }else if(Array.isArray(value)){
res = [...value];
}else if(isObject(value)){
res = {...value};
} // 检测我们浅拷贝的这个对象的属性值有没有是引用数据类型。如果是,则递归拷贝
Reflect.ownKeys(res).forEach(key=>{
if (typeof res[key] === "object" && res[key]!== null){ //此处我们用memo来记录已经被拷贝过的引用地址。以此来解决循环引用的问题
if(memo[res[key]]){
res[key] = memo[res[key]];
}else{
memo[res[key]] = res[key];
res[key] = baseClone(res[key])
}
}
}) return res;
} return baseClone(value)
}
验证cloneDeep是否能解决循环应用的问题:
var obj = {};
var b = {obj};
obj.b = bvar
copy= cloneDeep(obj);
console.log(copy);
4.说出修改 this 指向的三种方式以及区别?
Call() 、 apply() 、 bind()
共同点:
1.都是用于改变函数 this 指向的方法
2.第一个参数都是 this 要指向的对象
区别:
1.bind 调用的时候不会立即执行 , 返回值是一个新函数 , 多数与定时器连用; call 和 apply 调用立即执行
2.call() 接受的参数列表,apply() 接受的是参数数组, bind()的参数列表是创建时指定的参数序列
5.说出五种遍历数组的方法
1.forEach forEach 是最简单、最常用大的数组遍历方法,它提供一个回调函数,可以用于处理数组的每一个元素,默认没有返回值。
let arr =[10,20,30,40,50]
let res = 0
arr.forEach(item => {
item >= 30 ? res++ : res
})
以上是个简单的例子,计算出大于等于30的元素的个数。<br>循环的元素,第二个是该元素下标,第三个是数组本身。三个参数均可选。
2.map map,从字面上理解,是映射,即数组元素的映射。它提供一个回调函数,参数依次为处于当前循环的元素、该元素下标、数组本身,三者均可选。默认返回一个数组,这个新数组的每一个元素的都是原数组元素执行了回调函数之后的返回值。 map方法不改变原数组。
let arr = [10,20,30,40,50]
let res = arr.map((item,index)=>{
return item*(index + 1)
})
console.log(arr)
以上是个简单的例子,把原数组的每一项乘以自身下标+1的数。
3.reduce reduce方法有两个参数,第一个参数是一个回调函数(必须),第二个参数是初始值(可选)。回调函数有四个参数,依次为本轮循环的累计值、当前循环的元素(必须),该元素的下标(可选),数组本身(可选)。 reduce方法,会让数组的每一个元素都执行一次回调函数,并将上一次循环时回调函数得到返回值作为下一次循环的初始值,最后将这个结果返回。 如果没有初始值,则reduce会将数组的第一个元素作为循环开始的初始值,第二个元素开始执行回调函数。 最常用、最简单的场景,是数组元素的累加、累乘。
let arr = [1,2,3,4,5]
let res = arr.reduce((val,item,index)=>{
return val+item*inex
},10)
reduce方法不改变原数组
4.filter filter,过滤,即对数组元素的一个条件筛选。它提供一个回调函数,参数依次为处于当前循环的元素、该元素下标、数组本身、三者都可选。默认返回一个数组,原数组的元素执行了回调函数之后返回值若为true,则会将这个元素放入返回的数组中。 filter方法不改变原数组
let arr = [1,2,3,4]
let res = arr.filter((item,index)=>{
return item*indedx >= 3
})
console.log(arr)
以上是一个简单的例子,筛选出原数组中,自身乘以下标大于等于3的元素。
5.some、every some方法和every的用法非常类似,提供一个回调函数,参数依次为处于当前循环的元素、该元素下标、数组本身,三者都可选。 数组的每一个元素都会执行回调函数,当返回值全部为true时,every方法会返回true,只要有一个为false,every方法返回false。当有一个为true时,some方法返回true,当全部为false时,some方法返回false some、every方法不改变原数组。
let arr = [1,2,3,4,5]
let resSome = arr.some((item,inddex)=>{
return item*indedx >= 100
})
let resEvery = arr.every((item,index)=>{
return item*index >= 10
})
console.log(resSome)