1.bind,call,apply
//filter需要注意的点,你可以传第二个参数指定调用对象。
Array.prototype.myFilter = function (callback,thisArg){
let res = [];
for(let i=0;i<this.length;i++){
if(callback.apply(thisArg,[this[i],i,this]))
res.push(this[i]);
}
return res;
}
Function.prototype.myApply = function(obj,argArr = []){
if (obj === undefined || obj === null) {
return this();
}
obj._tempFunc = this; //添加一个临时对象,否则你就修改了obj的属性
const result = obj._tempFunc(...argArr);
delete obj._tempFunc;
return result;
}
Function.prototype.myCall = function(obj, ...argArr) {
if (obj === undefined || obj === null) {
return this();
}
obj._tempFunc = this; //添加一个临时对象,否则你就修改了obj的属性
const result = obj._tempFunc(...argArr);
delete obj._tempFunc;
return result;
}
Function.prototype.myBind = function(obj,...argsBefore) {
const tempFunction = this;
return function(...args){
//console.log("this指向的是谁",this); //这里的this指向的是window,而非函数本身,因此用闭包保存下函数本身
return tempFunction.apply(obj,[...argsBefore,...args]);//记得这里有return
}
}
2.展开数组
const arr = [1, [2, [3, [4, 5]]], 6];
//方法一:Infinity是层数
arr.flat(Infinity);
//方法二:递归
const myFlat = (arr)=>{
let ans = [];
for(let i=0;i<arr.length;i++){
if(typeof arr[i] === 'number'){
ans.push(arr[i]);
} else {
ans.push(...myFlat(arr[i]));
}
}
return ans;
}
//方法三:转字符串
let str = arr.toString();
console.log(str.split(',').map(number => {
return number-'0';
}))
3.数组去重
请重点看filter方法
const arr = [1, 1, '1', 17, true, true, false, false, 'true', 'a', {}, {}];
// => [1, '1', 17, true, false, 'true', 'a', {}, {}]
//1.使用set去重
const res1 = Array.from(new Set(arr));
//2.使用indexOf和include,原理相同
const unique2 = arr => {
const res = [];
for (let i = 0; i < arr.length; i++) {
if (res.indexOf(arr[i]) === -1) res.push(arr[i]);
}
return res;
}
const unique3 = arr => {
const res = [];
for (let i = 0; i < arr.length; i++) {
if (!res.includes(arr[i])) res.push(arr[i]);
}
return res;
}
//3.使用filter
const unique4 = arr => {
return arr.filter((item, index) => {
return arr.indexOf(item) === index;
});
}
4.将类数组转化为数组
4.1什么是类数组
像数组,但不是数组。他有length属性,但没有数组原型对象上的方法map,reduce,filter等等
4.2 如何获取一个类数组
//leishu没有map方法
let leishu = document.querySelectorAll('div');
leishu.map(item => {
console.log("item是什么",item);
})
function Fun(){
console.log(arguments)//arguments是类数组;
}
4.3如何将类数组转化为普通数组
Array.from(document.querySelectorAll('div'))
[...document.querySelectorAll('div')]
//concat和slice在类数组上不存在,因此通过call或者apply来调用
Array.prototype.slice.call(document.querySelectorAll('div'))
Array.prototype.concat.apply([], document.querySelectorAll('div'));
5.防抖和截流
5.1截流函数
使用场景:点击按钮,发送网络请求。
function throttle(originalFunction, time) {
let timer = null;
return function(...args){
if(!timer){
originalFunction.apply(this, args);
timer = setTimeout(() => {
timer = null;
}, time);
}
}
}
5.2防抖函数
debounce的本质:首次不触发,在规定的时间达到后才触发。
使用场景:输入框联想、窗口resize触发新手引导。
function debounce(originalFunction, time) {
let timer = null;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => {
originalFunction.apply(this, args);
timer = null;
}, time);
};
}
6.渲染10万条数据不卡顿
6.1原生js实现
通过requestAnimationFrame实现每次重绘的时候,只渲染你规定数量的li。createDocumentFragment不是一个真实的html元素,插入到dom树之后,它就消失了。
setTimeout(() => {
// 插入十万条数据
const total = 100000;
// 一次插入的数据
const once = 20;
// 插入数据需要的次数
const loopCount = Math.ceil(total / once);
let countOfRender = 0;
const ul = document.querySelector('ul');
// 添加数据的方法
function add() {
const fragment = document.createDocumentFragment();
for(let i = 0; i < once; i++) {
const li = document.createElement('li');
li.innerText = Math.floor(Math.random() * total);
fragment.appendChild(li);
}
ul.appendChild(fragment);
countOfRender += 1;
loop();
}
function loop() {
if(countOfRender < loopCount) {
window.requestAnimationFrame(add);
}
}
loop();
}, 0)
6.2实现虚拟列表
import React, { useState, useEffect } from 'react';
import './VirtualList.css';
const ITEM_HEIGHT = 30;
const VISIBLE_ITEMS = 20;
const VirtualList = ({ items }) => {
const [scrollTop, setScrollTop] = useState(0);
const handleScroll = (e) => {
setScrollTop(e.target.scrollTop);
};
const startIndex = Math.floor(scrollTop / ITEM_HEIGHT);
const endIndex = Math.min(startIndex + VISIBLE_ITEMS, items.length);
const visibleItems = items.slice(startIndex, endIndex).map((item, index) => (
<li key={startIndex + index} style={{ height: `${ITEM_HEIGHT}px` }}>
{item}
</li>
));
return (
<div className="virtual-list-container" onScroll={handleScroll}>
<div className="virtual-list-spacer" style={{ height: `${scrollTop}px` }} />
<ul>{visibleItems}</ul>
<div
className="virtual-list-spacer"
style={{ height: `${(items.length - endIndex) * ITEM_HEIGHT}px` }}
/>
</div>
);
};
export default VirtualList;
如何使用上面的虚拟列表
import React from 'react';
import VirtualList from './VirtualList';
const App = () => {
const items = Array.from({ length: 100000 }, (_, i) => `Item ${i + 1}`);
return (
<div>
<h1>Virtual List Example</h1>
<VirtualList items={items} />
</div>
);
};
export default App;
7. 实现模板字符串
const fn1 = (str, obj) => {
let res = '';
// 标志位,标志前面是否有{
let flag = false;
let start;
for (let i = 0; i < str.length; i++) {
if (str[i] === '{') {
flag = true;
start = i + 1;
continue;
}
if (!flag) res += str[i];
else {
if (str[i] === '}') {
flag = false;
res += match(str.slice(start, i), obj);
}
}
}
return res;
}
// 对象匹配操作
const match = (str, obj) => {
const keys = str.split('.').slice(1);
let res = obj;
for(let i = 0;i<keys.length;i++){
res = res[keys[i]];
}
return res;
}
8.promise的All和Race区别
在all方法中,会等到最慢的执行完,才会走到then。如果有一个错误,那就走catch(返回最快的resolve)。
const promise1 = new Promise((resolve) => {
setTimeout(() => resolve("One"), 1000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => reject("Two"), 2000);
});
Promise.all([promise1, promise2])
.then((values) => {
console.log(values);
})
.catch((res) => {
console.log("走到了catch", res);
});
在race方法中,会直接获取最快的promise的结果。如果最快的是resolve那就走then,如果最快的reject,那就走catch。
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => reject("One"), 1000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => reject("Two"), 2000);
});
Promise.race([promise1, promise2])
.then((values) => {
console.log(values);
})
.catch((res) => {
console.log("走到了catch", res);
});
手写race和all
重点:先把用法列出来,例如下面的用法,一眼看出promiseRace需要返回一个Promise。才会有then方法,所以第一行直接return new Promise。
promiseRace([myPro, myPro1])
.then((res) => {
console.log("所有的都成功了", res);
})
.catch((err) => {
console.log("又失败的", err);
});
function PromiseAll(promiseArr) {
return new Promise((resolve, reject) => {
let count = 0;
let result = [];
for (let i = 0; i < promiseArr.length; i++) {
Promise.resolve(promiseArr[i])
.then((res) => {
result[i] = res;
count++;
if (count === promiseArr.length) {
resolve(result);
}
})
.catch((err) => {
reject(err);
});
}
});
}
function promiseRace(promiseArr) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promiseArr.length; i++) {
Promise.resolve(promiseArr[i])
.then((res) => {
resolve(res);
})
.catch((err) => {
reject(err);
});
}
});
}
9.图片的预加载和懒加载
预加载
原理:new Image()对象,给其设置src,即开始发送网络请求。等到页面真正滚动到图片位置,图片已经加载完成了。
window.onload = function () {
const image = new Image();
image.src = "https://pic.cnblogs.com/avatar/1862411/20200610010655.png";
};
// 两种方法二选一即可
// document.addEventListener("DOMContentLoaded", function () {
// imageUrl = "https://example.com/path/to/your/large-image.jpg";
// preloadImage(imageUrl);
// });
const myButton = document.querySelector("button");
myButton.addEventListener("click", () => {
let img = document.querySelector("img");
img.src = "https://pic.cnblogs.com/avatar/1862411/20200610010655.png";
});
如何检测文档加载完成?
DOMContentLoaded:只有HTML加载、解析完成。样式表、图片还未加载完成window.onload事件:window.onload事件在页面上的所有资源加载完成。
懒加载
监听元素滚入视口。
document.documentElement.clientheight(window.innerHeight) > imgDOM.getBoundingClient().top
还可以使用新API。entry是一个数组,你observe了几个,数组里就有几个。
// 获取目标元素
const targetElement = document.getElementById("target-element");
const targetElement2 = document.getElementById("target-element2");
// 定义回调函数,当目标元素进入或离开视口时触发
function handleIntersection(entries, observer) {
console.log(" entries是什么", entries);
entries.forEach((entry) => {
if (entry.isIntersecting) {
console.log("Element is visible in the viewport");
} else {
console.log("Element is not visible in the viewport");
}
});
}
// 创建 Intersection Observer 实例
const observer = new IntersectionObserver(handleIntersection);
// 观察目标元素
observer.observe(targetElement);
observer.observe(targetElement2);
11.柯里化
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn(...args);
} else {
return function (...nextArgs) {
return curried(...args, ...nextArgs);
};
}
};
}
function sum(a, b, c) {
console.log(a + b + c);
}
const fn = curry(sum);
fn(1, 2, 3); // 6
fn(1, 2)(3); // 6
fn(1)(2, 3); // 6
fn(1)(2)(3); // 6