5个常见的前端手写功能:New、call apply bind、防抖和节流、instanceof、ajax

实现New

  • 首先创建一个新的空对象
  • 设置原型,将对象的原型设置为函数的prototype对象
  • 让函数的this指向这个对象,执行构造函数的代码
  • 判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象
function myNew(constructor, ...args) {// 如果不是一个函数,就报错if (typeof constructor !== "function") {throw "myNew function the first param must be a function";}// 基于原型链 创建一个新对象,继承构造函数constructor的原型对象上的属性let newObj = Object.create(constructor.prototype);// 将newObj作为this,执行 constructor ,传入参数let res = constructor.apply(newObj, args);// 判断函数的执行结果是否是对象,typeof null 也是'object'所以要排除nulllet isObject = typeof res === "newObject" && res !== null;// 判断函数的执行结果是否是函数let isFunction = typeof res === "function";// 如果函数的执行结果是一个对象或函数, 则返回执行的结果, 否则, 返回新创建的对象return isObject || isFunction ? res : newObj;
}// 用法
function Person(name, age) {this.name = name;this.age = age;// 如果构造函数内部,return 一个引用类型的对象,则整个构造函数失效,而是返回这个引用类型的对象
}
Person.prototype.say = function() {console.log(this.age);
};
let p1 = myNew(Person, "poety", 18);
console.log(p1.name);	//poety
console.log(p1);	//Person {name: 'poety', age: 18}
p1.say();	//18

测试结果:

image.png

call、apply、bind

实现call

思路:接受传入的context上下文,如果不传默认为window,将被调用的方法设置为上下文的属性,使用上下文对象来调用这个方法,删除新增属性,返回结果。

//写在函数的原型上
Function.prototype.myCall = function (context) {// 如果要调用的方法不是一个函数,则报错if (typeof this !== "function") {throw new Error("Type error");}// 判断 context 是否传入,如果没有传就设置为 windowcontext = context || window;// 获取参数,[...arguments]把类数组转为数组let args = [...arguments].slice(1);let result = null;// 将被调用的方法设置为context的属性,this即为要调用的方法context.fn = this;// 执行要被调用的方法result = context.fn(...args);// 删除手动增加的属性方法delete context.fn;// 将执行结果返回return result;
};//测试
function f(a, b) {console.log(a + b);console.log(this.name);
}
let obj = {name: 1,
};
f.myCall(obj, 1, 2); // 3,1

测试结果:

image.png

实现apply

思路:除了传参方式是数组,其它与call没区别

Function.prototype.myApply = function (context) {if (typeof this !== "function") {throw new Error("Type error");}let result = null;context = context || window;// 与上面代码相比,我们使用 Symbol 来保证属性唯一,也就是保证不会重写用户自己原来定义在 context 中的同名属性const fnSymbol = Symbol();context[fnSymbol] = this;// 执行要被调用的方法,处理参数和 call 有区别,判断是否传了参数数组if (arguments[1]) {//传了参数数组result = context[fnSymbol](...arguments[1]);} else {//没传参数数组result = context[fnSymbol]();}delete context[fnSymbol];return result;
};//测试
function f(a, b) {console.log(a, b);console.log(this.name);
}
let obj = {name: "张三",
};
f.myApply(obj, [1, 2]); //1 2,张三

测试结果:

image.png

实现bind

思路:bind返回的是一个函数,需要判断函数作为构造函数的情况,当作为构造函数时,this指向实例,不会被任何方式改变this,所以要忽略传入的context上下文。

bind可以分开传递参数,所以需要将参数拼接。如果绑定的是构造函数,还需要继承构造函数原型上的属性和方法,保证不丢失。

