手写代码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

    深拷贝、 hash解决深拷贝,循环引用 new 操作符做了这些事: 创建一个全新的对象,这个对象的 proto 要...

  • 程序员面试时手写代码的意义?

    一.手写代码和上机测试 我觉得,手写要写出思路,上机要能运行就够了 写代码 二.手写代码的意义和作用 手写代码是一...

  • 手写代码

    手写事件侦听器,并要求兼容浏览器 手写事件模型 手写事件代理,并要求兼容浏览器 手写事件触发器,并要求兼容浏览器 ...

  • 手写代码

    css 水平、垂直居中 1、 已知元素宽高<1>absolute+负margin --- 必须要定宽高<2>ab...

  • 手写代码

    节流: 重复操作被忽略,直到完成 防抖: 重复操作重置定时器 实现new 实现call和apply 实现 Prom...

  • 手写代码

  • mybatis Generator最佳实践

    [Mybatis Generator最完整配置详解] 尽量使用代码生成器来生成mapper代码,能不手写绝不手写....

  • mybatis Generator最佳实践(数据库:mysql)

    Mybatis Generator最完整配置详解 尽量使用代码生成器来生成mapper代码,能不手写绝不手写. 官...

  • [贪心]Two Merged Sequences(CF-1144

    传送门 Two Merged Sequences AC代码

  • 搜狗面试

    原型链上手写事件,手写原生js实现ajax事件,jsonp实现原理,阻止事件IE冒泡代码,事件捕获、处理,冒泡代码...

网友评论

    本文标题:手写代码Two

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