We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JS开发工具函数
渲染大数据时,合理使用createDocumentFragment 和requestAnimationFrame,将操作切分为一小段一小段执行。
createDocumentFragment
requestAnimationFrame
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)
// 实现一个 Event,示例如下: const e = new Event() e.on('click', x => console.log(x.id)) //=> 3 e.emit('click', { id: 3 }) //=> 4 e.emit('click', { id: 4 })
一个简单的订阅发布模式实现如下,主要有两个核心 API
class Event { events = {} emit (type, ...args) { const listeners = this.events[type] for (const listener of listeners) { listener(...args) } } on (type, listener) { this.events[type] = this.events[type] || [] this.events[type].push(listener) } }
原理就是监听页面滚动事件,分析clientHeight、scrollTop、scrollHeight三者的属性关系。
clientHeight
scrollTop
scrollHeight
window.addEventListener('scroll', function() { const clientHeight = document.documentElement.clientHeight; const scrollTop = document.documentElement.scrollTop; const scrollHeight = document.documentElement.scrollHeight; if (clientHeight + scrollTop >= scrollHeight) { // 检测到滚动至页面底部,进行后续操作 // ... } }, false);
可以给img标签统一自定义属性src='default.png',当检测到图片出现在窗口之后再补充src属性,此时才会进行图片资源加载。
src='default.png'
function lazyload() { const imgs = document.getElementsByTagName('img'); const len = imgs.length; // 视口的高度 const viewHeight = document.documentElement.clientHeight; // 滚动条高度 const scrollHeight = document.documentElement.scrollTop || document.body.scrollTop; for (let i = 0; i < len; i++) { const offsetHeight = imgs[i].offsetTop; if (offsetHeight < viewHeight + scrollHeight) { const src = imgs[i].dataset.src; imgs[i].src = src; } } } // 可以使用节流优化一下 window.addEventListener('scroll', lazyload);
使用原生XMLHttpRequest 对象
XMLHttpRequest
const getJSON = function(url) { return new Promise((resolve, reject) => { const xhr = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Mscrosoft.XMLHttp'); 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(); }) }
- pending 过渡态 - fulfilled 完成态 - rejected 失败态 class Promise{ constructor(excutorCallBack){ this.status = 'pending'; this.value = undefined; this.fulfillAry = []; this.rejectedAry = []; //=>执行Excutor let resolveFn = result => { if(this.status !== 'pending') return; let timer = setTimeout(() => { this.status = 'fulfilled'; this.value = result; this.fulfillAry.forEach(item => item(this.value)); }, 0); }; let rejectFn = reason => { if(this.status !== 'pending')return; let timer = setTimeout(() => { this.status = 'rejected'; this.value = reason; this.rejectedAry.forEach(item => item(this.value)) }) }; try{ excutorCallBack(resolveFn, rejectFn); } catch(err) { //=>有异常信息按照rejected状态处理 rejectFn(err); } } then(fulfilledCallBack, rejectedCallBack) { typeof fulfilledCallBack !== 'function' ? fulfilledCallBack = result => result:null; typeof rejectedCallBack !== 'function' ? rejectedCallBack = reason => { throw new Error(reason instanceof Error? reason.message:reason); } : null return new Promise((resolve, reject) => { this.fulfillAry.push(() => { try { let x = fulfilledCallBack(this.value); x instanceof Promise ? x.then(resolve, reject ):resolve(x); }catch(err){ reject(err) } }); this.rejectedAry.push(() => { try { let x = this.rejectedCallBack(this.value); x instanceof Promise ? x.then(resolve, reject):resolve(x); }catch(err){ reject(err) } }) }) ; } catch(rejectedCallBack) { return this.then(null, rejectedCallBack); } static all(promiseAry = []) { let index = 0, result = []; return new Promise((resolve, reject) => { for(let i = 0; i < promiseAry.length; i++){ promiseAry[i].then(val => { index++; result[i] = val; if( index === promiseAry.length){ resolve(result) } }, reject); } }) } } module.exports = Promise;
实现有并行限制的Promise调度器问题。
class Scheduler { constructor() { this.queue = []; this.maxCount = 2; this.runCounts = 0; } add(promiseCreator) { this.queue.push(promiseCreator); } taskStart() { for (let i = 0; i < this.maxCount; i++) { this.request(); } } request() { if (!this.queue || !this.queue.length || this.runCounts >= this.maxCount) { return; } this.runCounts++; this.queue.shift()().then(() => { this.runCounts--; this.request(); }); } } const timeout = time => new Promise(resolve => { setTimeout(resolve, time); }) const scheduler = new Scheduler(); const addTask = (time,order) => { scheduler.add(() => timeout(time).then(()=>console.log(order))) } addTask(1000, '1'); addTask(500, '2'); addTask(300, '3'); addTask(400, '4'); scheduler.taskStart() // 2 // 3 // 1 // 4
先进先出
/** * [Queue] * @param {[Int]} size [队列大小] */ function Queue(size) { var list = []; //向队列中添加数据 this.push = function(data) { if (data==null) { return false; } //如果传递了size参数就设置了队列的大小 if (size != null && !isNaN(size)) { if (list.length == size) { this.pop(); } } list.unshift(data); return true; } //从队列中取出数据 this.pop = function() { return list.pop(); } //返回队列的大小 this.size = function() { return list.length; } //返回队列的内容 this.quere = function() { return list; } } //初始化没有参数的队列 var queue = new Queue(); for (var i = 1; i <= 5; i++) { queue.push(i); } console.log(queue.quere()); console.log(queue.pop()); //从队列中取出一个 console.log(queue.quere()); var queue = new Queue(3); for (var i = 1; i <= 5; i++) { queue.push(i); } console.log(queue.quere()); console.log(queue.pop()); console.log(queue.quere());
指的是将一个接受多个参数的函数 变为 接受一个参数返回一个函数的固定形式,这样便于再次调用,例如f(1)(2)
经典面试题:实现add(1)(2)(3)(4)=10; 、 add(1)(1,2,3)(2)=9;
function add() { const _args = [...arguments]; function fn() { _args.push(...arguments); return fn; } fn.toString = function() { return _args.reduce((sum, cur) => sum + cur); } return fn; }
当持续触发事件时,保证一定时间段内只调用一次事件处理函数
const throttle = (fn, time) => { let flag = true; return function() { if (!flag) return; flag = false; setTimeout(() => { fn.apply(this, arguments); flag = true; }, time); } }
当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。(场景:模糊搜索请求)
const debounce = (fn, time) => { let timeout = null; return function() { clearTimeout(timeout) timeout = setTimeout(() => { fn.apply(this, arguments); }, time); } };
使用 clipboard-copy 这个库,复制内容到剪贴板
const copy = require('clipboard-copy') copy('hello, world')
/** * @param { number } arg1 * @param { number } arg2 */ export function add(arg1, arg2) { let r1, r2, m; try { r1 = arg1.toString().split(".")[1].length } catch (e) { r1 = 0 } try { r2 = arg2.toString().split(".")[1].length } catch (e) { r2 = 0 } m = Math.pow(10, Math.max(r1, r2)); return (arg1 * m + arg2 * m) / m }
function _new(constructor, ...arg) { var obj = {}; // 创建一个空对象 obj.__proto__ = constructor.prototype; // 空对象的`__proto__`指向构造函数的`prototype`, 为这个新对象添加属性 var res = constructor.apply(obj, arg); // 构造函数的作用域赋给新对象 return Object.prototype.toString.call(res) === '[object Object]' ? res : obj; // 返回新对象.如果没有显式return语句,则返回this } const Fun = function(name) { this.name = name; }; console.log(_new(Fun, '小明')); // Fun {name: "小明"}
/**
简单用法
function Fn () {} const fn = new Fn() fn instanceof Fn // true
实现如下:
// left instanceof right function _instanceof(left, right) { // 构造函数原型 const prototype = right.prototype // 实列对象属性,指向其构造函数原型 left = left.__proto__ // 查实原型链 while (true) { // 如果为null,说明原型链已经查找到最顶层了,真接返回false if (left === null) { return false } // 查找到原型 if (prototype === left){ return true } // 继续向上查找 left = left.__proto__ } } const str = "abc" _instanceof(str, String) // true
The text was updated successfully, but these errors were encountered:
No branches or pull requests
一大波JS工具函数
JS开发工具函数
渲染几万条数据不卡住页面
渲染大数据时,合理使用
createDocumentFragment
和requestAnimationFrame
,将操作切分为一小段一小段执行。使用 JS 实现一个发布订阅模式
滚动加载
原理就是监听页面滚动事件,分析
clientHeight
、scrollTop
、scrollHeight
三者的属性关系。图片懒加载
可以给img标签统一自定义属性
src='default.png'
,当检测到图片出现在窗口之后再补充src属性,此时才会进行图片资源加载。实现AJAX
使用原生
XMLHttpRequest
对象实现一个简陋但是功能较为完备的Promise
Promise并行限制
实现有并行限制的Promise调度器问题。
JS实现队列
先进先出
函数柯里化
经典面试题:实现add(1)(2)(3)(4)=10; 、 add(1)(1,2,3)(2)=9;
throttle(节流)
当持续触发事件时,保证一定时间段内只调用一次事件处理函数
debounce(防抖)
当持续触发事件时,一定时间段内没有再触发事件,事件处理函数才会执行一次,如果设定的时间到来之前,又一次触发了事件,就重新开始延时。(场景:模糊搜索请求)
如何复制内容到剪贴板?
使用 clipboard-copy 这个库,复制内容到剪贴板
加法函数(精度丢失问题)
实现一个New()
数据扁平化
/**
*/
export function flatten(value) {
const result = [];
value.forEach(v => {
result.push(v);
if (v.children) {
result.push(...flatten(v.children));
}
});
return result;
}
Javascript中instanceof方法原生实现
简单用法
实现如下:
The text was updated successfully, but these errors were encountered: