写点什么

js 进阶手写常见函数

作者:hellocoder2029
  • 2022 年 10 月 03 日
    浙江
  • 本文字数:4104 字

    阅读完需:约 13 分钟

JavaScript 进阶的必要性

无论是学习react还是vue,它们都是js的应用框架。剥去他们的壳子看到的始终是js,所以作为一个前端大厨必须要熟练掌握好js这个大勺,才能烧出一顿好菜


无论是自我提升还是应付面试以下这些手写功能是每一个前端程序员必须掌握的

1. 手写 apply、call、bind

  每个Function对象都存在apply()、call()、bind() 方法,其作用都是可以在特定的作用域  中调用函数,等于设置函数体内this对象的值,以扩充函数赖以运行的作用域。
复制代码


apply、call、bind 的异同


  1. 三者都可以改变this的指向,第一个参数都是this,如果指向是null或者undefined则指向window  2.  apply的参数是数组,call是列表,而bind可以多次传入  3.  apply和call是改变this的指向之后直接运行函数,而bind则是返回绑定之后的函数
复制代码


apply 实现的参考代码


/** * 手写apply */window.name='gy' // 全局变量 let obj={  name:'ckx'}
var func=function(b,c){ console.log(`this=`, this) console.log(this.name,b,c) return 1}
func('24','hz') // gy 24 hz
func.apply(obj,['24','hz']) // ckx 24 hz
let newObj={ name:'xmx', age:24}Function.prototype.myApply=function(base,args){ // 1. 如果指向是null 或者undefined 则指向window base=base || window // 2. 根据this是谁调用就指向谁的原理,将this指向的函数 赋值给base对象的一个属性 base.fn=this // 3.执行函数,调用base.fn时,fn中的函数指向 base对象 let result=base.fn(...args) // 4. 删除base的fn属性 delete base.fn // 5. 返回result 结果 return result}
func.myApply(newObj,['55','yw']) // xmx 55 yw
复制代码


apply 代码执行效果


相关 js 视频讲解:进入学习

call 实现的参考代码


/** * 手写call */window.name='gy' // 全局变量 let obj={  name:'ckx'}
var func=function(b,c){ console.log(`this=`, this) console.log(this.name,b,c) return 1}
func('24','hz') // gy 24 hz
func.call(obj,'24','hz') // ckx 24 hz
let newObj={ name:'xmx', age:24}// call 和apply需要注意参数的格式即可Function.prototype.myCall=function(base,...args){ // 1. 如果指向是null 或者undefined 则指向window base=base || window // 2. 根据this是谁调用就指向谁的原理,将this指向的函数 赋值给base对象的一个属性 base.fn=this // 3.执行函数,调用base.fn时,fn中的函数指向 base对象 let result=base.fn(...args) // 4. 删除base的fn属性 delete base.fn // 5. 返回result 结果 return result}
func.myCall(newObj,'55','yw') // xmx 55 yw
复制代码


call 代码执行效果



bind 实现的参考代码


/** * 手写bind */window.name = "gy"; // 全局变量let obj = {  name: "ckx",};
var func = function (b, c,d) { console.log(`this=`, this); console.log(this.name, b, c,d); return 1;};
func("24", "hz",26); // gy 24 hz 26
let funcRes = func.bind(obj, "24", "hz");funcRes(24); // ckx 24 hz 24
let newObj = { name: "xmx", age: 24,};// 注意bind 返回的时绑定的函数以及可以多次传递参数Function.prototype.myBind = function (base, ...args1) { return (...args2) => { // 1. 如果指向是null 或者undefined 则指向window base = base || window; // 2. 根据this是谁调用就指向谁的原理,将this指向的函数 赋值给base对象的一个属性 base.fn = this; // 3.执行函数,调用base.fn时,fn中的函数指向 base对象 let result = base.fn(...args1,...args2); // 4. 删除base的fn属性 delete base.fn; // 5. 返回result 结果 return result; };};let myfuncRes=func.myBind(newObj, "55", "yw")myfuncRes(24) // xmx 55 yw 24
复制代码


bind 代码执行效果


2. 手写 new

new 关键字执行时做了哪些


1. 创建了一个新对象2. 将这个新对象与构造函数用原型链链接起来3. 将构造函数的this指向新的对象,执行构造函数的代码赋值4. 如果构造函数没有返回一个对象就返回新创建的对象否则返回构造函数返回的对象
复制代码


手写 new 参考代码


/*** * 手写new关键字执行 */
function Person(name,age) { this.name = name;}Person.prototype.getName = function () { return this.name;};let a = new Person('gy');console.log(a);console.log(a.getName());

const myNew = (Func, ...args) => { let newObj = {}; newObj.__proto__=Func.prototype let result=Func.apply(newObj,args) return typeof result == Object ? result: newObj};let b = myNew(Person,'gy1')console.log(b);console.log(b.getName());
复制代码


代码执行结果参考图



原型链示意图


3. 手写 instanceof

    typeof 可以判断基本数据类型 但是null 返回的也是object 不能识别 引用数据类型    instanceof 可以准确的判断引用数据类型不可以判断 基本数据类型    instanceof是用于检测构造函数的prototype是否出现某个实例对象的原型链上
复制代码


参考代码


/*** 手写instanceof*/let obj= { label:'gy' }let arr= ['hello']
let result = obj instanceof Objectlet result1 = arr instanceof Arraylet result2 = arr instanceof Objectlet result3 = obj instanceof Array
console.log('result=',result )console.log('result1=',result1 )console.log('result2=',result2 )console.log('result3=',result3 )
const myInstanceof = (left,right)=>{ if(typeof left != 'object' || left == null ) return false let proto= Object.getPrototypeOf(left) while(true){ if(proto==null) return false if(proto==right.prototype) return true proto=Object.getPrototypeOf(proto) }}
const myResult= myInstanceof(obj,Object)const myResult1= myInstanceof(arr,Array)const myResult2= myInstanceof(arr,Object)const myResult3= myInstanceof(obj,Array)
console.log('myRsult=',myResult )console.log('myResult1=',myResult1 )console.log('myResult2=',myResult2 )console.log('myResult3=',myResult3 )
复制代码


代码执行结果截图



根据上面的代码打印结果 需要注意的是:万物皆对象,包括数组,如果存在一个变量(arrOrObj)可能为array或者object 如果使用instanceof 去判断,一定需要先判断是否为数组先,否则 arrOrObj instanceof Object 一定为true 就无法区分 array和object

4. 手写防抖和节流

    持续的触发某一事件,延迟n秒后执行回调,在未到n秒再次触发,会从新出发倒计时    持续的触发某一时间,延迟n秒后执行回调,在未达到n秒再次出发,不会重新计时
复制代码


两者的使用场景


防抖可能用于无法预知的用户主动行为,如用户输入内容去服务端动态搜索结果。用户打字的速度等是无法预知的,具有非规律性。


节流可能用于一些非用户主动行为或者可预知的用户主动行为,如用户滑动商品橱窗时发送埋点请求、滑动固定的高度是已知的逻辑,具有规律性。


节流防抖也是闭包的应用


手写防抖代码参考


/*** * 手写防抖 */const debounce = (func, delay) => {  let timer = null;  return function (...args) {    if (timer) {      clearTimeout(timer);      timer = null;    }    timer = setTimeout(() => {      func(args);    }, delay);  };};const getfn = (data) => {  console.log(data);};debounce(getfn, 2000)("gy");
复制代码


手写防抖代码执行结果



手写节流


/*** * 手写节流 * 这里只需要注意和防抖不同的时 不会清除定时器 */const throttle = (func, delay) => {  let flag = false;  return function (...args) {    if (flag)  return    flag=true    setTimeout(() => {      func(args);      flag=false    }, delay);  };};const getfn = (data) => {  console.log(data);};throttle(getfn, 2000)("gy");
复制代码

5. 手动实现 ajax

    AJAX 的全称为 Asynchronous JavaScript + XML, 最重要的要属 XHR(XMLHttpRequest)    XMLHttpRequest通过不刷新页面请求特定URL,获取数据。
复制代码


实现的必备条件


  1. XMLHttpRequest() 是一个构造函数

  2. XMLHttpRequest.onreadystatechange 状态码变化时触发事件(所有浏览器支持)

  3. XMLHttpRequest.readyState 请求的状态码



  1. XMLHttpRequest.status 响应状态码 返回标准的 HTTP 状态码



  1. 其他的请求响应参数


   XMLHttpRequest.response 这个是整个响应实体   XMLHttpRequest.responseText 返回 `DOMString`   XMLHttpRequest.timeout 超时时间   XMLHttpRequest.upload 上传进度
复制代码


  1. 在看看常用方法 open()

  2. // method/url 是必须的 xhr.open(method, url, async, user, password); send()

  3. // body 可选默认为 null// 可以是 Blob, BufferSource (en-US), FormData,// URLSearchParams, 或者 USVString 对象.XMLHttpRequest.send(body); setRequestHeader()

  4. XMLHttpRequest.setRequestHeader(header, value);// 例如 XMLHttpRequest.setRequestHeader ("content-type", "application/x-www-form-urlencoded" );


基于以上 API 实现 ajax


    1.  构造一个请求 XMLHttpRequest    2.  初始化一个请求 open    3.  监听请求 onreadystatechange    4.  发送该请求 send
复制代码


/*** 手写一个ajax*/const myAjax =(url,methods,header,success,error)=>{    // 创建一个请求    let request=new XMLHttpRequest()    // 设置请求头    for (const key in header) {        request.setRequestHeader(key,header[key])    }    // 初始化请求    request.open(methods,url)    // 发送请求    request.send()    // 监听请求 onreadystatechange    request.onreadystatechange =function(){        if(request.readyState==4){            if(request.status==200){                success(request.response)            }else {                error()            }        }    }}
复制代码


用户头像

还未添加个人签名 2022.09.08 加入

还未添加个人简介

评论

发布
暂无评论
js进阶手写常见函数_Vue_hellocoder2029_InfoQ写作社区