JavaScript编码能力篇

2020-04-27  本文已影响0人  阿羡吖

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)
})
上一篇下一篇

猜你喜欢

热点阅读