1.图片懒加载
当图片出现在是视中时才加载图片,今天给分享两种方式
1. 用Element.getBoundingClientRect() 方法实现图片懒加载(注意在项目中使用节流函数)
(function () {
let box = document.querySelector('.box'),
boxImg = document.querySelector('.box2'),
HTML = document.documentElement;
console.log('boximg', boxImg)
const lazyImage = function lazyImage() {
if (boxImg.isLoad) return;
console.log("走了美欧")
let { bottom, top } = boxImg.getBoundingClientRect();
console.log(bottom, top, HTML.clientHeight)
// console.log('top',top)
console.log('bottom',bottom,HTML.clientHeight,top)
if (bottom <= HTML.clientHeight&&top>=0) {
boxImg.isLoad = true;
boxImg.src = boxImg.getAttribute('data-img');
boxImg.onload = () => {
// boxImg.style.opacity = 1
}
}
}
lazyImage();
window.addEventListener('scroll', lazyImage)
2.用IntersectionObserver方法可监听元素和可视窗口交叉的信息
(function () {
let box = document.querySelector('.box'),
boxImg = document.querySelector('img')
HTML = document.documentElement
const ob = new IntersectionObserver(changes => {
// changes[0]监听第一个元素和窗口的交叉信息
let { isIntersecting, target } = changes[0];
// isIntersecting 刚出现在视口
console.log(changes[0])
if (isIntersecting) {
boxImg.src = boxImg.getAttribute('data-img')
boxImg.style.opacity = 1;
ob.unobserve(target)
}
})
ob.observe(boxImg)
})()
2 . 手写个简单的promise
// 三个常量用于表示状态
const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'
function MyPromise(fn) {
const that = this;
this.state = PENDING;
// value 变量用于保存resolve 或者reject 中的值
this.value = null;
// 用于保存then 中的回调函数,因为当执行完promise时这时候应该把then 中的回调保存起来用于状态改变时使用
that.resolvedCallbacks = [];
that.rejectedCallbacks = [];
function resolve(value) {
if (that.state = PENDING) {
that.state = REJECTED
this.value = value
// 遍历回调函数数组并执行
that.resolvedCallbacks.map(cd => cd(that.value))
}
}
function rejecte(value) {
if (that.state === PENDING) {
that.state = RESOLVED
that.value = value
that.rejectedCallbacks.map(cb => cb(that.value))
}
}
// 完成两个函数后,我们应该实现吐过promise中传入函数
try {
fn(resolve, reject)
} catch (error) {
reject(error)
}
}
// 最后实现pormise.then
MyPromise.prototype.then = function (onFulfilled, onRejected) {
const that = this;
// 判断两个参数是否为函数类型,因为这两个参数是可选参数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
onRejected = typeof onRejected === 'function' ? onRejected : e => new TypeError(e) ;
// 当状态不是等待态时,就去执行相对应的函数。如果状态是等待态的话,就往回调函数中 push 函数
if (this.state === PENDING) {
this.resolvedCallbacks.push(onFulfilled)
this.rejectedCallbacks.push(onRejected)
}
if (this.state === RESOLVED) {
onFulfilled(that.value)
}
if (this.state === REJECTED) {
onRejected(that.value)
}
}
3 手写Promse.all()
1. 手写简单的promse.all
检测是不是promise实例
var isPromise = function isPromise(x) {
if (x !== null && /^(object|function)$/.test(typeof x)) {
// 上面证明是个对象或者是个函数
var then;
try {
then = x.then
} catch (error) {
return false
}
if (typeof then === 'function') return
}
return false;
}
peomise.all()的特点
- 接收一个数组
- 接受的数组里如果有的不是promise转为promise 结果还是那个结果
- 接收的数组里的promise 都成功才成功,有一个失败就全部失败
Promise.all = function all(promises) {
if (!Array.isArray(promises)) throw new TypeError('promises must be an array');
var n = 0,
values = [];
return new Promise(function (resolve, reject) {
for (var i = 0; i < promises.length; i++) {
(function (i) {
var promise = promises[i];
// 要是不是promise 转成个 成功的promise 值还是那个值
if (!isPromise(promise)) promise = Promise.resolve(promise);
promise.then(function onfulfiled(value) {
// 当前实例是成功的
n++;
values[i] = value;
// 判断如果成功都成功,
if (n >= promises.length) resolve(values)
}).catch(function onrejected(reason) {
// 一个失败全体失败
reject(reason)
})
})(i)
}
})
}
2手写带错误次数限制的promse.all();
/* 加失败限制 */
Promise.allLimit = function allLimit(promises, limit) {
console.log('hahhahha',promises,limit)
if (!Array.isArray(promises)) throw new TypeError('promises must be is Array');
limit =+ limit; // 不是数字转为数字
if (isNaN(limit)) limit = 1;
if (limit < 1 || limit > promises.length) throw new RangeError('limit invakid');
let n = 0,
m = 0,
values = [];
reasons = [];
return new Promise(function (resolve, reject) {
for (var i = 0; i < promises.length; i++) {
(function (i) {
var promise = promises[i]; // 得到单个的promise
// 判断是不是promse 要是不是promise 转成promse 值还是那个值
if (!isPromise(promise)) promise = Promise.resolve(promise);
promise.then(function onfulfiled(value) {
n++;
values[i] = value;
reasons[i] = null;
if (n >= promises.length) resolve(values)
}).catch(function onrejected(reason) {
// 一个失败集体失败
m++;
reasons[i] = reason;
console.log('hahahh',m,limit)
if (m === limit) {
reject(reasons)
} else {
n++
values[i] = null
if (n >= promises.length) resolve(values)
}
})
})(i)
}
})
}
Promise.allLimit([p1, p2, p3, p4,p5],2).then(values => {
console.log("执行结果", values);
}).catch(reason => {
console.log("失败的理由", reason);
})
4. 实现一个带有过期时间的localStoragea
const cacheDate = function cacheDate(query, limit, cacheName) {
if (typeof limit !== 'number') limit = 60 * 60;
if (typeof cacheName !== 'string') cacheName = 'cache';
return new Promise(resolve => {
let cache = localStorage.getItem(cacheName);
if (cache) { // 有缓存走这里
if (new Date() - caches.time <= limit) return
}
// 没有缓存或者缓存过期了 走这个
try {
query().then(value => {
localStorage.setItem(cacheName, JSON.stringify({
time: +new Date(),
data: value
}))
resolve(value)
})
} catch (err) {
throw TypeError('query must be an function an return promise')
}
})
}
/* 事例 写个json 文件用fetch 请求下 */
cacheDate(() => {
return fetch(`./data.json?_${+new Date()}`).then(response => {
let { status, statusText, } = response;
if (status >= 200 && status < 400) {
return response.json();
}
})
}, 60, 'hahah')
5. 手写实现le t
let 的特征
- 在块级作用域有效
- 不能重复生明
- 不能预处理,不存在变量提升,没声明代码之前不能调用
(function(){
var c =3
console.log(c) //1
})()
console.log(c) //c is not defined
6. 手写const
const 的特征
- 它包含let 的所有特征
- 不能修改(要注意,数组和对象属于引用数据类型,const保存的是指向对象的指针,所以修改其中的属性时指针不变,可以修改)
- 使用时必须初始化(必须赋值)
const _const = function _const(key, value) {
window[key] = value;
Object.defineProperty(window, key, {
enumerable: false,
configurable: false,
get() {
return value
},
set(newValue) {
if (newValue !== value) {
throw new TypeError('只读变量不能修改')
}
return value
}
})
}
7 . 实现一个鼠标滑到某元素上提示信息的效果
(function () {
const handle = function handle(ev) {
let target = ev.target,
// 获取到提示信息
tooltip = target.getAttribute("tooltip"),
tooltipBox = document.querySelector("#tooltipBox");
// 提示信息存在盒子不存在,创建盒子
if (tooltip) {
if (!tooltipBox) {
tooltipBox = document.createElement("div");
tooltipBox.id = "tooltipBox";
document.body.appendChild(tooltipBox);
}
let { clientX, clientY } = ev;
// 把创建的信息插入到盒子里面
tooltipBox.innerHTML = tooltip;
tooltipBox.style.cssText = `
display:block;
position:fixed;
top:${clientY + 10}px;
left:${clientX + 10}px;
padding:5px 10px;
border:1px solid red;
`;
return;
}
// 提示信息不存在时删除提示信息盒子
if (tooltipBox) tooltipBox.style.display = "none";
};
// document.querySelector('h2').addEventListener('mousemove',handle)
document.addEventListener("mousemove", handle);
})();
8 手写内置call
((proto) => {
function myCall(thisArg, ...args) {
thisArg = thisArg == undefined ? window : thisArg;
let type = typeof thisArg;
// thisArg 是原始类型的值无效所以要变成对象或者函数
if (!/^(object|function)$/.test(type)) {
if (/^(symbol|bigint)$/.test(type)) {
// symbol|bigint 用new 不能直接转对象所以这样
thisArg = Object(thisArg);
} else {
// 转对象
thisArg = new thisArg.constructor(thisArg);
}
}
let key = Symbol("key"); //新增的属性不能和thisArg 原来的属性相同
thisArg[key] = this; // 改变this指向
let result = thisArg[key](...args); // 执行函数
delete thisArg[key]; // 删除新增的属性
return result; // 返回结果
}
proto.myCall = myCall;
}
)(Function.prototype);
9 手写apply
(proto => {
function myApply(thisArg, args) {
thisArg = thisArg == undefined ? window : thisArg;
let type = typeof thisArg;
// thisArg 是原始类型的值无效所以要变成对象或者函数
// thisArg 如果是对象或者函数不用做处理
if (!/^(object|function)$/.test(type)) {
if (/^(symbol|bigint)$/.test(type)) {
// 因为new 运算符不能把symbol 和bigint 变成对象 用Object 可以
thisArg = Object(thisArg);
} else {
// 如果不是对象函数也不是symbol|bigint 那么就用new 运算符把他变成函数
thisArg = new thisArg.constructor(thisArg);
}
}
let key = Symbol('key'); // 新增的属性不能和thisArg 原来的属性冲突
thisArg[key] = this;
let result = thisArg[key](args);
delete thisArg[key];
return result;
}
proto.myApply = myApply;
})(Function.prototype)
10 手写bind(主要用了函数柯理化的思想)
(proto => {
function myBind(context, ...params) {
let _this = this;
return function Proxy(...args) {
params = params.concat(args)
_this.call(thisArg, params);
}
}
proto.myBind = myBind;
})(Function.prototype)
11. 手写防抖
let submit = document.querySelector('.submit');
const debounce = function debounce(func, wait, immediate) {
if (!typeof func == "function") throw new TypeError('func must is a function');
if (typeof wait == "boolean") immediate = wait;
if (typeof await != "number") await = 300;
let timer = null
/* 立即执行的判断 */
return function operation(...args) {
let now = !timer && immediate,
result;
timer = clearTimeout(timer);
timer = setTimeout(() => {
if (!immediate) func.call(this, ...args)
// 先执行时清楚最后一次的定时器
timer = clearTimeout(timer);
}, wait);
if (now) result = func.call(this, ...args)
return result
}
}
let handle = function handle() {
console.log('我是函数我执行了')
}
submit.onclick = debounce(handle, 1000, true)
12. 手写节流函数
/* 节流函数 */
function handle() {
console.log("aaa")
}
let throttle = function throttle(fn, wait) {
let timeout = null, // 计时器变量
result = null, // 上次执行的结果
previous = 0; //上次执行的时间
return function anymouse(...args) {
let now = new Date,
context = this;
let remaining = wait - (now - previous);
// 看下次执行的时间到没有时间到了清除上次的内容执行
// console.log("Shiji",remaining);
if (remaining <= 0) {
clearTimeout(timeout);
previous = now;
result = fn.apply(context, args);
// 首次执行
} else if (!timeout) {
timeout = setTimeout(() => {
previous = new Date;
result = fn.apply(context, args)
},wait)
}
// console.log(result)
return result
}
}
window.addEventListener('scroll', throttle(handle, 1000))