// all实现
function myPromiseAll(httpArr){
let result = []
let count = 0
return new Promise((resolve,reject)=>{
for(let i=0;i<httpArr.length;i++){
Promise.resolve(httpArr[i]).then((res)=>{
result[i]=res
count++
if(count===httpArr.length){
resolve(result)
}
}).catch((err)=>{
return reject(err)
})
}
})
}
// allSettled实现
function myPromiseAllSettled(httpArr){
let result = []
let count = 0
return new Promise((resolve,reject)=>{
for(let i=0;i<httpArr.length;i++){
Promise.resolve(httpArr[i]).then((res)=>{
result[i]=res
count++
if(count===httpArr.length){
resolve(result)
}
}).catch((err)=>{
count++
result[i]= {
status: 'rejected',
reason: err
}
if(count===httpArr.length){
resolve(result)
}
})
}
})
}
// race实现
function myPromiseRace(httpArr){
return new Promise((resolve,reject)=>{
for(let i=0;i<httpArr.length;i++){
Promise.resolve(httpArr[i]).then((res)=>{
return resolve(res)
}).catch((err)=>{
return reject(err)
})
}
})
}
// resolve实现
function myPromiseResolve(httpArr){
return new Promise((resolve) => {
resolve(httpArr)
})
}
// reject实现
function myPromiseReject(httpArr){
return new Promise((resolve,reject) => {
reject(httpArr)
})
}
// promise实现
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
function myPromise(executor) {
let self = this;
self.status = PENDING;
self.onFulfilled = [];//成功的回调
self.onRejected = []; //失败的回调
function resolve(value) {
if (self.status === PENDING) {
self.status = FULFILLED;
self.value = value;
self.onFulfilled.forEach(fn => fn());
}
}
function reject(reason) {
if (self.status === PENDING) {
self.status = REJECTED;
self.reason = reason;
self.onRejected.forEach(fn => fn());
}
}
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
function limitRequest(urls = [], limit = 3) {
return new Promise((resolve, reject) => {
const len = urls.length
let count = 0
// 同时启动limit个任务
while (limit > 0) {
start()
limit -= 1
}
function start() {
const url = urls.shift() // 从数组中拿取第一个任务
if (url) {
axios.post(url).then(res => {
// todo
}).catch(err => {
// todo
}).finally(() => {
if (count == len - 1) {
// 最后一个任务完成
resolve()
} else {
// 完成之后,启动下一个任务
count++
start()
}
})
}
}
})
}
// 非立即执行
function debounce(fun,time) {
let timeout; // 存放定时器
return function () {
clearTimeout(timeout);
timeout = setTimeout(() => {
fun()
}, time);
};
}
// 触发立即执行一次
function debounce(func,wait) {
let timeout;
return function () {
if (!timeout) func()
clearTimeout(timeout);
timeout = setTimeout(() => {
timeout = null;
}, wait)
}
}
// 非立即执行
function throttle(fun, time) {
let timeout=null;
return function() {
if (timeout===null) {
timeout = setTimeout(() => {
timeout = null;
fun()
}, time);
}
};
}
// 立即执行
function throttle(fun, time) {
let timeout=null;
let doNow = !timeout
return function() {
if (doNow) {
doNow = null
fun()
}
if (timeout===null) {
timeout = setTimeout(() => {
timeout = null;
fun()
}, time);
}
};
}
function mySetInterval(fn, millisec){
function my_interval(){
fn();
setTimeout(my_interval, millisec);
}
setTimeout(my_interval, millisec)
}
var newscript = document.createElement('script');
newscript.src = 'https://www.adb.com?callback=fn'
document.body.appendChild(newscript);
//要跨域的脚本
function fn(data) {
console.log(data);
}
JSON.parse('{"p": 5}', function (k, v) {
if(k === '') return v; // 如果到了最顶层,则直接返回属性值,
return v * 2; // 否则将属性值变为原来的 2 倍。
});
JSON.stringify({p: 5,a:4,rg:'fd'},['p','a'] || null || function (k, v) {
if(!k) return v
return v * 2+"test";
});
// 快排 小到大
function quickSort(arr,left,right) {
if(left>right) return
let l=left,r=right,baseValue=arr[l]
while (l<r) {
while (l<r && arr[r]>=baseValue) {
r--
}
while (l<r && arr[l]<=baseValue) {
l++
}
if(l<r){
let temp = arr[r]
arr[r] = arr[l]
arr[l] = temp
}
}
arr[left] = arr[l]
arr[l] = baseValue
quickSort(arr,l+1,right)
quickSort(arr,left,l-1)
return arr
}
function quickSort(arr) {
if (arr.length < 2) return arr
let baseIndex = Math.floor(arr.length / 2);
let base = arr.splice(baseIndex, 1)[0];
let left = [];
let right = [];
for (let i = 0; i < arr.length; i++){
if (arr[i] < base) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
return quickSort(left).concat([base], quickSort(right));
}
数字格式化(三位添加,)
str.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
首尾去空字符
str.replace(/^\s+|\s+$/g,'')
url参数格式化
str.match(/(?<=[?]).*/)[0].match(/.*(?=[&])/g)
str.match(/(?<=[?]).*/)[0].match(/(?<=[&]).*/g)
首字母大写
str.replace(/\w+/g,(str)=>str.slice(0,1).toUpperCase()+str.slice(1))
相邻字符去重
str.replace(/(\w{1})(\1)+/g, '$1');
多余字符省略 20
str.replace(/(?<=.{20}).*$/,'...')
function deepClone (obj){
if (obj===undefined || obj===null) return obj;
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
if (typeof obj !== 'object') return obj;
let cloneObj = new obj.constructor;
for (const key in obj) {
cloneObj[key] = deepClone(obj[key]);
}
return cloneObj;
}
arr.flat(Infinity);
const flatten = (arr) => {
let result = [];
arr.forEach((item, i, arr) => {
// 若为数组,递归调用 faltten,并将结果与result合并
if (Array.isArray(item)) {
result = result.concat(flatten(item));
} else {
result.push(arr[i])
}
})
return result;
};
let xhr = new XMLHttpRequest()
xhr.open('GET', url, false);
xhr.setRequestHeader('Accept', 'application/json');
xhr.onreadystatechange = function() {
if (xhr.readyState !== 4) return;
if (xhr.status === 200 || xhr.status === 304) {
resolve(xhr.responseText);
} else {
reject(new Error(xhr.responseText));
}
}
xhr.send();
function sum(...args1) {
// 求和
let x = args1.reduce((sum, next) =>sum+next)
return function(...args2) {
if (args2.length == 0) return x;
// 求和
let y = args2.reduce((sum, next) =>sum+next)
return sum(x+y)
}
}
// 假设img元素是body的直接子元素
function lazyload(){
var imgs = document.querySelectorAll('img');
var flag= 0;//避免每次都从第一个元素开始遍历
function lazyload(imgs){
var h = window.innerHeight; // 可视区域
var s = document.body.scrollTop; // 滚动条宽高
for(let i=flag;i<imgs.length;i++){
//滚动高度 > 相对父元素顶部的高度
if ((h+s)>imgs[i].offsetTop && imgs[i].getAttribute("src") == "") {
imgs[i].src = imgs[i].getAttribute("data-src");
flag=i+1;
}
}
}
window.addEventListener('scroll',throttle(lazyload(imgs),5*1000),{captrue:false})
}
<template>
<div ref="list" class="infinite-list-container" @scroll="scrollEvent($event)">
<div class="infinite-list-phantom" :style="{ height: listHeight + 'px' }">
</div>
<div class="infinite-list" :style="{ transform: getTransform }">
<div ref="items"
class="infinite-list-item"
v-for="item in visibleData"
:key="item.id"
:style="{ height: itemSize + 'px',lineHeight: itemSize + 'px' }"
></div>
</div>
</div>
</template>
<script>
export default {
name:'VirtualList',
props: {
//所有列表数据
listData:{
type:Array,
default:()=>[]
},
//每项高度
itemSize: {
type: Number,
default:200
}
},
computed:{
//列表总高度
listHeight(){
return this.listData.length * this.itemSize;
},
//可显示的列表项数
visibleCount(){
return Math.ceil(this.screenHeight / this.itemSize)
},
//偏移量对应的style
getTransform(){
return `translate3d(0,${this.startOffset}px,0)`;
},
//获取真实显示列表数据
visibleData(){
return this.listData.slice(this.start, Math.min(this.end,this.listData.length));
}
// 定义缓冲区
<!-- aboveCount(){
return Math.min(this.start,this.bufferScale * this.visibleCount)
},
belowCount(){
return Math.min(this.listData.length - this.end,this.bufferScale * this.visibleCount);
},
visibleData(){
let start = this.start - this.aboveCount;
let end = this.end + this.belowCount;
return this.listData.slice(start, end);
} -->
},
mounted() {
this.screenHeight = this.$el.clientHeight;
this.start = 0;
this.end = this.start + this.visibleCount;
},
data() {
return {
//可视区域高度
screenHeight:0,
//偏移量
startOffset:0,
//起始索引
start:0,
//结束索引
end:null,
};
},
methods: {
scrollEvent() {
//当前滚动位置
let scrollTop = this.$refs.list.scrollTop;
//此时的开始索引
this.start = Math.floor(scrollTop / this.itemSize);
//此时的结束索引
this.end = this.start + this.visibleCount;
// 实际应用场景结合懒加载,下拉到底部加载更多
if(this.end===this.listData.at(-1)) fetch().then((data)=>{
this.listData.push(data)
})
//此时的偏移量
this.startOffset =this.start*this.itemSize Math.floor(scrollTop)
}
}
};
</script>
<style scoped>
.infinite-list-container {
height: 100%;
overflow: auto;
position: relative;
-webkit-overflow-scrolling: touch;
}
.infinite-list-phantom {
position: absolute;
left: 0;
top: 0;
right: 0;
z-index: -1;
}
.infinite-list {
left: 0;
right: 0;
top: 0;
position: absolute;
text-align: center;
}
.infinite-list-item {
padding: 10px;
color: #555;
box-sizing: border-box;
border-bottom: 1px solid #999;
}
</style>
// 预设高度,先渲染再根据真实高度替换
<template>
<div ref="list" :style="{height}" class="infinite-list-container" @scroll="scrollEvent($event)">
<div ref="phantom" class="infinite-list-phantom"></div>
<div ref="content" class="infinite-list">
<div
class="infinite-list-item"
ref="items"
:id="item._index"
:key="item._index"
v-for="item in visibleData"
>
<slot ref="slot" :item="item.item"></slot>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
//所有列表数据
listData: {
type: Array,
default: () => []
},
//预估高度
estimatedItemSize: {
type: Number,
required: true
},
//容器高度 100px or 50vh
height: {
type: String,
default: "100%"
}
},
computed: {
_listData() {
return this.listData.map((item, index) => {
return {
_index: `_${index}`,
item
};
});
},
visibleCount() {
return Math.ceil(this.screenHeight / this.estimatedItemSize);
},
visibleData() {
return this._listData.slice(this.start, this.end);
}
},
created() {
this.initPositions();
},
mounted() {
this.screenHeight = this.$el.clientHeight;
this.start = 0;
this.end = this.start + this.visibleCount;
},
updated() {
this.$nextTick(function() {
if (!this.$refs.items || !this.$refs.items.length) {
return;
}
//获取真实元素大小,修改对应的尺寸缓存
this.updateItemsSize();
//更新列表总高度
let height = this.positions[this.positions.length - 1].bottom;
this.$refs.phantom.style.height = height + "px";
//更新真实偏移量
this.setStartOffset();
});
},
data() {
return {
//可视区域高度
screenHeight: 0,
//起始索引
start: 0,
//结束索引
end: 0
};
},
methods: {
initPositions() {
this.positions = this.listData.map((d, index) => ({
index,
height: this.estimatedItemSize,
top: index * this.estimatedItemSize,
bottom: (index + 1) * this.estimatedItemSize
}));
},
//获取列表起始索引
getStartIndex(scrollTop = 0) {
//二分法查找
return this.binarySearch(this.positions, scrollTop);
},
//二分法查找
binarySearch(list, value) {
let start = 0;
let end = list.length - 1;
let tempIndex = null;
while (start <= end) {
let midIndex = parseInt((start + end) / 2);
let midValue = list[midIndex].bottom;
if (midValue === value) {
return midIndex + 1;
} else if (midValue < value) {
start = midIndex + 1;
} else if (midValue > value) {
if (tempIndex === null || tempIndex > midIndex) {
tempIndex = midIndex;
}
end = end - 1;
}
}
return tempIndex;
},
//获取列表项的当前尺寸
updateItemsSize() {
let nodes = this.$refs.items;
nodes.forEach(node => {
let rect = node.getBoundingClientRect();
let height = rect.height;
let index = +node.id.slice(1);
let oldHeight = this.positions[index].height;
let dValue = oldHeight - height;
//存在差值
if (dValue) {
this.positions[index].bottom = this.positions[index].bottom - dValue;
this.positions[index].height = height;
for (let k = index + 1; k < this.positions.length; k++) {
this.positions[k].top = this.positions[k - 1].bottom;
this.positions[k].bottom = this.positions[k].bottom - dValue;
}
}
});
},
//获取当前的偏移量
setStartOffset() {
let startOffset =
this.start >= 1 ? this.positions[this.start - 1].bottom : 0;
this.$refs.content.style.transform = `translate3d(0,${startOffset}px,0)`;
},
//滚动事件
scrollEvent() {
//当前滚动位置
let scrollTop = this.$refs.list.scrollTop;
//此时的开始索引
this.start = this.getStartIndex(scrollTop);
//此时的结束索引
this.end = this.start + this.visibleCount;
//此时的偏移量
this.setStartOffset();
}
}
};
</script>
<style scoped>
.infinite-list-container {
overflow: auto;
position: relative;
-webkit-overflow-scrolling: touch;
}
.infinite-list-phantom {
position: absolute;
left: 0;
top: 0;
right: 0;
z-index: -1;
}
.infinite-list {
left: 0;
right: 0;
top: 0;
position: absolute;
}
.infinite-list-item {
padding: 5px;
color: #555;
box-sizing: border-box;
border-bottom: 1px solid #999;
/* height:200px; */
}
</style>
Storage.prototype.setStorageWithAge = (key, value, age) => {
if (isNaN(age) || age < 1) throw new Error("age must be a number");
const obj = {
data: value, //存储值
time: Date.now(), //存值时间戳
maxAge: age, //过期时间
};
localStorage.setItem(key, JSON.stringify(obj));
};
Storage.prototype.getStorageWithAge = key => {
const { data, time, maxAge } = JSON.parse(localStorage.getItem(key));
if (time + maxAge < Date.now()) {
localStorage.removeItem(key);
return undefined;
}
return data;
};
function objectPools() {
let objPool = [];
return {
create: function() {
if (objPool.length === 0) {
let newDiv = document.createElement("div");
document.body.appendChild(newDiv);
console.log('div created')
return newDiv;
} else {
return objPool.shift();
}
},
recover: function(obj) {
return objPool.push(obj);
}
}
}
let objPool = objectPools()
// 创建两个div
let arr = [];
for (let i = 0, str=['A', 'B'];i<str.length; i++) {
let toolTip = objPool.create();
toolTip.innerHTML = str;
arr.push(toolTip);
}
// 回收进对象池
arr.forEach(function(toolTip) {
objPool.recover(toolTip);
});
// 再创建6个div,先从对象池中取出对象,再创建
['A', 'B', 'C', 'D', 'E', 'F'].forEach((str)=> {
let toolTip = objPool.create();
toolTip.innerHTML = str;
});
2 div created
4 div created