call函数
先从改变this指向上简单实现一个方法添加到Function的原型链上:
1 2 3 4 5
| Function.prototype.myCall = function (content) { content.fn = this const result = eval(`content.fn()`) return result }
|
这就实现了call函数核心部分,因为使用了字符串的形式,所以函数的参数部分还需要进行特殊处理:
1 2 3 4 5 6 7 8 9 10 11
| Function.prototype.myCall = function (content) { content.fn = this const args = [] for (let i = 1; i < arguments.length; i++) { args.push(`arguments[${i}]`) } const result = eval(`content.fn(${args})`) return result }
|
基本可以了,但还有问题,临时属性的处理,最终优化结果:
1 2 3 4 5 6 7 8 9 10 11
| Function.prototype.myCall = function (content) { const fn = `fn_${(Math.random()*999).toFixed()}` content[fn] = this const args = [] for (let i = 1; i < arguments.length; i++) { args.push(`arguments[${i}]`) } const result = eval(`content[fn](${args})`) delete content[fn] return result }
|
写个例子测试下:
1 2 3 4 5 6 7 8
| const a = { name: 'a', say: function (t) { console.log(`${t}, ${this.name}`) } } const b = { name: 'b' }
a.say.call(b, 'hi') a.say.myCall(b, 'hello')
|
节流
先说节流,以滚动事件为例,节流即是滚动过程中低频率地触发事件。
1 2 3
| window.onscroll = throttle(function () { console.log('throttle'); }, 1000)
|
定时器 throttle 实现:
1 2 3 4 5 6 7 8 9 10 11 12
| function throttle(fn, delay = 1000) { let timer = null; return function () { if (timer) { return } timer = setTimeout(() => { fn.apply(this, arguments); timer = null; }, delay) } }
|
时间戳 throttle 实现:
1 2 3 4 5 6 7 8 9 10
| function throttle(fn, delayTime = 1000) { let lastTime = 0 return function () { const nowTime = new Date().getTime() if (nowTime - lastTime > delayTime) { fn.call(this, ...arguments) lastTime = nowTime } } }
|
防抖
以滚动事件为例,防抖即是滚动过程中只触发一次事件,如果持续滚动,则一直不会触发事件。
1 2 3
| window.onscroll = debounce(function () { console.log('debounce'); }, 1000)
|
定时器实现:
1 2 3 4 5 6 7 8 9 10 11 12
| function debounce(fn, delay = 1000) { let timer = null return function () { if (timer) { clearTimeout(timer) } timer = setTimeout(() => { fn.call(this, ...arguments) timer = null }, delay); } }
|