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.mzph.cn/news/822802.shtml

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

相关文章

网站创建的流程是什么

网站的创建过程包括几个主要的步骤,其中涉及到一系列的决策和实践操作。下面我将详细介绍网站创建的流程,帮助读者了解如何创建一个成功的网站。 第一步:确定网站目标和功能 在创建网站之前,你需要明确自己网站的目标和功能。是用…

nodejs中使用node-addon-api框架:本地子线程调用JS代码

文章目录 JS代码中使用worker的方式main.jsapp.jsC本地代码基本框架环境变量napi_envreference,引用给传入C本地方法的JS函数增加引用在线程函数t_method2中获得引用函数对象并调用HandleScope::HandleScope错误scope还是行不通NODE-ADDON-API的方式

【剪映专业版】07素材导入与界面认识

视频课程:B站有知公开课【剪映电脑版教程】 点击开始创作后生成新的草稿 单击更改草稿名称 导入数量没有限制 根据选中素材创建文件夹:选中素材,单击右键 智能镜头分割:选中素材,单击右键,用于不同场景…

【Git】安装 Git

文章目录 1. CentOS 下安装2. Ubuntu 下安装 Git 是开放源代码的代码托管工具,最早是在 Linux 下开发的。开始也只能应用于 Linux 平台,后面慢慢的被移植到 Windows 下。现在,Git 可以在 Linux、Unix、Mac 和 Windows 这几大平台上正常运行了…

SA8000认证资讯--申请流程

SA8000认证流程 SA8000认证流程的第一步是申请组织参加管理体系在线自我评估。Social Fingerprint自我评估可以帮助该组织了解 SA8000管理体系要求并判断是否已做好认证申请准备。当该组织认为其管理实践足够成熟,具备认证条件时,可以从20 多家独立的SAA…

力扣2923、2924.找到冠军I、II---(简单题、中等题、Java、拓扑排序)

目录 一、找到冠军I 思路描述: 代码: 二、找到冠军II 思路描述: 代码: 一、找到冠军I 一场比赛中共有 n 支队伍,按从 0 到 n - 1 编号。 给你一个下标从 0 开始、大小为 n * n 的二维布尔矩阵 grid 。对于满足…

后端开发简介

后端开发是软件开发领域的一个重要组成部分,主要负责构建和维护应用程序的服务器端逻辑、数据处理、系统架构和基础设施。后端开发人员通常专注于处理那些用户看不到但对应用程序功能至关重要的部分,确保数据的安全存储、高效检索、业务逻辑的正确执行以…

基于springboot的甘肃非物质文化网站源码数据库

基于springboot的甘肃非物质文化网站源码数据库 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本甘肃非物质文化网站就是在这样的大环境下诞生&#xff0…

迁移学习入门

参考文献 [1]迁移学习与领域自适应:如何让模型更好地适应新的任务 [2]迁移学习与多模态数据处理的结合与应用 [3]Domain Adaptation(领域自适应,MMD,DANN) [4]迁移学习与多模态学习:实现跨模态知识传递 [5…

终于看到一个不在 Backbone上研究 ReNet的了!直接优化小目标检测性能,不卷ImageNet-1K数据集!

终于看到一个不在 Backbone上研究 ResNet的了!直接优化小目标检测性能,不卷ImageNet-1K数据集! 前言 传统的基于深度学习的目标检测网络在数据预处理阶段常通过调整图像大小以达到特征图中的统一尺寸和尺度。调整大小的目的是为了便于模型传播…

吴恩达llama课程笔记:第六课code llama编程

羊驼Llama是当前最流行的开源大模型,其卓越的性能和广泛的应用领域使其成为业界瞩目的焦点。Meta公司基于llama2发布了code llama,用于代码生成,补全等,Code Llama拥有7B、13B和34B三种版本。 吴恩达教授推出了全新的Llama课程&a…

数组扁平化的三种方式

方法一:ES6 的 flat 方法 flat() 方法将以指定的深度递归遍历数组,并将所有元素与遍历的子数组中的元素合并到一个新数组中以返回。 const arr [1,[2,[3,[4,5]]],6] // 方法一:数组自带的扁平化方法,flat的参数代表的是需要展开几层…

OceanBase 4.3 列存存储格式和列存索引存储格式

以 t1 表和索引为例子,下面两张图说明了存储层如何存储数据。 create table t1 (id1 int, id2 int, name varchar(10), salary int, primary key(id1, id2)) with column group (each column);create index idx (name) storing(salary) with column group(each co…

基于GIS、python机器学习技术的地质灾害风险评价与信息化建库应用

结合项目实践案例和科研论文成果进行讲解。入门篇,ArcGIS软件的快速入门与GIS数据源的获取与理解;方法篇,致灾因子提取方法、灾害危险性因子分析指标体系的建立方法和灾害危险性评价模型构建方法;拓展篇,GIS在灾害重建…

vue elmentui 可编辑table 实现

废话不多说上图: 1.可编辑input 2.可编辑下来框 3.点击chechbox 4.可编辑radio 其实后面两种可以直接显示值 需要修改直接改就行 保持风格统一所以就做了点击之后出现修改功能 上代码,不要哔哔 哈哈 粗暴 真得是曲不离口 拳不离手, 几天…

【力扣】45. 跳跃游戏 II

Problem: 45. 跳跃游戏 II 文章目录 问题思路复杂度Code 问题 思路 核心思路,例如nums[i]5,那么最远能跳五步; //那么在这接下来1-5范围内,哪个能让我跳的最远,这个最远指的是 -------------------------------------…

代码随想录图论

1. 所有可能的路径 class Solution:def allPathsSourceTarget(self, graph: List[List[int]]) -> List[List[int]]:def dfs(graph, result, path, root): #result 返回结果, path记录路径, root记录遍历到了第几个节点if root len(graph) - 1: #如果遍历到最后…

前端-嵌套对象转FormData(支持文件/对象无限层级递归)

/*** 嵌套对象转FormData* @param formData* @param data* @param parentKey*/ const appendFormData = (formData: any, data: any, parentKey = ) => {for (const key in data) {if (Object.prototype.hasOwnProperty.call(data, key)) {const value = data[key];const fu…

如何让多人同时扫码看图?图片转二维码的制作方法

现在经常会发现需要通过扫描二维码的方式来获取图片信息,这种方式可以让很多人能够同时获取图片信息,有利于提升传播的效率和用户体验。那么如何使用图片二维码生成器来在线制作二维码呢?其实步骤很简单,只需要在电脑3步就可以轻松…

Solana 上创建自己的 SLPToken:简明指南

Solana 定义 Solana 是由 Solana Labs 创建的区块链平台,旨在提供高吞吐量和低延迟的去中心化应用(DApps)开发环境。它采用一系列创新技术,如 PoH(Proof of History)共识机制和 Tower BFT(BFT …