JavaScript 手写源码(instanceof、深拷贝、节流、防抖)
一、instanceof 的实现
使用 instanceof 操作符,如果一个实例的原型链中出现过相应的构造函数的原型,则 instanceof 返回 true。
instanceof 主要的实现原理就是只要右边变量的 prototype 在左边变量的原型链上即可。 因此,instanceof 在查找的过程中会遍历左边变量的原型链,直到找到右边变量的 prototype,如果查找失败,则会返回 false,告诉我们左边变量并非是右边变量的实例。
function instanceof1(L,R) {
L = L.__proto__;
while(L != null) {
if(L === R.prototype) {
return true;
}
L = L.__proto__;
}
return false;
}
二、浅拷贝
// 浅拷贝
function shallowClone(obj) {
const result = {};
for(let key in obj) {
console.log('ddddd',key);
if(obj.hasOwnProperty(key)) {
result[key] = obj[key];
}
}
return result;
}
测试代码:
let obj = {
b: {
c: [1, 5, 11, 23, 422]
},
d: function() {
console.log('hello world');
}
};
const result = shallowClone(obj);
console.log(result);
const newObj = Object.assign({},obj);
console.log(newObj);
三、深拷贝
// 深拷贝
function deepClone(obj) {
if(typeof obj != 'object' || obj == null) {
return obj;
}
const result = Array.isArray(obj) ? [] : {};
for(let key in obj) {
// 自有属性
if(obj.hasOwnProperty(key)) {
const type = typeof obj[key];
if(type == 'object' && obj[key] != null) {
result[key] = deepClone(obj[key]);
} else {
result[key] = obj[key];
}
}
}
return result;
}
测试代码:
var data = {
age: 18,
name: "liuruchao",
education: ["小学", "初中", "高中", "大学", undefined, null],
likesFood: new Set(["fish", "banana"]),
friends: [
{ name: "summer", sex: "woman"},
{ name: "daWen", sex: "woman"},
{ name: "yang", sex: "man" } ],
work: {
time: "2019",
project: { name: "test",obtain: ["css", "html", "js"]}
},
play: function() { console.log("玩滑板"); }
}
console.log(deepClone(data));
解决循环引用问题:
// 解决循环引用问题
function deepClone2(obj, hash = new WeakMap()) {
const type = typeof obj;
if(type != 'object' || obj == null) {
return obj;
}
if(hash.has(obj)) {
return hash.get(obj);
}
const result = Array.isArray(obj) ? [] : {};
for(let key in obj) {
if(obj.hasOwnProperty(key)) {
if(typeof obj[key] != 'object' || obj[key] == null) {
result[key] = obj[key];
} else {
// 首次调用时,weakMap为空,不会走上面那个if(hash.has())语句,如果待拷贝对象中有属性也为对象时,
// 则将该待拷贝对象存入weakMap中,此时的健值和健名都是对该待拷贝对象的引用
hash.set(obj, obj)
result[key] = deepClone2(obj[key], hash);
}
}
}
return result;
}
测试代码:
var data2 = {
name: 'foo',
child: null,
}
data2.child = data2;
console.log(deepClone2(data2));
四、函数防抖
- 当持续触发事件时,一定时间内没有再触发事件,事件处理函数才会执行一次;
- 如果在设定的时间来到之前,又一次触发了事件,则会取消上次事件,重新开始计时;
使用场景:resize、scroll、输入框内容校验等。
function debounce(handle,wait) {
let timer = null;
return function () {
if(timer != null) {
clearTimeout(timer);
}
timer = setTimeout(handle, wait);
}
}
五、节流
当持续触发事件时,保证一定时间内只调用一次事件处理函数。通俗解释就比如:我们把水龙头打开,水哗哗的往外流,秉着节约的原则,我们要把水龙头关小,最好是如我们的心愿按照一定的规律,在某个时间内一滴一滴的往下滴。
时间戳版本:
function throttle (handle,wait) {
let prev = Date.now();
return function() {
let current = Date.now();
if(current - prev >= wait) {
handle();
prev = Date.now();
}
}
}
定时器版本:
- 当触发事件的时候,我们设置一个定时器;
- 当再次触发的时候,如果定时器存在,就不执行,直到 wait 时间后,定时器执行 handle 函数,并且清空定时器,这样就可以设置下一个定时器;
- 当第一次触发事件时,不会立即执行行数,而是在 delay 秒之后才执行,而后在怎么频繁触发也都是在 wait 时间才执行一次。
function throttle1(handle, wait) {
let timer = null;
return function() {
if(!timer) {
timer = setTimeout(() => {
handle();
timer = null;
},wait);
}
}
}
定时器 + 时间戳版本:
- 节流中使用时间戳和定时器版本都是可以的,更精确的,可以使用时间戳和定时器相结合,当第一次事件触发时马上执行事件处理函数,最后一次触发时也还会执行一次事件处理函数。
- 在节流函数内部使用了 pre 和 current 与 wait 来计算剩余时间 remaining,当 remaining <= 0 时,表示执行该执行事件处理函数了(保证了第一次触发事件就能立即执行事件处理函数和每隔 wait 时间执行一次事件处理函数)。
- 如果还没到时间的话就设定在remaining时间后再触发 (保证了最后一次触发事件后还能再执行一次事件处理函数)。当然在 remaining 这段时间中如果又一次触发事件,那么会取消当前的计时器,并重新计算一个remaining来判断当前状态。
function throttle2 (handle, wait) {
let pre = Date.now();
let timer = null;
return function() {
let current = Date.now();
let remaining = wait - (current - pre);
clearTimeout(timer);
if(remaining <= 0) {
handle();
pre = Date.now();
} else {
timer = setTimeout(handle, remaining);
}
}
}
节流和防抖总结:
- 函数防抖:将几次操作合并为一次操作进行。原理是维护一个计时器,规定在delay时间后触发函数,但是在delay 时间内再次触发的话,就会取消之前的计时器而重新设置。这样一来,只有最后一次操作能被触发。
- 函数节流:使得一定时间内只触发一次函数。原理是通过判断是否到达一定时间来触发函数。
- 区别: 函数节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,而函数防抖只是在最后一次事件后才触发一次函数。比如在页面的无限加载场景下,我们需要用户在滚动页面时,每隔一段时间发一次 Ajax 请求,而不是在用户停下滚动页面操作时才去请求数据。这样的场景,就适合用节流技术来实现。
参考:
转载自:https://juejin.cn/post/7234057319285882936