如题JS手写防抖,节流,对象深拷贝,call/apply/bind,new,数组map/filter,数组扁平化实现代码
防抖
高频触发规定时间后触发一次,若等待时间内触发了时间则从新计算
//防抖
debounce(fn, delay) {
let timer = null
return function() {
// 先清除定时器
if(timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
fn(arguments)
}, delay)
}
}
// test code
function inputHandle(e) {
console.log(e[0].target.value)
}
document.getElementById('debonce-iunput').addEventListener('input', debounce(inputHandle, 2000), false);
节流
高频触发规定时间内只执行一次
function throttle(fn, delay) {
let prev = +new Date() // 定义一个初始时间
return function() {
let now = +new Date() // 当前时间减去初始时间大于延迟时间执行
if(now - prev >= delay) {
fn(arguments)
prev = +new Date() // 重新赋值当前时间为初始时间
}
}
}
// test code
function inputHandle2(e) {
console.log(e[0].target.value)
}
document.getElementById('throttle-input').addEventListener('input', throttle(inputHandle2, 1000), false)
对象深拷贝
返回一个新对象而不是原对象的引用
function cloneDeep1(source) { // 对象深拷贝
let obj = Array.isArray(source) ? [] : {}
for(let key in source) {
if(Object.prototype.hasOwnProperty.call(source, key)) {
if(typeof source[key] === 'object' && source[key] !== null) { // 如果当前属性值是对象,递归当前属性值
obj[key] = cloneDeep1(source[key])
} else {
obj[key] = source[key]
}
}
}
return obj
}
call、apply
改变当前上下文
Function.prototype.myCall = function(target, ...args) {// 变成apply的话将...args改成args
if(typeof this !== 'function') { // 当前调用者不是函数的话报错
throw new Error('this should be a function')
}
const fn = Symbol('fn') // Symbol的唯一性,防止fn覆盖已有属性
target = target || window //如果没传target的话把window赋值给target
target[fn] = this // 将this函数赋值给target fn属性,当target调用时this就会变成target。这一步是改变this关键步骤
const result = target[fn](...args) // target调用fn执行 此时this已变成target
delete target[fn] // 删除定义的fn属性
return result //将结果返回
}
bind
也是改变当前上下文,但是返回的是一个函数还要考虑到被new实例化的问题
Function.prototype.myBind = function(target, ...args) {
if(typeof this !== 'function') {
throw new Error('bind should be a function')
}
let self = this
//new 优先级
let fbound = function() {
//bind()返回的函数也接收参数,这两部分的参数都要传给返回的函数
self.apply(this instanceof self ? this : target, args.concat(Array.prototype.slice(arguments)))
}
// 继承原型上的属性和方法
fbound.prototype = Object.create(self.prototype)
return fbound
}
手写一个new实现
创建一个对象
// 手写一个new
function MyNew(foo, ...args) {
// 1.创建一个新的对象并继承该对象的prototype
let obj = {}
obj.__proto___ = Object.create(foo.prototype)
// 2.执行构造函数,方法内的this指定为新实例
let result = foo.apply(obj, args)
//3.返回该实例,若构造函数有返回一个对象那么返回该对象
return typeof result === 'object' && result !== null ? result : obj
}
// 测试手写new
function FTest(name) {
this.name = name
}
let objTest = MyNew(FTest, 'test')
console.log('test mynew====' + myObj.name) // test
手写一个map
map接收一个函数参数,函数接收currentVal, index, array三个参数,最后返回新数组
Array.prototype.myMap = function(fn) {
let newarr = []
for(let i = 0, len = this.length; i<len; i++) {
newarr.push(fn(this[i], i, this))
}
return newarr
}
手写一个filter
filter接收一个函数参数, 函数第一个参数接收判断条件第二个参数接收一个对象绑定内部this变量,返回符合条件数组
Array.prototype.myFilter = function(fn, obj) {
let newarr = []
for(let i = 0, len = this.length; i<len; i++) {
if(obj) {
if(fn.call(obj, this[i])) {
newarr.push(this[i])
}
} else {
if(fn(this[i])) {
newarr.push(this[i])
}
}
}
return newarr;
}
数组扁平化
多维数组降成一维
let arr = [1,2,3,4, [1,4,46,7], 12,[123,12312,123,3,3, [1,2,4]]]
while(arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr) // 只要还有一个item是数组就通过拓展符遍历然后重新给arr赋值直到数组变成一维数组为止
}