防抖、节流、发布订阅、apply、call、bind
2020-04-02 Javascript
防抖(dobeunce) #
适用场景:
- 按钮提交场景:防止多次提交按钮,只执行最后提交的一次
- 实时搜索,只执行最后一次输入的。
单位时间内执行最后一次
const debounce = (fn,delay) =>{
let timer = null;
return (...args)=>{
clearTimeout(timer);
timer = setTimeout(()=>{
fn.apply(this,args);
},delay)
}
}
const debounce = (fn,delay) =>{
let timer = null;
return (...args)=>{
clearTimeout(timer);
timer = setTimeout(()=>{
fn.apply(this,args);
},delay)
}
}
节流 (throttle) #
使用场景:
- 拖拽:固定时间内只执行一次,防止高频次出发位置变动
- 缩放:监控浏览器的resize
- 动画: 避免短时间内多次出发动画引起性能问题
const throttle = (fn,delay=200) => {
let flag = true;
return (...args)=>{
if(!flag) return false;
flag = false;
setTimeout(()=>{
fn.apply(this,args);
flag = true;
},delay);
}
}
const throttle = (fn,delay=200) => {
let flag = true;
return (...args)=>{
if(!flag) return false;
flag = false;
setTimeout(()=>{
fn.apply(this,args);
flag = true;
},delay);
}
}
单位时间内只执行第一次
Event #
Event bus 是node中各个模块的基石,又是组件间通信的依赖手段之一,同时涉及到了订阅发布的设计模式
class EventEmeitter {
constructor(){
this.events = new Map();
this._maxListeners = this._maxListenners || 10;
}
emit(type,...args){
let handler;
handler = this.events.get(type);
if(Array.isArray(handler)){
for(let i = 0; i < handler.length; i++){
if(args.length>0){
handler[i].apply(this,args);
}else{
handler[i].apply(this);
}
}
}else{
if(args.length>0){
handler.apply(this,args);
}else{
handler.apply(this);
}
}
return true;
}
addEventListener(type,fn){
const handler = this.events.get(type); // 获取对应事件名称的函数
if(!handler){
this.events.set(type,fn);
}else if(handler && typeof handler === 'function'){
// 如果handler是函数说明当前只有一个监听函数
this.events.set(type,[handler,fn]);
}else{
if(handler.length <= this._maxListenners) return handler.push(fn); // 添加多个监听有上限
}
}
removeListener(type, fn){
const handler = this.events.get(type); // 获取对应事件名称的函数
// 如果是函数,说明只被监听了一次
if(handler && typeof handler === 'function'){
this.events.delete(type,fn);
}else{
// 如果handler是数组,说明被多次监听,需要找到对应的函数的位置
let position;
for(let i = 0 ; i< handler.length; i++){
if(handler[i] === fn){
position = i
}else{
position = -1;
}
}
// 如果找到匹配的函数
if(position !== -1){
// 直接删除
handler.splice(position,1);
// 如果清除后只有一个函数,那么取消数组,以函数形式保存
if(handler.length ===1){
this.events.set(type,handler[0]);
}
} else {
return this;
}
}
}
}
class EventEmeitter {
constructor(){
this.events = new Map();
this._maxListeners = this._maxListenners || 10;
}
emit(type,...args){
let handler;
handler = this.events.get(type);
if(Array.isArray(handler)){
for(let i = 0; i < handler.length; i++){
if(args.length>0){
handler[i].apply(this,args);
}else{
handler[i].apply(this);
}
}
}else{
if(args.length>0){
handler.apply(this,args);
}else{
handler.apply(this);
}
}
return true;
}
addEventListener(type,fn){
const handler = this.events.get(type); // 获取对应事件名称的函数
if(!handler){
this.events.set(type,fn);
}else if(handler && typeof handler === 'function'){
// 如果handler是函数说明当前只有一个监听函数
this.events.set(type,[handler,fn]);
}else{
if(handler.length <= this._maxListenners) return handler.push(fn); // 添加多个监听有上限
}
}
removeListener(type, fn){
const handler = this.events.get(type); // 获取对应事件名称的函数
// 如果是函数,说明只被监听了一次
if(handler && typeof handler === 'function'){
this.events.delete(type,fn);
}else{
// 如果handler是数组,说明被多次监听,需要找到对应的函数的位置
let position;
for(let i = 0 ; i< handler.length; i++){
if(handler[i] === fn){
position = i
}else{
position = -1;
}
}
// 如果找到匹配的函数
if(position !== -1){
// 直接删除
handler.splice(position,1);
// 如果清除后只有一个函数,那么取消数组,以函数形式保存
if(handler.length ===1){
this.events.set(type,handler[0]);
}
} else {
return this;
}
}
}
}
call简单实现 #
call的作用
- 将函数设为对象的属性
- 执行并且删除这个函数
- 指定this到函数并传入给定参数执行函数
- 如果不传入参数、默认指向window
- 如果传入一个原始类数据,这个只会被转换为它对应的对象形式
Function.prototype.myCall = function(ctx, ...args) {
// 兼容浏览器和node中的 this; 需要对传进来的上下文 进行处理
// 如果是 null or undefined 需要指定默认this;
ctx = ctx === null || ctx === undefined ? globalThis : Object(ctx)
// Symbol 创建的key是唯一的、不重复的key. 防止与对象的key重复
const key = Symbol('fn')
// 定义这个 给 ctx 新增一个全局唯一的属性 key. 并设置 不能被枚举
Object.defineProperty(ctx, key, {
enumerable: false,
value: this
})
// 把参数传入执行
const result = ctx[key](...args)
// 删除ctx上新创建的 属性
delete ctx[key]
return result
}
function myMethod(a, b) {
console.log(this, a, b)
return a + b
}
console.log(myMethod.myCall({name: 2}, 2, 3))
Function.prototype.myCall = function(ctx, ...args) {
// 兼容浏览器和node中的 this; 需要对传进来的上下文 进行处理
// 如果是 null or undefined 需要指定默认this;
ctx = ctx === null || ctx === undefined ? globalThis : Object(ctx)
// Symbol 创建的key是唯一的、不重复的key. 防止与对象的key重复
const key = Symbol('fn')
// 定义这个 给 ctx 新增一个全局唯一的属性 key. 并设置 不能被枚举
Object.defineProperty(ctx, key, {
enumerable: false,
value: this
})
// 把参数传入执行
const result = ctx[key](...args)
// 删除ctx上新创建的 属性
delete ctx[key]
return result
}
function myMethod(a, b) {
console.log(this, a, b)
return a + b
}
console.log(myMethod.myCall({name: 2}, 2, 3))
apply 简单实现 #
apply原理与call很相似
Function.prototype.myApply = function(context,arr){
var context = Object(context) || window;
context.fn = this;
var result ;
if(!arr){
result = context.fn()
}else{
var args = [];
for(var i=0,len=arr.length;i<len; i++){
args.push("arr["+i+"]");
}
result = eval("context.fn(" + args + ")");
}
delete context.fn;
return result;
}
Function.prototype.myApply = function(context,arr){
var context = Object(context) || window;
context.fn = this;
var result ;
if(!arr){
result = context.fn()
}else{
var args = [];
for(var i=0,len=arr.length;i<len; i++){
args.push("arr["+i+"]");
}
result = eval("context.fn(" + args + ")");
}
delete context.fn;
return result;
}
bind 简单实现 #
实现bind分为两步
- 返回一个函数,绑定this,传递预置参数
- bind返回的函数可以作为构造函数使用,作为构造函数时应使得this失效,但是传入的参数依然有效
// mdn的实现
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeof this !== 'function') {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function() {},
fBound = function() {
// this instanceof fBound === true时,说明返回的fBound被当做new的构造函数调用
return fToBind.apply(this instanceof fBound
? this
: oThis,
// 获取调用时(fBound)的传参.bind 返回的函数入参往往是这么传递的
aArgs.concat(Array.prototype.slice.call(arguments)));
};
// 维护原型关系
if (this.prototype) {
// Function.prototype doesn't have a prototype property
fNOP.prototype = this.prototype;
}
// 下行的代码使fBound.prototype是fNOP的实例,因此
// 返回的fBound若作为new的构造函数,new生成的新对象作为this传入fBound,新对象的__proto__就是fNOP的实例
fBound.prototype = new fNOP();
return fBound;
};
}
// mdn的实现
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeof this !== 'function') {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function() {},
fBound = function() {
// this instanceof fBound === true时,说明返回的fBound被当做new的构造函数调用
return fToBind.apply(this instanceof fBound
? this
: oThis,
// 获取调用时(fBound)的传参.bind 返回的函数入参往往是这么传递的
aArgs.concat(Array.prototype.slice.call(arguments)));
};
// 维护原型关系
if (this.prototype) {
// Function.prototype doesn't have a prototype property
fNOP.prototype = this.prototype;
}
// 下行的代码使fBound.prototype是fNOP的实例,因此
// 返回的fBound若作为new的构造函数,new生成的新对象作为this传入fBound,新对象的__proto__就是fNOP的实例
fBound.prototype = new fNOP();
return fBound;
};
}
版权属于: vincent
转载时须注明出处及本声明
Tags:# Javascript