手撕JavaScript

328 阅读1分钟

1. 防抖

function debounce (fn, delay = 300) {
    let timer = null;
    return function (...arg)=> {
        if(timer) {
            cleanTimeout(timer);
            return;        
        }
        timer = setTimeout(()=>{
           fn.apply(this,arg)         
        }, delay)
    }
}

2. 节流

// 判断执行时间是否大于延迟时间再执行
function throttle(fn, delay = 300) {
    let oldTime = 0;
    return function(...args) {
        let nowTime = Date.now();
        if (nowTime-oldTime > delay) {
            oldTime = nowTime;
            fn.apply(this,args);        
        }          
    }
}

3. 深拷贝

function deepClone(obj, cache = new WeakMap()) {
    // 非object判断
    if (obj === null || typeof obj !== 'object') return obj
    // 其他object判断
    if (obj instanceof Date) return new Date(obj)
    if (obj instanceof RegExp) return new RegExp(obj)

    // 缓存判断是否有
    if (cache.get(obj)) return cache.get(obj)
    let cloneObj = Object.prototype.toString.call(obj) === '[object Array]' ? [] : {};
    cache.set(obj, cloneObj);
    
    // 递归克隆
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) continue;
        cloneObj[key] = deepClone(obj[key], cache);
    }
    
    return cloneObj;
}

4. 手写call

Function.prototype.myCall= function (obj) {
    // obj为空时,就执行当前函数
    let newObj = obj || window;
    newObj.fn = this;
    
    // 如果有参数
    let otherArg = [];
    for (let i = 1;i < arguments.length;i++) {
       // 拼接参数
       otherArg.push('arguments[' + i + ']');    
    }
                
    // 执行方法
   let result = eval('newObj.fn(' + otherArg + ')');
    
    // 不应该改变原对象
    delete newObj.fn;
    
    // 结果要返回
    return result;
}

5. 手写apply

与call的思路差不多,区别是第二参数传的是数组 因此参数调用的是arguments[1][i]

Function.prototype.myApply = function(obj) {
    let newObj = obj || window;
    newObj.fn = this;
    
    let otherArg = [];
    
    // 遍历的是arguments[1],存放的是数组,i要从0开始
    for (let i = 0;i<arguments[1].length;i++) {
        otherArg.push('arguments[1][' + i + ']');    
    }
    
    let result = eval('newObj.fn(' + otherArg + ')');
    
    // 别忘了删除fn
    delete newObj.fn;
    return result;
}

6. 手写bind

bind函数传值跟call一样,

  • bind返回的是一个函数
  • 也可以new一个bind返回的函数
Function.prototype.myBind = function(obj) {
    // 1. 防止this不是function
    if (typeof this !== 'function') {
        throw new TypeError('错误');    
    }
    
    // 2. 保存this;参数对象转数组;定义fun
    const that = this;
    const argArr = Array.prototype.slice.call(arguments, 1);
    const fun1 = function() {};
    const fun2 = function() {
        if (arguments.length) {
            for (let i = 0;i<arguments.length;i++) {
                argArr.push(arguments[i]);            
            }        
        }
        
        // 4. 判断是不是new了
        if (this instanceof fun1) {
            that.apply(this, argArr);                    
        } else {
            that.apply(that, argArr);        
        }
    }
    
    // 3. 把原型链串联好,让new 好的实例可以使用到that的原型对象属性
    // 使用原型式继承来避免直接修改原型对象
    fun1.prototype = that.prototype
    fun2.prototype = new fun1;
    
    return fun2;
}

7. 手写一个new

  • 创建一个空对象,对象__proto__属性指向new的函数的prototype
  • 执行构造函数中的代码,因为构造函数可能是通过this给新对象添加新的成员属性或方法。
  • 返回对象
function _new(Fun) {
  return function () {
    // 写法1
    var obj = {};
    // 修改指向,认爹
    obj.__proto__ = Fun.prototype;
    
    // 写法2
    // 建个对象,顺便修改指向,认爹
    var obj = Object.create(Fun.prototype); 
    
    // 写法3
    // 建个对象,
    var obj = {}; 
    Object.setPrototypeOf(obj, Fun.prototype); // 修改指向,认爹
         
    let result = Fun.apply(obj, arguments);
    
    var isObject = typeof result === "object" && result !== null;
    var isFunction = typeof result === "function";
    if (isObject || isFunction) {
      return result;
    }
    return obj;
  };
}

function Person(other) {
  this.age = 30;
  this.name = "sds";
  this.other = other;
}

var newEd = _new(Person)("other prams1111");
console.log(newEd);

8. instanceof实现原理

instanceof主要用于判断某个实例是否属于某个类型、是否其父辈类型的实例 因此只要右边变量的prototype在左边的prototype的原型链上即可

const myInstanceof = (judValue, rootValue) => {
    let judValueProto = judValue.__proto__;
    let rootValueProto = rootValue.prototype;
    
    while(true) {
        if (judValueProto === null) return false
        if (judValueProto === rootValueProto) return true
        judValueProto = judValueProto.__proto__;                                    
    }
}

9. 手写快排

①拿出一个中间数
②遍历判断值在左或右并存入数组
③继续递归左右数组并合并,记得合并中间数
④返回合并结果

const quickSort = arr => {
    if (arr.length <= 1) return arr;
    
    let center = arr.shift(),
        leftArr = [],
        rightArr = [],
        result = [];
        
    for (let i = 0;i <=arr.length; i++) {
        arr[i] <= center && leftArr.push(arr[i]);
        arr[i] > center && rightArr.push(arr[i]);   
    }
    
    result = quickSort(leftArr).concat(center, quickSort(rightArr));
    return result;        
}