Function.prototype.myBind = function (context) {// 判断调用对象是否为函数if (typeof this !== "function") {throw new Error("Type error");}// 获取参数const args = [...arguments].slice(1);const fn = this; // 保存this的值,代表调用bind的函数//返回一个函数,此函数可以被作为构造函数调用,也可以作为普通函数调用const Fn = function () {// 根据调用方式,传入不同绑定值// 当作为构造函数时,this 指向实例,不会被任何方式改变 this,要忽略传入的context上下文return fn.apply(this instanceof Fn ? this : context,// bind可以分开传递参数(如f.bind(obj, 1)(2)),所以需要将参数拼接,这里使用apply,参数拼接成一个数组args.concat(...arguments)//当前的这个 arguments 是指 Fn 的参数,也可以用剩余参数的方式);};//对于构造函数,要保证原函数的原型对象上的属性不能丢失Fn.prototype = Object.create(fn.prototype);return Fn;
};// 1.先测试作为构造函数调用
function Person(name, age) {console.log(name);console.log(age);console.log(this); //构造函数this指向实例对象
}
// 构造函数原型的方法
Person.prototype.say = function () {console.log("say");
};
var obj = {name: "cc",age: 18,
};
var bindFun = Person.myBind(obj, "cxx");
var a = new bindFun(10);
// cxx
// 10
// Person {}
a.say(); // say// 2.再测试作为普通函数调用
function normalFun(name, age) {console.log(name);console.log(age);console.log(this); // 普通函数this指向绑定bind的第一个参数 也就是例子中的obj
}
var obj = {name: "aa",age: 18,
};
var bindNormalFun = normalFun.myBind(obj, "cc");
bindNormalFun(12);
// cc
// 12
// { name: 'aa', age: 18 }

测试结果:

image.png

防抖和节流

防抖

防抖是指在事件被触发n秒后在执行回调,如果在这n秒内时间又被触发,则重新计时。

可以使用在一些点击请求的事件上,避免因为用户的多次点击向后端发送多次请求。

//fn是需要防抖的函数,delay是等待时间
function debounce(fn, delay = 500) {let timer = null;// 这里返回的函数是每次用户实际调用的防抖函数return function(...args) {	//...args是es6的剩余参数语法,将多余的参数放入数组,用来代替arguments对象// 如果已经设定过定时器了就清空上一次的定时器if(timer) {clearTimeout(timer);	}// 开始一个新的定时器,延迟执行用户传入的方法;注:定时器的返回值是一个数值,作为定时器的编号,可以传入clearTimeout来取消定时器timer = setTimeout(() => {  //这里必须是箭头函数,不然this指向window,要让this就指向fn的调用者fn.apply(this, args);   }, delay)	}
}

节流

节流就是一定时间内执行一次事件,即使重复触发,也只有一次生效。

可以使用在监听滚动scroll事件上,通过事件节流来降低事件调用的频率。

