手写代码Two

作者: 斗伽 | 来源:发表于2021-04-22 14:39 被阅读0次

    深拷贝、 hash解决深拷贝,循环引用

    // 深拷贝,hash解决深拷贝,循环引用
    function deepCopy(obj, hash = new WeakMap()) {
        let cloneObj
        let Constructor = obj.constructor
        switch(Constructor){
            case RegExp:
                cloneObj = new Constructor(obj)
                break
            case Date:
                cloneObj = new Constructor(obj.getTime())
                break
            default:
                if(hash.has(obj)) return hash.get(obj)
                cloneObj = new Constructor()
                hash.set(obj, cloneObj)
        }
        for (let key in obj) {
            cloneObj[key] = isObj(obj[key]) ? deepCopy(obj[key], hash) : obj[key];
        }
        return cloneObj
    }
    
    // 获取节点位置
    const getPosition = el => {
      if (!el) return false;
      var rect = el.getBoundingClientRect();
      //获取元素距离文档顶部的距离
      var top =
        rect.top +
        (window.pageYOffset || document.documentElement.scrollTop) -
        (document.documentElement.clientTop || 0);
      var left =
        rect.left +
        (window.pageXOffset || document.documentElement.scrollLeft) -
        (document.documentElement.clientLeft || 0);
    
      return {
        top,
        left
      };
    };
    
    // 版本比较
    const compareVersion = function(version1, version2) {
      let verision1Arr = version1.split(".");
      let verision2Arr = version2.split(".");
      let maxLen =
        verision1Arr.length > verision2Arr.length
          ? verision1Arr.length
          : verision2Arr.length;
    
      for (let i = 0; i < maxLen; i++) {
        let p1 = verision1Arr[i] >> 0 || 0;
        let p2 = verision2Arr[i] >> 0 || 0;
        if (p1 > p2) {
          return 1;
        } else if (p1 < p2) {
          return -1;
        }
      }
      return 0;
    };
    
    
    
    
     
    

    new 操作符做了这些事:

    • 创建一个全新的对象,这个对象的 proto 要指向构造函数的原型对象
    • 执行构造函数
    • 返回值为 object 类型则作为 new 方法的返回值返回,否则返回上述全新对象
    function myNew(fn, ...args) {
      let instance = Object.create(fn.prototype);
      let res = fn.apply(instance, args);
      return typeof res === 'object' ? res: instance;
    }
    

    实现 call 方法

    调用 call 方法具体做了什么:

    • 将函数设为对象的属性
    • 执行后删除这个函数
    • 指定 this 到函数并传入给定参数执行函数
    • 如果不传入参数,默认指向为 window
    //实现一个call方法:
    Function.prototype.myCall = function(context) {
      //此处没有考虑context非object情况
      context.fn = this;
      let args = [];
      for (let i = 1, len = arguments.length; i < len; i++) {
        args.push(arguments[i]);
      }
      context.fn(...args);
      let result = context.fn(...args);
      delete context.fn;
      return result;
    };
    
    

    实现 apply 方法

    思路: 利用 this 的上下文特性。

    //实现apply只要把下一行中的...args换成args即可 
    Function.prototype.myCall = function(context = window, ...args) {
      let func = this;
      let fn = Symbol("fn");
      context[fn] = func;
    
      let res = context[fn](...args);//重点代码,利用this指向,相当于context.caller(...args)
    
      delete context[fn];
      return res;
    }
    
    

    实现 bind

    bind 的实现对比其他两个函数略微地复杂了一点,因为 bind 需要返回一个函数,需要判断一些边界问题,以下是 bind 的实现

    • bind 返回了一个函数,对于函数来说有两种方式调用,一种是直接调用,一种是通过 new 的方式,我们先来说直接调用的方式
    • 对于直接调用来说,这里选择了 apply 的方式实现,但是对于参数需要注意以下情况:因为 bind 可以实现类似这样的代码 f.bind(obj, 1)(2),所以我们需要将两边的参数拼接起来,于是就有了这样的实现 args.concat(...arguments)
    • 最后来说通过 new 的方式,在之前的章节中我们学习过如何判断 this,对于 new 的情况来说,不会被任何方式改变 this,所以对于这种情况我们需要忽略传入的 this
    Function.prototype.myBind = function (context) {
      if (typeof this !== 'function') {
        throw new TypeError('Error')
      }
      const _this = this
      const args = [...arguments].slice(1)
      // 返回一个函数
      return function F() {
        // 因为返回了一个函数,我们可以 new F(),所以需要判断
        if (this instanceof F) {
          return new _this(...args, ...arguments)
        }
        return _this.apply(context, args.concat(...arguments))
      }
    }
    

    懒加载和预加载

    // 懒加载
    // <img src="default.png" data-src="https://xxxx/real.png">
    function isVisible(el) {
      const position = el.getBoundingClientRect()
      const windowHeight = document.documentElement.clientHeight
      // 顶部边缘可见
      const topVisible = position.top > 0 && position.top < windowHeight;
      // 底部边缘可见
      const bottomVisible = position.bottom < windowHeight && position.bottom > 0;
      return topVisible || bottomVisible;
    }
    
    function imageLazyLoad() {
      const images = document.querySelectorAll('img')
      for (let img of images) {
        const realSrc = img.dataset.src
        if (!realSrc) continue
        if (isVisible(img)) {
          img.src = realSrc
          img.dataset.src = ''
        }
      }
    }
    
    // 测试
    window.addEventListener('load', imageLazyLoad)
    window.addEventListener('scroll', imageLazyLoad)
    // or
    window.addEventListener('scroll', throttle(imageLazyLoad, 1000))
    
    //预加载
    
    

    相关文章

      网友评论

        本文标题:手写代码Two

        本文链接:https://www.haomeiwen.com/subject/suzxrltx.html