面试题汇总

115 阅读2分钟

面试题汇总

实现 call、apply、bind

核心还是利用了 this 指向的规则:谁调用 this 指向谁

因此可以将函数变为需要绑定上下文的属性,执行完毕之后再删除其属性即可,避免污染。

call

let obj = {
  age: 18,
};
function log(...arg) {
  console.log(this.age, ...arg);
}

log.call(obj, "age", "name"); // 18 age name

实现

let obj = {
  age: 18,
};

Function.prototype.myCall = function (context, ...arg) {
  context.fn = this; // this 即为调用的函数
  let result = context.fn(...arg);
  delete context.fn;
  return result;
};

function log(...arg) {
  console.log(this.age, ...arg);
}

log.myCall(obj, "age", "name"); // 18 age name

apply

call 与 apply 仅仅是参数不一致而已,仅仅改动两行即可。apply 接收的已经是一个数组了。

log.myApply(obj, ["age", "name"]); // 18 age name

Function.prototype.myApply = function (context, arg){ }

bind

let obj = {
  age: 18,
};

function log(...arg) {
  console.log(this.age, ...arg);
}

let fn = log.bind(obj, "age");
// 返回的是一个函数
fn("name"); // 18 age name

实现

let obj = {
  age: 18,
};

Function.prototype.myBind = function (context, ...arg) {
  let fn = this;
  return function () {
    // fn.apply(context) 保证指向
    // [...arg, ...arguments] // 保证参数不丢失
    return fn.apply(context, [...arg, ...arguments]);
  };
};

function log(...arg) {
  console.log(this.age, ...arg);
}

let fn = log.myBind(obj, "age");
// 返回的是一个函数
fn("name"); // 18 age name

图片懒加载

效果预览

图片懒加载.gif

原理

  • 图片的 src 不设置图片的展示路径,或者设置一个比较小的默认图片

  • 图片的真实路径放置到其他属性中,比如 data-src

  • 通过 js 判断图片是否进入可视区域

  • 如果进去可视区域,将图片的 src 替换为真实的路径

  • <img
          lazyload="true"
          data-src="https://img.alicdn.com/imgextra/i2/3455042615/O1CN01rA5Jyq1VBiIgwMISE_!!3455042615-0-beehive-scenes.jpg_180x180xzq90.jpg_.webp"
        />
    

代码

// 可视区高度
let innerHeight = window.innerHeight;
function lazyLoad() {
  let imgList = document.querySelectorAll("img[data-src][lazyload]");
  imgList.forEach((img) => {
    let dataset = img.dataset || {};
    let { src } = dataset;
    // 不需要使用懒加载
    if (!src) return;

    // 获取图片的相对位置
    let top = img.getBoundingClientRect().top;
    // 如果处于视窗内
    if (top < innerHeight) {
      img.setAttribute("src", src);
      img.removeAttribute("data-src");
      //移除属性,下次不再遍历
      img.removeAttribute("lazyload");
    }
  });
}
document.addEventListener("scroll", lazyLoad);
// 先触发一次函数,初始化首页的页面图片
lazyLoad();

IntersectionObserver

function lazyLoad(entries) {
  entries.forEach((entry) => {
    // 元素是否可见
    if (entry.isIntersecting) {
      // 当前的元素
      let img = entry.target;
      let dataset = img.dataset || {};
      let { src } = dataset;
      // 不需要使用懒加载
      if (!src) return;
      img.setAttribute("src", src);
      // 取消观察 避免再次调用
      observer.unobserve(img);
    }
  });
}

// 获取元素 生成实例分别观察元素
let imgList = document.querySelectorAll("img[data-src][lazyload]");
let observer = new IntersectionObserver(lazyLoad);
imgList.forEach((img) => {
  observer.observe(img);
});

script(async、defer)

当浏览器加载 html 的时候,一旦遇到 script 标签,就会停下来先执行其标签中的内容。如果外部文件访问速度慢,或者执行时间比较久,那么整个页面的加载就会收到影响,会产生页面白屏问题。

DOMContentLoaded load 性能优化的两个事件

2151798436-59da4801c6772_fix732.png

那么 async defer 都可以解决此问题。他们都会异步加载,但是他们的执行时机又有些区别。

  • async
    • 不会按照加载的顺序来执行,谁先加载完就谁先执行
    • 执行的时候有可能 DOM 还未解析完毕,因此不适合操作 DOM
    • 如果你的脚本不关心 DOM 元素,可以使用它,比如百度统计
  • defer
    • 不会影响 DOM 的渲染,
    • 多个脚本的话,会按顺序执行
    • 在文档渲染完毕后,DOMContentLoaded 事件前调用