定时器版本
throttle(fn, delay = 500) {let timer = null;return function(...args) {// 当前有任务了,直接返回if(timer) {return;}timer = setTimeout(() => {fn.apply(this, args);//执行完后,需重置定时器,不然timer一直有值,无法开启下一个定时器timer = null;	}, delay)}
}
时间戳版本
// 节流
function throttle(fn, delay = 500) {let prev = Date.now();// 上一次执行该函数的时间return function(...args) {let now = Date.now();//返回从UTC到当前时间的毫秒数// 如果差值大于等于设置的等待时间就执行函数if (now - prev >= delay) {fn.apply(this, args);prev = Date.now();}};
}

实现instanceof

instanceof用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上。

myInstanceof(instance, constructor) {//如果不是对象,或者是null,直接返回falseif (typeof instance !== "object" || instance === null) {return false;}// 获取对象的原型let proto = Object.getPrototypeOf(instance);// 获取构造函数的 prototype 对象let prototype = constructor.prototype;// 判断构造函数的 prototype对象是否在对象的原型链上while (true) {// 到达原型链终点null,说明没找到if (!proto) {return false;}if (proto === prototype) {return true;}// 如果没有找到,就继续从其原型上找proto = Object.getPrototypeOf(proto);}
}//测试
let Fn = function () { }
let p1 = new Fn()
console.log(myInstanceof(p1, Fn));//true
console.log(myInstanceof([], Fn));//false
console.log(myInstanceof([], Array)) // true
console.log(myInstanceof(function(){}, Function)) // true

测试结果:

image.png

实现Ajax

创建一个XMLHttpRequest对象
在这个对象上使用open方法创建一个HTTP请求(参数为请求方法、请求地址、是否异步和用户的认证信息)
通过send方法来向服务器发起请求(post请求可以入参作为发送的数据体)
监听请求成功后的状态变化:根据状态码进行相应的出来。onreadystatechange设置监听函数,当对象的readyState变为4的时候,代表服务器返回的数据接收完成,这个时候可以通过判断请求的状态,如果状态是200则为成功,404或500为失败。

function ajax(url) {//1.创建XMLHttpRequest对象const xhr = new XMLHttpRequest();//2.使用open方法创建一个GET请求xhr.open('GET',url);//xhr.open('GET',url,true);//true代表异步,已完成事务的通知可供事件监听器使用;如果为false,send() 方法直到收到答复前不会返回//3.发送请求xhr.send(); //4.监听请求成功后的状态变化(readyState改变时触发):根据状态码(0~5)进行相应的处理xhr.onreadystatechange = function () {//readyState为4代表服务器返回的数据接收完成if (xhr.readyState == 4) { //请求的状态为200或304代表成功if(xhr.status == 200 || xhr.status == 304) {//this.response代表返回的数据handle(this.response);} else {//this.statusText代表返回的文本信息console.error(this.statusText);}}};
}

使用Promise封装Ajax:

function ajax(url) {return new Promise((resolve, reject) => {let xhr = new XMLHttpRequest()xhr.open('get', url)xhr.send()  xhr.onreadystatechange = () => {if (xhr.readyState == 4) {if (xhr.status == 200 || xhr.status == 304) {resolve(this.response)} else {reject(new Error(this.statusText));}}}})
}
//使用
let url = '/data.json'
ajax(url).then(res => console.log(res)).catch(reason => console.log(reason))

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/312353.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

UE5 C++ 射线检测

一.声明四个变量 FVector StartLocation;FVector ForwardVector;FVector EndLocation;FHitResult HitResult;二.起点从摄像机,重点为摄像机前9999m。射线检测 使用LineTraceSingleByChannel 射线直线通道检测,所以 void AMyCharacter::Tick(float Delt…

9、statefulSet 有状态pod

1、statefulSet与replicationController、replicaSet的区别 1.1 有无状态 statefulSet是有状态的,类似于数据库、配置中心等,移除后需要创建相同的pod rc与rs是无状态的,可替换的 1.2 命名规则 1.3创建规则 statefulSet修改完ymal文件&…

GVRP协议与动态、静态vlan

一、GVRP协议使用场景 1、当实际组网复杂到网络管理员无法短时间内了解网络的拓扑结构,或者是整个网络的VLAN太多时,工作量会非常大,而且非常容易配置错误。在这种情况下,用户可以通过GVRP的VLAN自动注册功能完成VLAN的配置。 2、…

HarmonyOS开发实例:【分布式数据管理】

介绍 本示例展示了在eTS中分布式数据管理的使用,包括KVManager对象实例的创建和KVStore数据流转的使用。 通过设备管理接口[ohos.distributedDeviceManager],实现设备之间的kvStore对象的数据传输交互,该对象拥有以下能力 ; 1、注册和解除注…

Compose 简单组件

文章目录 Compose 简单组件TextText属性使用AnnotatedStringSpanStyleParagraphStyle SelectionContainer 和 DisableSelectionClickableText TextFieldTextField属性使用OutlinedTextFieldBasicTextFieldKeyboardOptions 键盘属性KeyboardActions IME动作 ButtonButton属性使用…

基于SpringBoot+Vue的校园商铺管理系统设计与实现(源码+文档+包运行)

一.系统概述 信息数据从传统到当代,是一直在变革当中,突如其来的互联网让传统的信息管理看到了革命性的曙光,因为传统信息管理从时效性,还是安全性,还是可操作性等各个方面来讲,遇到了互联网时代才发现能补…

中铁建智能安全帽与统一视频平台smarteye配奥维地图是否绝配?

中铁建智能安全帽与统一视频平台smarteye配奥维地图是否绝配? 在电厂、煤矿等厂矿企业内部,施工、抢修,临抢任务等场合下,都需要智能安全帽的实时图传和地图定位功能,相比较传统的高德/百度地图,奥维地图在…

黄仁勋最新访谈:GPU性能的革命性提升与AI未来

近期,英伟达CEO黄仁勋与美国CNBC知名主持人、股评人吉姆克莱默(Jim Cramer)在《Mad Money》节目中展开了一场关于技术未来和人工智能的对话。访谈里,黄仁勋不仅提到了英伟达在过去八年中将AI算力性能提高1000倍,还预言…

HarmonyOS实战开发-横竖屏切换

介绍 本实例展示如何使用媒体查询,通过ohos.mediaquery 接口完成在不同设备上显示不同的界面效果。 效果预览 使用说明 1.在竖屏设备上,首页展示新闻列表,点击新闻进入详情界面。 2.在横屏设备上,首页左侧展示新闻列表&#x…

Learn something about front end——颜色

​ 好装的标题啊哈哈哈哈哈哈 最近get了一个学习前端的网站叫FreeCodeCamp 原色:rgb三个值的其中一个值拉满,比如说rgb(255,0,0)是红色这样,三个主色: 红色 rgb(255, 0, 0) #FF0000绿色 rgb(0, 255, 0) #00FF00蓝色 rgb(0, 0, …

GAN:对抗生成网络【通俗易懂】

一、概述 对抗生成网络(GAN)是一种深度学习模型,由两个神经网络组成:生成器G和判别器D。这两个网络被训练来协同工作,以生成接近真实数据的新样本。 生成器的任务是接收一个随机噪声向量,并将其转换为与真…

鸿蒙语言TypeScript学习第18天:【泛型】

1、TypeScript 泛型 泛型(Generics)是一种编程语言特性,允许在定义函数、类、接口等时使用占位符来表示类型,而不是具体的类型。 泛型是一种在编写可重用、灵活且类型安全的代码时非常有用的功能。 使用泛型的主要目的是为了处…

SqlServer快速导出数据库结构的方法

1、查询出所有的表 SELECT name, id From sysobjects WHERE xtype u ORDER BY name ASC 2、根据表名查询出表结构 select syscolumns.name as "列名", systypes.name as "数据类型", syscolumns.length as "数据长度", sys.extended_prope…

prompt 工程整理(未完、持续更新)

工作期间会将阅读的论文、一些个人的理解整理到个人的文档中,久而久之就积累了不少“个人”能够看懂的脉络和提纲,于是近几日准备将这部分略显杂乱的内容重新进行梳理。论文部分以我个人的理解对其做了一些分类,并附上一些简短的理解&#xf…

【Golang】并发编程之三大问题:原子性、有序性、可见性

目录 一、前言二、概念理解2.1 有序性2.2 原子性后果1:其它线程会读到中间态结果:后果2:修改结果被覆盖 2.3 可见性1)store buffer(FIFO)引起的类似store-load乱序现象2)store buffer(非FIFO)引起的类似store-store乱序…

