JavaScript基础(手写代码)

本文详细介绍了如何手写实现JavaScript中的几个重要功能,包括Object.create用于创建新对象,instanceof检查对象的原型链,new操作符的模拟实现,防抖和节流函数用于优化性能,类型判断函数以确定数据类型,以及call、apply、bind函数的模拟,还有函数的柯里化和深拷贝对象的方法。这些内容对于深入理解JavaScript语言机制非常有帮助。
摘要由CSDN通过智能技术生成

1.手写 Object.create
/**
 * 手写 Object.create
 * @param {object} obj 传入对象
 * @author sunzhibin
 */
function create(obj){
    function F(){} // 定义空的构造函数
    F.prototype = obj // 将传入的obj加到构造函数的原型中
    return new F()
}
2.手写 instanceof 方法
/**
 * 手写 instanceof 方法
 * 通过原型链的方式判断类型
 * 1.首先获取类型的原型
 * 2.然后获得对象的原型
 * 然后一直循环判断对象的原型是否等于类型的原型,直到对象原型为 null,因为原型链最终为 null
 * @author sunzhibin
 */
function myInstanceof(left,right){
    let leftPro = left.__proto__,
        rightPro = right.prototype

    while(true){
        if(!leftPro) return false // 通过循环查到原型链的最底层,当返回的是null,停止循环,返回false
        if(leftPro === rightPro) return true // 查到,返回true
        leftPro = leftPro.__proto__ // 将leftPro赋值为下一层的原型,一级一级的匹配,直到最底层原型为null时,停止循环
    }
}
3. 手写 new 操作符
/**
 * 手写 new 操作符
 * 在调用 new 的过程中会发生以上四件事情:
 * 1.首先创建了一个新的空对象
 * 2.设置原型,将对象的原型设置为函数的 prototype 对象
 * 3.让函数的 this 指向这个对象,执行构造函数的代码(为这个新对象添加属性)
 * 4.判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象
 * @param {function} fn
 * @author sunzhibin
 */
function _new(fn,...args){
    // 判断参数是否是一个函数
    if (typeof fn !== "function") {
        console.error("type error not function");
        return;
    }
    const o = {} // 1. 在内存中创建一个新空对象
    o.__proto__ = fn.prototype // 2. 这个新对象内部的 [[Prototype]] 指针被赋值为构造函数(fn)的 prototype 属性
    const r = fn.apply(o,args) // 3.构造函数内部的 this 被赋值为这个新对象(即 this 指向新对象)
    const isTrue = r && typeof r === 'function' || typeof r === 'object' // 4.判断r是否为function或者object

    return isTrue ? r : o // 4. 如果构造函数返回非空对象,则返回该对象;否则,返回刚才创建的新对象 
}
4.手写防抖函数
/**
 * 手写防抖函数
 * 函数防抖是指在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。这可以使用在一些点击请求的事件上,避免因为用户的多次点击向后端发送多次请求
 * 例:搜索框
 * @param {function} fn 
 * @param {number} wait 延迟时间-默认800毫秒
 * @author sunzhibin
 * @example
 */
function debounce(fn,wait=800){
    let timer = null
    return function(...args){
        const that = this
        if(timer) clearTimeout(timer) // 清除定时器
        
        timer = setTimeout(()=>{
            fn.apply(that,args) // 修改函数this指向,并传递参数,执行传进来的方法
            timer = null // 将定时器置空,放置内存泄漏
        },wait)
    }
}
5.手写节流函数
/**
 * 手写节流函数
 * 函数节流是指规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。
 * 例:节流可以使用在 scroll 函数的事件监听上,通过事件节流来降低事件调用的频率。
 * @param {function} fn
 * @param {number} delay 延迟时间-默认800毫秒
 * @author sunzhibin
 */
function throttle(fn,delay=800){
    let cueTime = Date.now();
    return function(...args){
        const that = this
        let nowTime = Date.now();
        if(nowTime - curTime >= delay){
            fn.apply(that,args)
            cueTime = nowTime
        }
    }
}
6.手写类型判断函数
/**
 * 手写类型判断函数
 * 判断类型的4种方式:
 * 1.typeof:基本数据类型中:null 。引用数据类型中的:Array,Object,Date,RegExp。不可以用typeof检测。都会返回小写的object   例:typeof 100 //"number"
 * 2.instanceof:基本数据类型中:Number,String,Boolean。字面量值不可以用instanceof检测   例:[1,2,3] instanceof Array //true
 * 3.constructor:除了undefined和null报错之外,其他类型都可以通过constructor属性来判断类型   例:num.constructor==Number // true
 * 4.Object.prototype.toString.call():例:Object.prototype.toString.call(123) //"[object Number]"
 * 当前函数是封装类型判断
 * @param {any} value 
 * @author sunzhibin
 */
function getType1(value){
    // 判断数据是 null 的情况
    if(value == null) return value+""

    // 判断数据是引用类型的情况
    if(typeof value === 'object'){
        return Object.prototype.toString.call(value).replace(/^\[object (\S+)\]$/, '$1')
    }else{
        // 判断数据是基本数据类型的情况和函数的情况
        return typeof value;
    }
}

function getType2(value){
    let r = Object.prototype.toString.call(value)
    return r.replice(/^\[object (\S+)\]$/,'$1')
}
7.手写 call 函数
/**
 * 手写 call 函数
 * 1.判断传入上下文对象是否存在,如果不存在,[则设置为 window] 。globalThis(因为环境不一样,可能是window,可能是node),所以不用window用globalThis
 * 2.生成唯一key,防止添加对象时,存在相同名称导致覆盖
 * 4.将this放入,这里的this指向调用者。例:obj.myCall,this指向的是obj
 * 5.使用上下文对象来调用这个方法,并保存返回结果。
 * 6.删除刚才新增的属性。
 * 7.返回结果。
 * @param {context} context -this指向
 * @param {any} args -剩余参数
 */

Function.prototype.myCall = function(ctx,...args){
    ctx = Object(ctx) || globalThis // context = Object(context) || window (这里不用window是因为可能环境不同)
    const key = Symbol() // 生成唯一key
    ctx[key] = this
    const result = ctx[key](...args)
    delete ctx[key]
    return result
}
8.手写 apply 函数
/**
 * 手写 apply 函数
 */
Function.prototype.myApply = function(ctx,args){
    ctx = Object(ctx) || globalThis
    const key = Symbol()
    ctx[key] = this
    const result = ctx[key](...args)
    delete ctx[key]
    return result
}
9.手写 bind 函数
/**
 * 手写 bind 函数
 */
Function.prototype.bind = function(ctx,...args1) {
    var fn = this;
    return function(...args2) {
        fn.apply(ctx, ...args1, ...args2);
    };
};
10.函数柯里化的实现
11.手写实现深拷贝函数
/**
 * 手写实现深拷贝函数
 * @param {array | object} obj  -针对数组或对象的深拷贝
 * @author sunzhibin
 */
function deepClone(obj){
    // null, Array,Object,Date,RegExp 都会返回小写的object
    if(!obj || typeof obj !=='object') return
    let newObj = Array.isArray(obj) ? [] : {} // 判断类型是数组还是对象

    for(key in obj){
        if(obj.hasOwnProperty(key)){ // 判断是否存在key值
            // 这里的判断,如果说obj[key]里面还是对象或数组,继续调用自身,一层层展开
            newObj[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key]
        }
    }
    return newObj
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一个......

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值