面试题汇总
实现 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
图片懒加载
效果预览
原理
-
图片的
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 性能优化的两个事件
那么 async defer 都可以解决此问题。他们都会异步加载,但是他们的执行时机又有些区别。
- async
- 不会按照加载的顺序来执行,谁先加载完就谁先执行
- 执行的时候有可能 DOM 还未解析完毕,因此不适合操作 DOM
- 如果你的脚本不关心 DOM 元素,可以使用它,比如百度统计
- defer
- 不会影响 DOM 的渲染,
- 多个脚本的话,会按顺序执行
- 在文档渲染完毕后,
DOMContentLoaded事件前调用