Netty学习——实战篇4 Netty开发Http服务实战、ByteBuf使用、开发群聊系统 备份

1 Netty开发Http服务实战 (1)Netty服务器监听8000端口,浏览器发出请求“http://localhost:8000” (2)服务器可以回复消息给客户端,“你好,我是服务器”,并对特定请求资源进行过滤。 HttpServer…

冯诺依曼与进程【Linux】

文章目录 冯诺依曼体系结构(从硬件的角度描述)冯诺依曼体系结构(从软件的角度描述)操作系统(软件)理解管理系统调用和库函数进程查看进程的两种方式 通过系统调用获取进程的PID和PPID通过系统调用创建进程-…

Linux之 USB驱动框架-USB总线核心和主控驱动(4)

一、USB设备描述符 一个USB设备描述符中可以有多个配置描述符,即USB设备可以有多种配置;一个配置描述符中可以有多个接口描述符,即USB设备可以支持多种功能(接口);一个接口描述符中可以有多个端点描述符。 …

SpringBoot项目创建及简单使用

目录 一.SpringBoot项目 1.1SpringBoot的介绍 1.2SpringBoot优点 二.SpringBoot项目的创建 三.注意点 一.SpringBoot项目 1.1SpringBoot的介绍 Spring是为了简化Java程序而开发的,那么SpringBoot则是为了简化Spring程序的。 Spring 框架: Spring…

linux 自定义命令/别名

参考资料 Linux(Ubuntu)自定义命令的使用Linux/Ubuntu系统自定义Shell命令Ubuntu/Linux 操作系统 自定义命令 目录 一. 为路径取别名二. 修改.profile文件2.1 .profile简介2.2 需求2.3 修改.profile文件 三. 创建软链接 一. 为路径取别名 ⏹需求:有一个work文件夹…