算法-JavaScript编码能力

1.多种方式实现数组去重、扁平化、对比优缺点

数组去重

  1. set和from
let arr = [12,43,23,43,68,12];
let item = new Set(arr);
console.log(item);//结果输出的是一个对象

//使用Array.from转成数组

let arr = [12,43,23,43,68,12];
let item = Array.from(new Set(arr));
console.log(item);// [12, 43, 23, 68]
  1. set和…
let arr = [12,43,23,43,68,12];
let item = [...new Set(arr)];
console.log(item);//[12, 43, 23, 68]
  1. indexOf
1  var arr = [2, 8, 5, 0, 5, 2, 6, 7, 2]
2   var newArr = []
3   for (var i = 0; i < arr.length; i++) {
4     if (newArr.indexOf(arr[i]) === -1) {
5       newArr.push(arr[i])
6     }
7   }
8   console.log(newArr) // 结果:[2, 8, 5, 0, 6, 7]

数组扁平化:是指将一个多维数组变为一维数组

[1, [2, 3, [4, 5]]]  ------>    [1, 2, 3, 4, 5]

**思路:**遍历数组arr,若arr[i]为数组则递归遍历,直至arr[i]不为数组然后与之前的结果concat。

  1. 遍历数组每一项,若值为数组则递归遍历,否则concat。
  • reduce是数组的一种方法,它接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。
function flatten(arr) {  
    return arr.reduce((result, item)=> {
        return result.concat(Array.isArray(item) ? flatten(item) : item);
    }, []);
}

2)调用数组的toString方法,将数组变为字符串然后再用split分割还原为数组

function flatten(arr) {
    return arr.toString().split(',').map(function(item) {
        return Number(item);
    })
} 
  1. 和上面的toString一样,join也可以将数组转换为字符串
function flatten(arr) {
    return arr.join(',').split(',').map(function(item) {
        return parseInt(item);
    })
}
  1. 递归
function flatten(arr) {
    var res = [];
    arr.map(item => {
        if(Array.isArray(item)) {
            res = res.concat(flatten(item));
        } else {
            res.push(item);
        }
    });
    return res;
}

5) 扩展运算符:es6的扩展运算符能将二维数组变为一维

function flatten(arr) {
    while(arr.some(item=>Array.isArray(item))) {
        arr = [].concat(...arr);
    }
    return arr;
}

2.多种方式实现深拷贝、对比优缺点

数组浅拷贝:// 浅拷贝,拷贝的是属性值。假如源对象的属性值是一个对象的引用,那么它也只指向那个引用

  • 还有浅拷贝数组的话可以利用,splice() 和 slice() 这两个方法。他们的区别一个是splice可以改变数组本身,slice不能改变数组本身。
  • **Array.from() **方法从一个类似数组或可迭代对象中创建一个新的,浅拷贝的数组实例。

数组深拷贝:// 深拷贝,遍历到到每一项都是值类型时可以直接赋值.
1)

a =[1,2,3];
let b =[]
a.forEach(val => b.push(val));b.push('change');
console.log('a:'+a,'b:'+b)
// 结果VM167:5 a:1,2,3      b:1,2,3,change
  1. 通过 ** JSON.parse() 和 JSON.stringify() ** 实现对象的深拷贝。但对于值为 undefined 的值不进行拷贝
var test2 = JSON.parse(JSON.stringify(test));
  1. 递归函数
function deepClone(obj) {

var objClone = Array.isArray(obj) ? [] : {}

if (obj && typeof obj === 'object') {
for (key in obj) {

if (obj.hasOwnProperty(key)) {

//判断obj的子元素是否为object对象,如果是则就递归拷贝

if (obj[key] && typeof obj[key] === 'object') {

objClone[key] = deepClone(obj[key])

} else {

//如果不为对象就直接拷贝

objClone[key] = obj[key]

}

}

}

}

return objClone

}

3.手写函数柯里化工具函数、并理解其应用场景和优势
  • 在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。这个技术以逻辑学家 Haskell Curry 命名的。
// 这是一个接受3个参数的函数
const add = function(x, y, z) {
  return x + y + z
}
// 接收一个单一参数
const curryingAdd = function(x) {
  // 并且返回接受余下的参数的函数
  return function(y, z) {
    return x + y + z
  }
}
// 调用add
add(1, 2, 3)

// 调用curryingAdd
curryingAdd(1)(2, 3)
// 看得更清楚一点,等价于下面
const fn = curryingAdd(1)
fn(2, 3)

//手写柯里化工具函数

function curry(fn, ...args) {
  // 判断参数个数是不是等于原函数参数个数
  // 如果是,直接返回调用结果
  if ([...args].length >= fn.length) {
    return fn(...args);
  } else {
    // 如果不是,则返回一个函数
    return (...params) => {
      // 将前面传的全部参数传给curry,回到第一步的if判断,直到参数个数满足要求
      return curry(fn, ...args, ...params);
    };
  }
}

4.手写防抖和节流工具函数、并理解其内部原理和应用场景

节流(throttle)

  • 规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。
  • **场景:**scroll滚动事件,每隔特定描述执行回调函数input输入框,每个特定时间发送请求或是展开下拉列表
  • 节流重在加锁** flag = false**
function throttle(fn, delay) {
    let flag = true,
        timer = null
    return function(...args) {
        let context = this
        if(!flag) return
        
        flag = false
        clearTimeout(timer)
        timer = setTimeout(function() {
            fn.apply(context,args)
            flag = true
        },delay)
    }
}

防抖(debounce)

  • 在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。
  • **场景:**浏览器窗口大小resize避免次数过于频繁登录、发短信等按钮避免发送多次请求、文本编辑器实时保存
  • 防抖重在清零 ** clearTimeout(timer)**
function debounce(fn, delay) {
    let timer = null
    return function(...args) {
        let context = this
        if(timer) clearTimeout(timer)
        timer = setTimeout(function(){
            fn.apply(context,args)
        },delay)
    }
}

总结

  • 函数节流:是确保函数特定的时间内至多执行一次。 函数防抖:是函数在特定的时间内不被再调用后执行。

5.实现一个sleep函数
function sleep(time) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(time)
    }, time)
  })
}
 
sleep(5000).then(
  (time) => { 
    console.log(`你睡了${time / 1000}s,然后做你的事情`) 
  }
)
6.call、apply、bind

call

// 不覆盖原生call方法,起个别名叫myCall,接收this上下文context和参数params
Function.prototype.myCall = function (context, ...params) {
  // context必须是个对象并且不能为null,默认为window
  const _this = typeof context === "object" ? context || window : window;
  // 为了避免和原有属性冲突,定义一个Symbol类型的属性
  const key = Symbol();
  // call方法的目的是改变函数的this指向,函数的this指向它的调用者,也就是说我们的目标是改变函数的调用者。
  // 下面的this就是函数本身,给_this增加一个名为[key]的方法指向this,就能用_this来调用this了
  _this[key] = this;
  const result = _this[key](...params);
  // 获取函数执行结果后,删除以上添加的属性
  delete _this[key];
  return result;
};

apply:和call的区别在于第二个参数

Function.prototype.myApply = function (context, params) {
  return this.myCall(context, ...params);
};

bind:和call的区别在于不立即执行,返回一个函数即可

Function.prototype.myBind = function (context, ...params) {
  const _this = this;
  // 返回的函数也能接收参数,但是是放在params后面
  return function (...args) {
    return _this.myCall(context, ...[...params, ...args]);
  };
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值