前端开发规范系列文章之Javascript Tips and Tricks,本意是写成常用代码收集、常用技巧整理的文章,感觉“常用代码大全”太土、“实用代码整理”有失偏颇,“提示与技巧”不够稳重,所以使用常用的英语说法,相信广大程序员都懂得。
##妙味
Javascript美妙之处,需要我们静静体会,慢慢吸收,然后让代码在您指下曼舞。整理的这些代码我们称之为妙味,请大家细品。
博主会不断更新本文,方便大家阅读起见,我们采用倒序更新的方式,把最新更新的放最上方。
事件处理
我们知道IE和标准浏览器在事件处理方面有很大不同,所以我们在使用的时候需要首先进行统一化处理,统一处理时有两种方式,shim和polyfill。
shim将新的api引入新的环境中,例如下面的代码使用addEvent和removeEvent来添加删除事件。
1 | // Shim for DOM Events for IE7- // http://www.quirksmode.org/blog/archives/2005/10/_and_the_winner_1.html // Use addEvent(object, event, handler) instead of object.addEventListener(event, handler) window.addEvent = function(obj, type, fn) { if (obj.addEventListener) { obj.addEventListener(type, fn, false); } else if (obj.attachEvent) { obj["e" + type + fn] = fn; obj[type + fn] = function() { var e = window.event; e.currentTarget = obj; e.preventDefault = function() { e.returnValue = false; }; e.stopPropagation = function() { e.cancelBubble = true; }; e.target = e.srcElement; e.timeStamp = Date.now(); obj["e" + type + fn].call(this, e); }; obj.attachEvent("on" + type, obj[type + fn]); } }; window.removeEvent = function(obj, type, fn) { if (obj.removeEventListener) { obj.removeEventListener(type, fn, false); } else if (obj.detachEvent) { obj.detachEvent("on" + type, obj[type + fn]); obj[type + fn] = null; obj["e" + type + fn] = null; } }; |
polyfill让旧浏览器支持新功能,使用方式和标准浏览器一样,例如下面的EventListener polyfill在IE9-浏览器上实现addEventListener、removeEventListener、dispatchEvent和customEvent等。
1 | // this.Element && Element.prototype.attachEvent && !Element.prototype.addEventListener && (function () { function addToPrototype(name, method) { Window.prototype[name] = HTMLDocument.prototype[name] = Element.prototype[name] = method; } // add addToPrototype("addEventListener", function (type, listener) { var target = this, listeners = target.addEventListener.listeners = target.addEventListener.listeners || {}, typeListeners = listeners[type] = listeners[type] || []; // if no events exist, attach the listener if (!typeListeners.length) { target.attachEvent("on" + type, typeListeners.event = function (event) { var documentElement = target.document && target.document.documentElement || target.documentElement || { scrollLeft: 0, scrollTop: 0 }; // polyfill w3c properties and methods event.currentTarget = target; event.pageX = event.clientX + documentElement.scrollLeft; event.pageY = event.clientY + documentElement.scrollTop; event.preventDefault = function () { event.returnValue = false }; event.relatedTarget = event.fromElement || null; event.stopImmediatePropagation = function () { immediatePropagation = false; event.cancelBubble = true }; event.stopPropagation = function () { event.cancelBubble = true }; event.target = event.srcElement || target; event.timeStamp = +new Date; // create an cached list of the master events list (to protect this loop from breaking when an event is removed) for (var i = 0, typeListenersCache = [].concat(typeListeners), typeListenerCache, immediatePropagation = true; immediatePropagation && (typeListenerCache = typeListenersCache[i]); ++i) { // check to see if the cached event still exists in the master events list for (var ii = 0, typeListener; typeListener = typeListeners[ii]; ++ii) { if (typeListener == typeListenerCache) { typeListener.call(target, event); break; } } } }); } // add the event to the master event list typeListeners.push(listener); }); // remove addToPrototype("removeEventListener", function (type, listener) { var target = this, listeners = target.addEventListener.listeners = target.addEventListener.listeners || {}, typeListeners = listeners[type] = listeners[type] || []; // remove the newest matching event from the master event list for (var i = typeListeners.length - 1, typeListener; typeListener = typeListeners[i]; --i) { if (typeListener == listener) { typeListeners.splice(i, 1); break; } } // if no events exist, detach the listener if (!typeListeners.length && typeListeners.event) { target.detachEvent("on" + type, typeListeners.event); } }); // dispatch addToPrototype("dispatchEvent", function (eventObject) { var target = this, type = eventObject.type, listeners = target.addEventListener.listeners = target.addEventListener.listeners || {}, typeListeners = listeners[type] = listeners[type] || []; try { return target.fireEvent("on" + type, eventObject); } catch (error) { if (typeListeners.event) { typeListeners.event(eventObject); } return; } }); // CustomEvent Object.defineProperty(Window.prototype, "CustomEvent", { get: function () { var self = this; return function CustomEvent(type, eventInitDict) { var event = self.document.createEventObject(), key; event.type = type; for (key in eventInitDict) { if (key == 'cancelable'){ event.returnValue = !eventInitDict.cancelable; } else if (key == 'bubbles'){ event.cancelBubble = !eventInitDict.bubbles; } else if (key == 'detail'){ event.detail = eventInitDict.detail; } } return event; }; } }); // ready function ready(event) { if (ready.interval && document.body) { ready.interval = clearInterval(ready.interval); document.dispatchEvent(new CustomEvent("DOMContentLoaded")); } } ready.interval = setInterval(ready, 1); window.addEventListener("load", ready); })(); !this.CustomEvent && (function() { // CustomEvent for browsers which don't natively support the Constructor method window.CustomEvent = function CustomEvent(type, eventInitDict) { var event; eventInitDict = eventInitDict || {bubbles: false, cancelable: false, detail: undefined}; try { event = document.createEvent('CustomEvent'); event.initCustomEvent(type, eventInitDict.bubbles, eventInitDict.cancelable, eventInitDict.detail); } catch (error) { // for browsers which don't support CustomEvent at all, we use a regular event instead event = document.createEvent('Event'); event.initEvent(type, eventInitDict.bubbles, eventInitDict.cancelable); event.detail = eventInitDict.detail; } return event; }; })(); |
置乱数组
Javascript中置乱数组的一种方式,sort方法可以接受一个 函数为参数,当函数返回值为1的时候就交换两个数组项的顺序,否则就不交换。如下代码所示。1
yourArray.sort(function() { return 0.5 - Math.random() });
但是这种方式执行效率比较低,我还需探索更为高效的置乱方式。
1 | /* 添加原型方法的方式 */ |
这种方式已经比较高效,但是可控性不强,不能指定随机方法,我们又进行了改进。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21/* 带参数的置乱数组原型方法
* copy默认值为false,copy为true时 ,返回置乱的数组,不影响数组本身
* rng为置乱函数,默认为Math.random
* */
Array.prototype.shuffle = Array.prototype.shuffle || function(copy, rng){
var that = this, //保证原数组不受影响
i = this.length, //循环指针,初始值为数组长度
r, //随机数
t; //交换时的临时变量
copy = copy || false; //参数默认值
rng = rng || Math.random;
copy&&(that=this.slice()); //copy为true时,生成新的数组that
while(i){
r = Math.floor(rng() * i); //生成随机数
t=that[--i]; //交换数值
that[i]=that[r];
that[r]=t;
}
return that; //返回置乱后数组
}
删除字符串中的空格(Trim String)
es5中具有删除空格的原生方法String.trimLeft()、 String.trimRight()和 String.trim(),这些方法在标准浏览器中兼容性良好,trim方法在ie 9+兼容良好,trimLeft和trimRight在IE中不兼容,不兼容的浏览器我们需要使用polyfill。
1 | /* e5中trim方法的polyfill */ |
检测html标签属性
有的时候我们想知道某个元素是否具备某属性,我们可以使用该函数检测。
1 | //检测属性 |
尽量使用“===”
在javascript中,==和===都表示相等,但是==会在需要的时候进行类型转换,而===则不会进行类型转换,会比较数据类型和数据数值,比==执行速度快。
1 | [10] === 10 // is false [10] == 10 // is true '10' === 10 // is false '10' == 10 // is true [] === 0 // is false [] == 0 // is true '' === false // is false '' == false // is true but true == "a" is false |
悬挂(Hoisting)
首先来看段代码,大家先猜下运行结果。
1 | var a = 1; |
运行结果为: undefined,你猜对了吗?我们接下来看看为啥?
悬挂,也即所有函数体内的变量声明(注意,仅仅是声明)都将被提到函数体开头进行。所以上面这段代码实际上是这样执行的。
1 | var a; |
立即调用函数表达式(IIFE)
立即调用函数表达式(IIFE, Immediately Invoked Function Expressions)是Javascript里面常用特性,我们可以利用它“避免污染全局变量”、“解决闭包冲突”、“只执行一次的函数”、“减少重复创建比较大的对象的开销(常见在一些工具函数内部,保存正则对象,数组,长字符串等对象”等。1
2
3
4/*简化版的IIFE*/
(function(){
//您的代码
})();
模拟块作用域,避免污染全局变量,常见的插件即是如此。1
2
3
4/* jquery1.9.0的写法 */
(function( window, undefined ) {
//非常长的代码
})( window );
解决闭包冲突,我们知道闭包可以让“函数内部所定义的函数持有外层函数的执行环境”,然而也有可能有些问题,例如下面的代码。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15var f1 = function() {
var res = [];
var fun = null;
for(var i = 0; i < 10; i++) {
fun = function() { console.log(i);};//产生闭包
res.push(fun);
}
return res;
}
// 会输出10个10,而不是预期的0 1 2 3 4 5 6 7 8 9
var res = f1();
for(var i = 0; i < res.length; i++) {
res[i]();
}
我们可以使用IIFE解决这个问题,修正过的代码如下。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18var f1 = function() {
var res = [];
for(var i = 0; i < 10; i++) {
// 添加一个IIFE,立即执行
(function(index) {
fun = function() {console.log(index);};
res.push(fun);
})(i);
}
return res;
}
// 输出结果为0 1 2 3 4 5 6 7 8 9
var res = f1();
for(var i = 0; i < res.length; i++) {
res[i]();
}
模拟单例,javascript中我们可以使用IIFE实现OOP。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19var counter = (function(){
var i = 0;
return {
get: function(){
return i;
},
set: function( val ){
i = val;
},
increment: function() {
return ++i;
}
};
}());
counter.get(); // 0
counter.set( 3 );
counter.increment(); // 4
counter.increment(); // 5
配合逻辑运算符使用,例如addEventListener的polyfill可以这么写。1
2
3
4
5
6/*把IIFE和逻辑运算符配合使用,检测是否需要运行polyfill*/
this.Element &&
Element.prototype.attachEvent && !Element.prototype.addEventListener &&
(function () {
//polyfill
}
逻辑运算符妙用
使用&&和||条件运算符,我们可以达到简化操作的目的,来看下面代码。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41/* code one */
if (!theTitle) {
theTitle = "Untitled Document";
}
//使用||
theTitle = theTitle || "Untitled Document";
/* code two */
function isAdult(age) {
if (age && age > 17) {
return true;
}
else {
return false;
}
}
//使用 &&
function isAdult(age) {
return age && age > 17 ;
}
/* code three*/
if (userName) {
logIn (userName);
}
else {
signUp ();
}
//混合使用&&、||
userName && logIn (userName) || signUp ();
/*code four*/
var userID;
if (userName && userName.loggedIn) {
userID = userName.id;
}
else {
userID = null;
}
//使用&&、||
var userID = userName && userName.loggedIn && userName.id
##深入
本文的写作过程大量参考了以下文章,大家可以仔细阅读下面文章获得更深的体会。
- 45 Useful JavaScript Tips, Tricks and Best Practices
- 12 Simple (Yet Powerful) JavaScript Tips
- 13 JavaScript Tips & Tricks
- 21 JavaScript Tips And Tricks For JavaScript Developers
- CSS-Tricks Javascript Snippets
- JavaScript Snippets
- JavaScript Snippets
- 5 Array Methods That You Should Be Using Now
- JS-Tricks
- ten-useful-javascript-tips-best-practices
- javascript-performance-tips-tricks
- Superpower your JavaScript with 10 quick tips - For Beginners
##声明
前端开发whqet,关注前端开发,分享相关资源。csdn专家博客,王海庆希望能对您有所帮助,限于作者水平有限,出错难免,欢迎拍砖!
欢迎任何形式的转载,烦请注明装载,保留本段文字。
本文原文链接,http://blog.csdn.net/whqet/article/details/43865127
欢迎大家访问独立博客http://whqet.github.io