欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

手动实现new、深拷贝、防抖、节流等js原生方法

程序员文章站 2022-06-09 17:02:51
...

一、手动实现new操作符

new操作符的实现过程包括以下几个步骤:
1、创建一个空对象
2、将空对象的__proto__指向构造函数的 prototype
3、将空对象的this值绑定到构造函数上;
4、通过new创建的对象,其prototype最终将指向构造函数的prototype
5、如果函数没有显式返回对象类型,那么new会返回这个this
根据new操作符的作用原理,我们可以创建以下函数来实现new

function New(func){
	let res = {}
	if(func.prototype !== null){
		res.__proto__ = func.prototype
	}
	let ret = func.apply(res, Array.from(arguments))
	if(['function', 'object'].includes(typeof ret) && ret !== null){
		return ret
	}
	return res
}

二、手动实现一个深拷贝

在这里首先要针对对象,区分一下赋值浅拷贝深拷贝的区别。
赋值:当我们把对象赋值给一个变量的时候,赋的实际上是对象在栈中的地址,而不是在堆中的数据。
浅拷贝:浅拷贝是按位拷贝对象,它会获得一个新对象。如果对象属性是基本数据类型,那么就会获得基本数据类型的属性;如果是引用类型,那么就会获得引用地址。浅拷贝获得的新旧对象,其第一层数据是不共享的。如果有嵌套数据,那么新旧对象会共享嵌套数据,修改新对象会影响原对象。
深拷贝:深拷贝也是获得一个全新的对象。和浅拷贝不同的是,深拷贝获取到的对象,每一层数据都获得一份拷贝,新旧数据完全相互独立,互不影响。
深拷贝方法
对于Json安全(也就是一个可以被序列化成Json字符串,并且可以根据这个字符串解析出结构和值完全一样的对象)的对象来说,可以采用以下方法:

let newObj = JSON.parse(JSON.stringify(obj))

对于其他对象,可以手写一个深拷贝方法:

//深拷贝方法
function deeClone(obj){
	let newObj 
	if(typeof obj === 'object' && obj !== null){ //如果是引用类型
		newObj = obj.constructor === Array? [] : {}
		for(i in obj){ //遍历对象属性的key值
			newObj[i] = typeof obj[i] === 'object' ? deeClone(obj[i]) : obj[i]
		}
	}else { //如果是基本数据类型
		newObj = obj
	}
	return newObj
}

三、手动实现instanceOf

instanceOf操作符常用来判断一个实例是否属于某种类型。

// 判断 foo 是否是 Foo 类的实例
 function Foo(){}
 var foo = new Foo();
 console.log(foo instanceof Foo)//true

实现instanceOf操作符,关键在于判断实例的__proto__是否与指定对象类型的prototype相同

//手动实现instanceof
function instanceOf(left, right){
	let proto = left.__proto__
	let prototype = right.prototype
	while(true){ //追溯到原型链顶端
		if(proto === null) return false
		if(proto === prototype) return true
		proto = proto.__proto__
	}
}

四、手动实现防抖函数

针对页面高频度触发事件问题,有两种常用的解决方法,防抖和节流。
防抖
当一次事件发生后,事件处理器要等一定阈值的时间,如果这段时间过去后 再也没有事件发生,就处理最后一次发生的事件。假设还差 0.01 秒就到达指定时间,这时又来了一个事件,那么之前的等待作废,需要重新再等待指定时间。

//手动实现防抖函数
function debounce(fn, wait, immedite){
	let timer;
	let debounced =  function(){
		let context = this
		if(timer){
			clearTimeout(timer)
		}
		if(immedite){ // 是否立即执行
			let callNow = !timer
			if(callNow){
				fn.apply(context,arguments)
			}
			timer = setTimeout(() => {
				timer = null //闭包引用了timer,手动置空使其能被垃圾回收机制回收
			}, wait);
		}else{
			timer = setTimeout(()=>{
				fn.apply(context,arguments)
			},wait)
		}
	}
	debounced.cancel = function(){ //取消立即执行
		clearTimeout(timer)
		timer = null
	}
	return debounced
}

五、手动实现节流函数

可以理解为事件在一个管道中传输,加上这个节流阀以后,事件的流速就会减慢。实际上这个函数的作用就是如此,它可以将一个函数的调用频率限制在一定阈值内,例如 1s,那么 1s 内这个函数一定不会被调用两次
节流:限制一个函数在一定时间内只能执行一次

//节流函数
function throttle(fn, wait){
    let prev = new Date() 
    return function(){
        let args = arguments
        let now = new Date()
        if(now - prev > wait){
            fn.applay(this, args)
            prev = new Date()
        }
    }
}

//操作函数
function handle(){
    console.log(Math.random())
}

//实现函数防抖⬇️,表示监听mouseover事件。在5秒内,mouseover事件只会被触发一次
window.addEventListener("mouseover", throttle(handle, 5000))

相关标签: javascript