1.多种方式实现数组去重,扁平化、对比优缺点
数组去重
var arr =[1,43,2,4,5,7,4,2,3,8]
//去重去
arr = [1,43,2,4,5,7,3,8]
传统方式,for循环实现
function dedupe(arr){
var rets = [];
for(var i =0; i<arr.length;i++){
if(!rets.includes(arr[i])){
rets.push(arr[i])
}
}
return rets
}
//方法二:forEach方法实现
function dedupe(arr){
var rets = [],obj ={};
arr && arr.forEach(function(item){
if(!obj[item]){
obj[item]=item;
rets.push(item);
}
})
return rets
}
ES6方法实现
//es6提供的新的数据结构Set,类似数组,但是成员的值都是唯一的,没有重复值。
function dedupe(arr){
var newSet = new Set(arr); //arr变成了set的数据结构,并去除了其中重复的元素
return Array.from(newSet);//Array.from方法将set数据结构转为数组结构
}
复杂数据结构的数组去重
[1,2,{a:1},3,2,{a:1},[1,2],[1,2]] //数组中的元素包含对象和数组
function unique(arr){
const hash ={};
const res = [];
for(let i =0;i<arr.length;i++){
if(hash[arr[i]] == null){
res.push(arr[i])
hash(arr[i]) = true
}
}
return res
}
unique([1,2,{a:1},3,2,{a:1},[1,2],[1,2]])
// 1,2,{a:1},3,[1,2]
数组扁平化
数组的扁平化,就是将一个嵌套多层的数组array(嵌套可以使任何层数)转换为只有一层的数组
var arr =[1,2,3,[4,3,[2,7],2],5[5,9,10],7];
//去扁平化后
arr = [1,2,3,4,3,2,7,2,5,5,9,10,7]
循环递归实现
// for循环 如果子元素还是数组,则递归调用该方法
function flatten(arr){
var rets = [];
for(var i =0; i<arr.length;i++){
if(Array.isArray(arr[i])){
rets = rets.concat(flatten(arr[i]))
}else{
rets.push(arr[i])
}
}
return rets;
}
// 使用forEach
function flatten(arr){
var rets = [];
arr && arr.forEach(function(item) =>{
if(Array.isArray(item)){
rets = rets.concat(flatten(item));
}else{
rets.push(item)
}
})
return rets;
}
使用reduce简化代码
function flatten(arr){
return arr.reduce(function(pre,item){
return pre.concat(Array.isArray(item)?flatten(item):item);
},[])
}
如果数组元素为数字,则可以使用toString方法。
function flatten(arr){
var newArr = arr.toString().split(',');
return newArr.map(function(item){
return + item //将字符串转为数字
})
}
2.多种方式实现深拷贝、对比优缺点
如何区分深拷贝和浅拷贝
简单来说,就是假设B复制了A ,当修改A时,看B是否发生变化,如果B跟着变,说明是浅拷贝。反之,B没变,那就是深拷贝。
(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[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)借用JQ的extend方法
$.extend([deep],target,object1[,objectN])
//deep 表示是否深拷贝,true为深拷贝,false 为浅拷贝
// target Object 类型目标对象,其他对象的成员属性将被附加到该对象上
object1 objectN可选。 Object类型 第一个以及第一N个被合并的对象。
let a = [0,1,[2,3],4],
b=$.extend(true,[],a);
a[0] =1;
a[2][0] = 1;
console.log(a,b)
3.手写函数柯里化工具函数、并理解其应用场景和优势
简单来说,就是固定一些参数,返回一个接受剩余参数的函数
其实就是使用闭包返回一个延迟执行函数
//这个例子是柯里化,也可以说是部分应用
function add(num1,num2){
return num1 + num2
}
function curry(func){
let args = [] .slice.call(arguments,1);
return function(){
let innerArgs = [] .slice.call(arguments)
let finalArgs = [...args,...innerArgs];
return func.apply(null,finalArgs)
}
}
// 得到延迟执行函数
let curriedAdd = curry(add,5)
// 可以多次复用得到的函数
curriedAdd(1); // 6
curriedAdd(2); //7
下面再来实现curriedAdd(1)(2,3)(4)的柯里化函数
function aidCurry(func){
let args = [] .slice.call(arguments,1);
return function(){
return func.apply(null,[...arge,...arguments]);
}
}
function curry(func,length){
length = length || func.length
return function(){
//传入参数为0 表示返回结果
if(arguments.length !=0 && arguments.length < length){
let finalArgs = [func, ...arguments];
// 参数的数量不足时,利用闭包将已传入参数存起来
return curry(aidCurry.apply(null,finalArgs),length-arguments.length);
}else{
//参数数量足够了或者中途结束,则返回结果
return func.apply(null,arguments);
}
};
}
// length 为4 表示当传入参数达到4个时返回结果
let curriedAdd = curry(function add){
retutrn [...arguments].reduce((acc,cur) => acc + cur);
},4);
// 传入参数为4个
curriedAdd(1),(2,3)(4); // 10
//中途结束
curriesAdd(1)(2,3)() // 6
//error:not a function
curriedAdd(1)(2)()(3);//使用() 就表示返回结果已经不是函数
curriedAdd(1))(2,3)(4)(); //传入参数已经为4个,所以返回结果了 已经不是函数
4.手写防抖和节流工具函数、并理解其内部原理和应用场景
防抖函数
什么是函数防抖
函数防抖:就是指触发事件后,在n秒内函数只能执行一次,如果触发事件后在n秒内又触发了事件,则会重新计算函数延迟执行之间。为什么需要做函数防抖
前端开发中,有一些时间事件,常见用onresize
scroll
mousemove
mpusehover
等等,会被频繁触发,如不作限制的话,有可能一秒之内执行很多次,如果在这些函数内部执行了其他函数,尤其是执行对DOM 操作的函数,那不仅会浪费资源,还会降低程序运行速度,甚至造成浏览器卡死、崩溃,这些问题显然是致命的。
函数防抖如何解决上述问题
函数防抖的重点,是一个需要setTimeout
来辅助实现,延迟需要执行的代码,如果方法多次触发,则把上次记的延迟执行代码clearTimeout
清掉,重新开始计时。若计时期间事件没有被重新触发,等延迟时间结束,则执行目标代码。
函数防抖代码实现
function debouce(fn,wait){
var timer = null;
return function(){
if(timer != null){
clearTimeout(timer)
}
timer = setTimeout(fn,wait);
}
}
function handle(){
console.log(Math.random())
}
window.addEventListener('resize',debounce(handle,1000))
函数节流
什么是函数节流
限制一个函数在一定时间段内只能执行一次
为什么需要函数节流
同函数防抖
函数节流代码实现
// 时间戳方案
function throttle(fn,wait){
var pre = Date.now();
return function(){
var contect = this;
var args = arguments;
var now = Date.now();
if(now - pre > = wait){
fn.apply(context,args);
pre = Date.now();
}
}
}
function handle(){
console.log(Math.random());
}
window.addEventListener('mousemove',throttle(handle,1000));
// 定时器方案
function throttle(fn,wait){
var timer = null;
return function(){
var context = this;
var args = arguments;
if(!timer){
timer = setTimeout(function(){
fn.apply(context,args);
timer = null
},wait)
}
}
}
function handle(){
console.log(Math.random())
}
window.addEventListener('mousemove',throttle(handle,1000));
函数节流的使用场景
1、懒加载,滚动加载,加载更多或监听滚动条的位置
2、百度搜索框,搜索联想功能
3、防止高频点击提交、防止表单重复提交
5.实现一个sleep函数
// 方法一
function sleep1(ms,callback){
setTimeout(callback,ms)
}
// sleep 1s
sleep1(1000,()=>{
console.log(1000)
})
//方法二
function sleep2(ms){
return new Promise(function(resolve,reject){
setTimeout(resolve,ms)
})
}
sleep2(1000).then(() =>{
console.log(2000)
})
//方法三:
function sleep3(ms){
return new Promise(function(reslove,reject){
setTimeout(reslove,ms)
})
}
asyns function init(){
await sleep3(1000)
}
init().then(() =>{
console.log(3000)
})
网友评论