当前位置: 代码迷 >> 综合 >> 前端基础——手写 (深拷贝、防抖、节流、call、apply、instanceof)
  详细解决方案

前端基础——手写 (深拷贝、防抖、节流、call、apply、instanceof)

热度:31   发布时间:2023-12-26 18:03:31.0

1、手写深拷贝

  1. 什么是浅拷贝?
    如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
  2. 什么是深拷贝?
    将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象

常用的深拷贝方法:

JSON.parse(JSON.stringify(obj))

缺点:不能拷贝 undefined、函数、正则会转成空对象、data 会调用toString()等。

手写深拷贝:

function clone (orgin) {
    const type = getType(orgin)let resultdebuggerswitch (type) {
    case 'object':result = {
    }Object.keys(orgin).forEach(key => {
    result[key] = ['object', 'array', 'date', 'regexp', 'function'].includes(getType(orgin[key])) ? clone(orgin[key]) : orgin[key]})return resultcase 'array':result = []orgin.forEach(item => {
    ['object', 'array', 'date', 'regexp', 'function'].includes(getType(item)) ? result.push(clone(item)) : result.push(item)})return resultcase 'date':return new Date(orgin.getTime())case 'regexp':return new RegExp(orgin)default:return orgin}}function getType(data) {
    const intermediary = Object.prototype.toStringconst res = intermediary.call(data)const type = res.substring(8, res.length -1)return type.toLocaleLowerCase()}

2、防抖、节流

2.1 节流

节流之时间戳版

let throttle = function(func, delay){
    let prev = Date.now();return function(){
    var now = Date.now();if(now-prev>=delay){
    func.apply(this, arguments);prev = Date.now();}}}

节流之定时器版

let throttle = function(func, delay) {
    let timer = null;return function() {
    if (!timer) {
    timer = setTimeout(function() {
    func.apply(this, arguments);timer = null;}, delay);}};};

定时器节流,由于定时器的特性,无法首次立即执行,但可以确保必定会执行最后一次
时间戳节流,可以首次立即执行,但无法确保会执行最后一次

2.2 防抖
function debounce(fn, wait) {
    let timeout = null;return function() {
    // 如果多次触发将上次记录延迟清除掉if (timeout !== null) clearTimeout(timeout);timeout = setTimeout(function() {
    fn.apply(this, arguments);timeout = null;}, wait);};}

3、手写call、apply

改变this指向,语法:

fun.call(thisArg, arg1, arg2, ...),调用一个函数, 其具有一个指定的this值和分别地提供的参数(参数的列表)。
func.apply(thisArg, [argsArray]),调用一个函数,以及作为一个数组(或类似数组对象)提供的参数。

手写call代码:

//通过对象调用方法来修改this指向
Function.prototype.call2 = function (context){
    const ctx = context || windowctx.func = this// 测试用例中这个this打印出来是:函数test [function test]const args = [...arguments].slice(1) // 保存剩余参数const res = arguments.length > 1 ? ctx.func(...args) : ctx.func()delete ctx.func // 避免造成全局污染return res
}let foo = {
    value: 1
}
function test(name) {
    console.log(name)console.log(this.value);
}
test.call2(foo, 'black') // black 1

手写apply代码:

Function.prototype.apply2 = function (context){
    const ctx = context || windowctx.func = this// 测试用例中这个this打印出来是:函数test [function test]const res = arguments[1] ? ctx.func(...arguments[1]) : ctx.func()delete ctx.func // 避免造成全局污染return res
}let foo = {
    value: 1
}
function test(name) {
    console.log(name)console.log(this.value);
}
test.call2(foo, 'black') // black 1

4、手写instanceof

instanceof 可以判断一个引用是否属于某构造函数;
还可以在继承关系中用来判断一个实例是否属于它的父类型

// left 必须是对象
// right 必须是函数
function myInstance(left, right){
        // 验证如果为基本数据类型,就直接返回falseconst baseType = ['string', 'number','boolean','undefined','symbol']if(baseType.includes(typeof(L))) return false// 取 right 的显示原型let RP  = right.prototype;//取 left 的隐式原型 left = left.__proto__;while(true){
    // 找到最顶层if(left === null){
        return false;}if(left === RP){
    return true;}// 没找到继续向上一层原型链查找left = left.__proto__;}
}

5、手写promise

转载掘金 IT老班长

笔试题链接

转载掘金 ErDong

以上仅做笔试使用,如有侵权请联系删除!

  相关解决方案