G - I 系列
获取元素距离顶部的距离
const getVerticalOffset = el => {
let offset = el.offsetTop,
_el = el;
while (_el.offsetParent) {
_el = _el.offsetParent;
offset += _el.offsetTop;
}
return offset;
};
- offsetTop 是相对其父元素的距离,这里主要通过累加的方式来获取
- 对于嵌套较深的节点,可能其他 api (元素位置信息)来的更快些
根据给定函数对数组元素进行分组
const groupBy = (arr, fn) =>
arr
.map(typeof fn === 'function' ? fn : val => val[fn])
.reduce((acc, val, i) => {
acc[val] = (acc[val] || []).concat(arr[i]);
return acc;
}, {});
groupBy([6.1, 4.2, 6.3], Math.floor); // {4: [4.2], 6: [6.1, 6.3]}
groupBy(['one', 'two', 'three'], 'length'); // {3: ['one', 'two'], 5: ['three']}
- 第一次 map 的时候就将分组条件划分为一个数组
- reduce 时再以,条件做为key值,往其中添加值
检查一维数组中是否有重复的值
const hasDuplicates = arr => new Set(arr).size !== arr.length;
hasDuplicates([0, 1, 1, 2]); // true
hasDuplicates([0, 1, 2, 3]); // false
- 转化为 Set 对象,再通过size与原数组是否相等
- Set 后就去重了,size可理解为长度
检查是否为绝对url格式
const isAbsoluteURL = str => /^[a-z][a-z0-9+.-]*:/.test(str);
isAbsoluteURL('https://google.com'); // true
isAbsoluteURL('ftp://www.myserver.net'); // true
isAbsoluteURL('/foo/bar'); // false
类数组的检查
const isArrayLike = obj =>
obj != null && typeof obj[Symbol.iterator] === 'function';
isArrayLike([1, 2, 3]); // true
isArrayLike(document.querySelectorAll('.className')); // true
isArrayLike('abc'); // true
isArrayLike(null); // false
检查一个函数是否是异步函数
const isAsyncFunction = val =>
Object.prototype.toString.call(val) === '[object AsyncFunction]';
isAsyncFunction(function() {}); // false
isAsyncFunction(async function() {}); // true
// 类型为 AsyncFunction,类似的还要检查是否为 Promise
const isPromiseLike = obj =>
obj !== null &&
(typeof obj === 'object' || typeof obj === 'function') &&
typeof obj.then === 'function';
isPromiseLike({
then: function() {
return '';
}
}); // true
isPromiseLike(null); // false
isPromiseLike({}); // false
判断当前页面是否可见
const isBrowserTabFocused = () => !document.hidden;
isBrowserTabFocused(); // true
// 更多依赖 Page Visibility 这个API吧,权当了解下
检查值是否是有下铺的JSON
const isValidJSON = str => {
try {
JSON.parse(str);
return true;
} catch (e) {
return false;
}
};
// 之前同时事也问过此问题,当时确定没想到可以 try catch 的方式来判断
isValidJSON('{"name":"Adam","age":20}'); // true
isValidJSON('{"name":"Adam",age:"20"}'); // false
isValidJSON(null); // true
实现一个 findIndex
const linearSearch = (arr, item) => {
for (const i in arr) {
if (arr[i] === item) return +i;
}
return -1;
};
linearSearch([2, 9, 9], 9); // 1
linearSearch([2, 9, 9], 7); // -1
实现事件一次绑定
// 这个是运用添加事件参数实现的
const listenOnce = (el, evt, fn) =>
el.addEventListener(evt, fn, { once: true });
// 可以传入 option(对象) 也可以传入 useCapture(布尔)
listenOnce(
document.getElementById('my-id'),
'click',
() => console.log('Hello world')
);
// 通过闭包函数实现
const once = fn => {
let called = false;
return function(...args) {
if (called) return;
called = true;
return fn.apply(this, args);
};
};
const startApp = function(event) {
console.log(this, event); // document.body, MouseEvent
};
document.body.addEventListener('click', once(startApp));
// 现在多数框架也有对应实现,比如 VUE @click.once,起初并未运用,也没想过实现原理,现在知道了,好像也不难实现 哈哈
实现一个缓存(记忆)函数
const memoize = fn => {
const cache = new Map();
const cached = function (val) {
return cache.has(val)
? cache.get(val)
: cache.set(val, fn.call(this, val)) && cache.get(val);
// && 返回cache.get(val),如果是比较,&&的才返回布尔值
};
cached.cache = cache;
return cached;
};
// See the `anagrams` snippet.
const anagramsCached = memoize(anagrams);
anagramsCached('javascript'); // takes a long time
anagramsCached('javascript'); // returns virtually instantly since it's cached
console.log(anagramsCached.cache); // The cached anagrams map
将对象转换成查询字符串形式 ?key=value&xxx=aaa
// Object.entries 直接将对象转为 数组形式的 key-value
const objectToQueryString = queryParameters => {
return queryParameters
? Object.entries(queryParameters).reduce(
(queryString, [key, val], index) => {
const symbol = queryString.length === 0 ? '?' : '&';
queryString +=
typeof val === 'string' ? `${symbol}${key}=${val}` : '';
return queryString;
},
''
)
: '';
};
objectToQueryString({ page: '1', size: '2kg', key: undefined });
// '?page=1&size=2kg'