JavaScript进阶必会的手写功能

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代码执行效果
image.png
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代码执行效果
image.png
/**
 * 手写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代码执行效果
image.png

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());
代码执行结果参考图
image.png
原型链示意图
image.png

3. 手写instanceof

    typeof 可以判断基本数据类型 但是null 返回的也是object 不能识别 引用数据类型
    instanceof 可以准确的判断引用数据类型不可以判断 基本数据类型
    instanceof是用于检测构造函数的prototype是否出现某个实例对象的原型链上
参考代码
/**
* 手写instanceof
*/
let obj= { label:'gy' }
let arr= ['hello']

let result = obj instanceof Object
let result1 = arr instanceof Array
let result2 = arr instanceof Object
let 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 )
代码执行结果截图
image.png

根据上面的代码打印结果 需要注意的是:万物皆对象,包括数组,如果存在一个变量(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");
手写防抖代码执行结果
image.png
手写节流
/***
 * 手写节流
 * 这里只需要注意和防抖不同的时 不会清除定时器
 */
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 请求的状态码

image.png
  1. XMLHttpRequest.status 响应状态码 返回标准的HTTP 状态码
image.png
  1. 其他的请求响应参数

     XMLHttpRequest.response 这个是整个响应实体
     XMLHttpRequest.responseText 返回 `DOMString`
     XMLHttpRequest.timeout 超时时间
     XMLHttpRequest.upload 上传进度
    
  2. 在看看常用方法
    open()

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

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

    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()
            }
        }
    }
}

总结

    欢迎提出更好的思路以及其他必备手写功能

最后分享

更多文章可点击查看

WechatIMG6.jpeg

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,294评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,780评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,001评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,593评论 1 289
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,687评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,679评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,667评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,426评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,872评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,180评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,346评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,019评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,658评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,268评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,495评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,275评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,207评论 2 352

推荐阅读更多精彩内容

  • 1. JS 有哪些数据类型? 根据 JavaScript 中的变量类型传递方式,分为基本数据类型和引用数据类型两大...
    陈二狗想吃肉阅读 379评论 0 4
  • innerHTML和document.write()区别 dom元素属性 document方法,写入页面的内容流会...
    14d986ef7574阅读 402评论 0 0
  • 看了很多 JS 的书籍和教程,往往会模糊焦点,这篇文章会做一个梳理,将 JS 中的重难点进行总结。 文章分为上下两...
    你好星期四阅读 440评论 0 0
  • 日常学习知识点总结(JS篇) 1、闭包: 闭包就是函数中的函数,里面的函数可以访问外面函数的变量,外面的变量是内部...
    依稀_Sting阅读 2,065评论 0 1
  • 数组去重 (一维)数组去重最原始的方法就是使用双层循环,分别循环原始数组和新建数组;或者我们可以使用indexOf...
    陈观齐阅读 170评论 0 0