不管是什么项目,总有一些基本的功能函数默默的躺在你的工具库中,为你遮挡bug,提升性能,一起来复习下!
当监听一些scroll,resize事件时,如果我们什么都不限制,srcoll事件在滚动过程中会一直触发,极大的影响性能,这个时候,就需要节流函数debounce。debounce函数返回一个函数,在给定的时间间隔下,连续被调用将不会触发。
/* 事件停止被触发N秒后才会再次触发回调
* @param {Function} func - 回调执行函数
* @param {String} wait - 触发间隔
* @param {Boolean} immediate - 是否延时执行
*/
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
// Usage
var myEfficientFn = debounce(function() {
// todo
}, 250);
window.addEventListener('resize', myEfficientFn);
很多时候,我们需要了解某个函数的执行状态,并根据状态执行相应的处理。在没有事件通知时,需要以一定的时间间隔轮询执行状态。
/* 轮询条件函数,根据状态执行相应回调
* @param {Function} fn- 条件函数
* @param {Function} callback - 成功回调
* @param {Function} errback - 失败回调
* @param {int} timeout - 超时间隔
* @param {int} interval - 轮询间隔
*/
function poll(fn, callback, errback, timeout, interval) {
var endTime = Number(new Date()) + (timeout || 2000);
interval = interval || 100;
(function p() {
// If the condition is met, we're done!
if(fn()) {
callback();
}
// If the condition isn't met but the timeout hasn't elapsed, go again
else if (Number(new Date()) < endTime) {
setTimeout(p, interval);
}
// Didn't match and too much time, reject!
else {
errback(new Error('timed out for ' + fn + ': ' + arguments));
}
})();
}
// Usage: ensure element is visible
poll(
function() {
return document.getElementById('lightbox').offsetWidth > 0;
},
function() {
// Done, success callback
},
function() {
// Error, failure callback
}
);
实用的执行一次函数,不用多解释。虽然很简单的函数,但是防止重复加载或者初始化的习惯必须养成。
function once(fn, context) {
var result;
return function() {
if(fn) {
result = fn.apply(context || this, arguments);
fn = null;
}
return result;
};
}
// Usage
var canOnlyFireOnce = once(function() {
console.log('Fired!');
});
从字符串变量中获取绝对url路径并没有那么容易,URL构造函数必须提供必要的参数,getAbsoluteUrl函数可以单纯的从字符串输入中得到绝对路径。
var getAbsoluteUrl = (function() {
var a;
return function(url) {
if(!a) a = document.createElement('a');
a.href = url;
return a.href;
};
})();
// Usage
getAbsoluteUrl('/shirlyzhang'); // "http://imweb.io/shirlyzhang"
当需要重写某个函数时,必须先确定其是否是原生函数。
(function() {
var toString = Object.prototype.toString;
var fnToString = Function.prototype.toString;
// 构造函数,数组
var reHostCtor = /^\[object .+?Constructor\]$/;
var reNative = RegExp('^' +
// 强制转换(Object-toString)为字符串
String(toString)
// 转移特殊字符
.replace(/[.*+?^${}()|[\]\/\\]/g, '\\$&')
// 替换 `toString` 为 `.*?`, 保持模板通用
// 替换 `for ...` 等字符来支持添加了额外信息的环境
.replace(/toString|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
);
function isNative(value) {
var type = typeof value;
return type == 'function'
// 使用 `Function#toString` 来绕过value原生的 `toString` 方法,避免误判
? reNative.test(fnToString.call(value))
// 回退到宿主对象检查。因为某些环境中,会将类型数组当做DOM方法。
: (value && type == 'object' && reHostCtor.test(toString.call(value))) || false;
}
module.exports = isNative;
}());
// Usage
isNative(alert); // true
isNative(myCustomFunction); // false
使用css选择器来选取节点修改样式不太高效,可以选择用js新建一段css样式规则插入。
var sheet = (function() {
var style = document.createElement('style');
// WebKit 兼容
style.appendChild(document.createTextNode(''));
document.head.appendChild(style);
return style.sheet;
})();
// Usage
sheet.insertRule("header { float: left; opacity: 0.8; }", 1);
判断页面内的元素是否具有某些属性值。
function matchesSelector(el, selector) {
var p = Element.prototype;
var f = p.matches || p.webkitMatchesSelector || p.mozMatchesSelector || p.msMatchesSelector || function(s) {
return [].indexOf.call(document.querySelectorAll(s), this) !== -1;
};
return f.call(el, selector);
}
// Usage
matchesSelector(document.getElementById('myDiv'), 'div.someSelector[some-attribute=true]')