Javascript开发经验V1
2012-05-29 17:03 臭小子1983 阅读(337) 评论(0) 编辑 收藏 举报一、简化代码
采用更为简短的写法,不仅可以减少输入的字符数,还可以减少文件大小。大部分采用简单写法的代码,执行效率都有轻微提高。
1.1 简化常用对象定义:
使用 var obj = {}; 代替 var obj = new Object();
使用 var arr = []; 代替 var arr = new Array();
1.2 精简if语句
三元操作符可以有效精简只涉及赋值传值操作的if语句,比如
1 var score = 60, grade; 2 if (score < 60) { 3 grade = “不及格”; 4 } 5 6 else { 7 grade = “及格”; 8 }
可以精简为:
1 var score = 60; 2 var grade = score < 60 ? “不及格” : “及格”;
三元操作符也支持嵌套,但是嵌套的层次太多会影响程序的可读性,这方面要多加斟酌。
1.3 使用JSON
JSON是一种轻量级的数据格式,轻量级首先体现在它的结构定义非常简单。
1 var obj = {}; 2 obj.p1 = ‘a’; 3 obj.p2 = ‘b’; 4 obj.p3 = ‘c’; 5 6 可精简为: 7 8 var obj = { 9 p1 : ‘a’, 10 p2 : ‘b’, 11 p3 : ‘c’ 12 };
二、使用高效率的代码
网上流传的效率优化文章非常多,一些比较专业的Javascript书籍也谈到了不少,因此,这里就只列出一些很少谈到的。
2.1 精简循环体
循环的效率很大程度上是由循环体决定的,与之相比,用for还是while的差别就太小了。考虑如下的代码,其功能是为某一批元素添加事件:
1 function addEvent(elems, eventName, handler) { 2 for (var i = 0, len = elems.length; i < len; i++) { 3 if (window.attachEvent) { 4 elems[i].attachEvent(”on” + eventName, handler); 5 } 6 else if (window.addEventListener) { 7 elems[i].addEventListener(eventName, handler, false); 8 } 9 } 10 }
循环每执行一次,都会判断window对象的attachEvent或addEventListener是否存在,其实这个仅判断一次也就够了;此外,“”on” + eventName”的字符串拼接也会重复执行。优化如下:
1 function addEvent(elems, eventName, handler) { 2 var i = -1, len = elems.length; 3 if (window.attachEvent) { 4 eventName = “on” + eventName; 5 while (++i < len) { 6 elems[i].attachEvent(eventName, handler); 7 } 8 } 9 else if (window.addEventListener) { 10 while (++i < len) { 11 elems[i].addEventListener(eventName, handler, false); 12 } 13 } 14 }
2.2 尽量使用原生的函数而不是自定义函数
当你对Javascript的内置类型变量执行某项操作时,你应该先查查这项操作是否有原生的方法。
要生成一个数组的副本,你会怎么做呢?遍历数组元素然后逐个赋值到另一个数组,这似乎是唯一的方法。其实,原生的Array.prototype.slice就可以达到复制的目的。这个方法可以从某个数组返回选定的元素,且不影响原来的数组。如果参数留空,返回的就是全部元素。
Array.prototype.slice还可以对某些不是数组而又能通过数字索引访问的类型进行操作,比如arguments:
1 function test() { 2 alert(Array.prototype.slice.call(arguments)); 3 } 4 test(1, 2, 3); // output “1,2,3″
在Firefox下,它甚至可以对HtmlCollection进行操作。可惜在IE下不行。
另一个例子是数组排序,一般情况下,我们不需要另外写排序算法,用原生的Array.prototype.sort就够了。sort方法只有一个参数,该参数是一个函数,决定两个相比较的元素谁在前谁在后,默认是按照字符顺序排序,比如11会排在2之前。要按数字大小排序,可以这样写:
1 var arr = [11, 2, 0, 12, 33]; 2 arr.sort( 3 function(a, b) { 4 return a - b; 5 } 6 );
也可以按照对象的某个属性进行排序:
1 var arr = [ 2 { id : 11 }, 3 { id : 0 }, 4 { id : 22 } 5 ]; 6 arr.sort( 7 function(a, b) { 8 return a.id - b.id; 9 } 10 );
2.3 数组去重复
1 function unique(arr) { 2 var result = [], isRepeated; 3 for (var i = 0, len = arr.length; i < len; i++) { 4 isRepeated = false; 5 for (var j = 0, len = result.length; j < len; j++) { 6 if (arr[i] == result[j]) { 7 isRepeated = true; 8 break; 9 } 10 } 11 if (!isRepeated) { 12 result.push(arr[i]); 13 } 14 } 15 return result; 16 }
Array类型并没有提供去重复的方法,如果要把数组的重复元素干掉,那得自己想办法:
总体思路是把数组元素逐个搬运到另一个数组,搬运的过程中检查这个元素是否有重复,如果有就直接丢掉。从嵌套循环就可以看出,这种方法效率极低。我们可以用一个hashtable的结构记录已有的元素,这样就可以避免内层循环。恰好,在Javascript中实现hashtable是极为简单的,改进如下:
1 function unique(arr) { 2 var result = [], hash = {}; 3 for (var i = 0, elem; (elem = arr[i]) != null; i++) { 4 if (!hash[elem]) { 5 result.push(elem); 6 hash[elem] = true; 7 } 8 } 9 return result; 10 }
三、使代码更易读、更好维护
无论是在开发中还是开发后,保持代码清晰易读可以更快更准确地修改代码。
3.1 连接HTML字符串
相信做前端开发的朋友都受过这个折磨:连接HTML的时候被可恶的单引号、双引号搞得头昏脑胀。比如:
1 element.innerHTML = ‘<a href=”‘ + url + ‘” onclick=”alert(\” + msg + ‘\’);”>’ + text + ‘</a>’; 2 这里介绍一个字符串格式化函数: 3 String.format = function(str) { 4 var args = arguments, re = new RegExp(”%([1-" + args.length + "])”, “g”); 5 return String(str).replace( 6 re, 7 function($1, $2) { 8 return args[$2]; 9 } 10 ); 11 };
调用方法很简单:
element.innerHTML = String.format(’<a href=”%1″ onclick=”alert(\’%2\’);”>%3</a>’, url, msg, text);
意思就是用第n个参数把%n替换掉。这样一来清晰多了吧。
3.2 为您的程序打造一个Config配置对象
编写Java或者C#程序的时候,我们一般会从XML读取程序的配置信息。在Javascript里面,用XML做配置信息不大划算,一方面要多请求一个XML文件或者把XML字符串转换为XML对象,另一方面XML对象的方法比较复杂冗长。轻量级的JSON是最好的选择。
程序中的常量应该放到Config配置对象中,比如Ajax请求的Url、某个操作的提示等,例如:
1 var Config = { 2 ajaxUrl : “test.jsp”, 3 successTips : “请求完成” 4 }; 5 如果Config的数量较多,可以根据配置类型多嵌套一层,比如: 6 var Config = { 7 url : { 8 src1 : “test1.jsp”, 9 src2 : “test2.jsp”, 10 . 11 . 12 }, 13 tips : { 14 src1Suc : “请求1完成”, 15 src2Suc: “请求2完成”, 16 . 17 . 18 } 19 }; 20 Config应放置于程序的最前面,方便查看和修改。
1、json字符串转成json对象:eval("(' + data + ')")
2、json对象转成Json字符串:eval('"{\'football_live_event\':\'" + sPlayerId +"\'}"')
3、查看对象中是否有指定的属性: var obj = {pro:"111"} if("pro" in obj){} // true
四、IE下操作iframe出现SCRIPT5: 拒绝访问错误提示
在同一域下,因在父页面上设置了document.domain,而导致无法正常和Iframe(也是同域下)进行通信,IE下抛出的错误是:SCRIPT5: 拒绝访问,导致无法操作iframe中的内容。
解决方法:
1、不要设置父页面的domain(如果可以)
2、让双方均设置同样的domain
参考:http://ningxiaotao.iteye.com/blog/579321
五、iframe的contentWindow属性 指定的frame或者iframe所在的window对象
1 function f(){ 2 var doc; 3 4 if (document.all){//IE 5 doc = document.frames["MyIFrame"].document; 6 }else{//Firefox 7 doc = document.getElementById("MyIFrame").contentDocument; 8 } 9 10 doc.getElementById("s").style.color="blue"; 11 }
六、document.domain 跨域问题
1、domain只能在根域、子域之间来设置,如,www.test.com和live.test.com之间不同的域
1 var sUrl = lcoation.href; // 比如是 a.test.com 2 if(sUrl.indexOf("b.test.com") != -1){ // 如果当前在b.test.com域中 3 document.domain = "a.test.com"; // 将域设置为a.test.com 4 };
用来得到当前网页的域名。
比如在地址栏里输入:
javascript:alert(document.domain); //www.315ta.com
我们也可以给document.domain属性赋值,不过是有限制的,你只能赋成当前的域名或者基础域名。
比如:
javascript:alert(document.domain = "315ta.com"); //315ta.com
javascript:alert(document.domain = "www.315ta.com");//www.315ta.com
上面的赋值都是成功的,因为www.315ta.com是当前的域名,而315ta.com是基础域名。
但是下面的赋值就会出来"参数无效"的错误:
javascript:alert(document.domain = "cctv.net"); //参数无效
javascript:alert(document.domain = "ttt.315ta.com"); //参数无效
因为cctv.net与ttt.315tas.com不是当前的域名也不是当前域名的基础域名,所以会有错误出现。
这是为了防止有人恶意修改document.domain来实现跨域偷取数据。
利用document.domain实现跨域:
前提条件:这两个域名必须属于同一个基础域名!而且所用的协议,端口都要一致,否则无法利用document.domain进行跨域
Javascript出于对安全性的考虑,而禁止两个或者多个不同域的页面进行互相操作。
相同域的页面在相互操作的时候不会有任何问题。
比如在:aaa.com的一个网页(a.html)里面利用iframe引入了一个bbb.com里的一个网页(b.html)。
这时在a.html里面可以看到b.html里的内容,但是却不能利用javascript来操作它。因为这两个页面属于不同的域,在操作之前,js会检测两个页面的域是否相等,如果相等,就允许其操作,如果不相等,就会拒绝操作。
这里不可能把a.html与b.html利用JS改成同一个域的。因为它们的基础域名不相等。(强制用JS将它们改成相等的域的话会报跟上面一样的"参数无效错误。")
所以如果在a.html里引入aaa.com里的另一个网页,是不会有这个问题的,因为域相等。
有另一种情况,两个子域名:
aaa.xxx.com
bbb.xxx.com
aaa里的一个网页(a.html)引入了bbb 里的一个网页(b.html),
这时a.html里同样是不能操作b.html里面的内容的。
因为document.domain不一样,一个是aaa.xxx.com,另一个是bbb.xxx.com。
这时我们就可以通过Javascript,将两个页面的domain改成一样的,
需要在a.html里与b.html里都加入:
document.domain = "xxx.com";
这样这两个页面就可以互相操作了。也就是实现了同一基础域名之间的"跨域"。
七、去除数组内容
使用splice可以对级数进行添加、删除、替换,
扩展Array方法,
1 Array.prototype.remove = function(val) { 2 var index = this.indexOf(val); 3 if (index > -1) { 4 this.splice(index, 1); 5 } 6 }; 7 8 var arr = [1,34,5,6,7,8]; 9 arr.remove(5); // 返回[1,34,6,7,8]
八、转成整形parseInt问题
在字符串转成数字时,如果以“0”开头的字符不定义以几进制来转换在ie7、8下有问题
1 var str1 = "125"; 2 var str2 = "012"; 3 console.log(parseInt(str1)); // 返回125 4 console.log(parseInt(str2)); // 返回10
九、数组对象和数组字符串排序
1、以 数组对象 值的大小来排序
1 function sortNum(){ 2 var arr = [{"6":100}, {"5":22}, {"3":560}, {"1":17}, {"9":67}, {"8":1}, {"4":13}, {"2":46}, {"7":20}, {"10":500}] 3 // var arr = [["6",100], ["5",22], ["3",560], ["1",17], ["9",67], ["8",1], ["4",13], ["2",46], ["7",20], ["10",500]]; 4 5 arr.sort(actionSort); // 排序 6 7 for(var i=0; i<arr.length; i++){ 8 for(var key in arr[i]){ 9 console.log("a", key, arr[i][key]); 10 } 11 } 13 } 14 15 function actionSort(v1, v2){ 16 var sVal1 = 0; 17 var sVal2 = 0; 18 $.each(v1, function(i,val){ sVal1 = parseInt(val) }); 19 $.each(v2, function(i,val){ sVal2 = parseInt(val) }); 20 return sVal1 - sVal2; 21 } 22 23 sortNum();
2、以数组字符串来进行排序
1 function smallSort(v1, v2){ 2 // 13:4,02:1,11:3 3 var nVal1 = 0; 4 var nVal2 = 0; 5 6 nVal1 = v1.split(":")[0]; 7 nVal2 = v2.split(":")[0]; 8 9 return nVal1 - nVal2; 10 } 11 12 var str = "13:4,02:1,11:3"; // 以13、02、11进行排序 13 var sArr = str.split(","); 14 console.log(sArr); 15 sArr.sort(smallSort); 16 console.log(sArr);
3、对数组对象的一个属性值进行排序
1 <script> 2 var data = [{id:1, year:10}, {id:2, year:250}, {id:2, year:3850}, {id:2, year:30}, {id:2, year:2}]; 3 4 data.sort(function(d1, d2){ 5 return d1.year > d2.year; 6 }) 7 8 console.log(data); 9 </script>
十、日期转换
如果new Date获取什么1970年到现在的毫秒数需要.getTime()
也可以简写成 var setDate = new Date(); console.log(+setDate);
1、js毫秒时间转换成日期时间
var oldTime = (new Date("2012/12/25 20:11:11")).getTime(); //得到毫秒数
// 不是上面格式的时间需要转换
// starttime ='2012-12-25 20:17:24';
starttime = starttime.replace(new RegExp("-","gm"),"/");
var starttimeHaoMiao = (new Date(starttime)).getTime(); //得到毫秒数
2、毫秒数转化为时间
var oldTime = (new Date("2012/12/25 20:11:11")).getTime(); //得到毫秒数
var newTime = new Date(oldTime); //就得到普通的时间了
十一、js跳转锚点
<div name="traderList"></div>
window.location.hash = "traderList";
十二、js绘制圆
1 <script> 2 3 function getId(id){ 4 return document.getElementById(id); 5 } 6 7 var oCanvas = getId("canvas"); 8 var oContext = oCanvas.getContext("2d"); 9 10 function runRect(x, y){ 11 oContext.fillStyle = "#ccc"; 12 oContext.fillRect(x, y, 2, 2); 13 } 14 15 var x = 0; y = 0; 16 var timer = setInterval(function(){ 17 18 var nX = Math.round(200 * Math.sin((x++) * Math.PI / 180))+200; // 公式:半径 * Math.sin(步数) * Math.PI / 180 19 var nY = Math.round(200 * Math.cos((y++) * Math.PI / 180))+200; 20 21 console.log(nX, nY); 22 runRect(nX, nY); 23 24 }, 10); 25 26 27 var oBtn = getId("but"); 28 oBtn.onclick = function(){ 29 clearInterval(timer); 30 } 31 32 </script>
十三、控制子页面(Iframe)文档
一、父页面控制iframe子页面
1、contentWindow属性来获取iframe的window对象,都兼容,但在chrome下需要有服务器环境才可以,chrome安全沙箱的原因
2、contentDocument属性来获取iframe的document对象,不兼容ie6、7
1 <iframe id="ifm" src="iframe.html"></iframe> 2 <input type="button" id="but" value="改变iframe"> 3 4 <script> 5 function getId(id){ 6 return document.getElementById(id); 7 } 8 9 var oIfm = getId("ifm"); 10 var oBut = getId("but"); 11 12 function editIframe(){ 13 // 如果我们要操作一个iframe的DOM元素使用contentWindow来引用 14 // 也可以作用于一个新窗口的操作 15 var ifrdoc = oIfm.contentWindow; 16 // var s = fixingHTB.innerHTML; // 进入可编辑模式前存好 17 ifrdoc.document.designMode = 'On'; // 文档进入可编辑模式 18 ifrdoc.document.contentEditable = true;// 文档进入可编辑模式 19 // ifrdoc.open(); 20 21 // 改变Iframe背景颜色 22 ifrdoc.document.body.style.background = "red"; 23 24 // ifrdoc.close(); 25 } 26 27 oBut.onclick = function(){ 28 editIframe() 29 } 30 </script>
二、iframe控制父页面
1、window.parent来获取父页面的window对象
1 <div id="box"> 2 <iframe src="index.html" id="ifr"></iframe> 3 <input type="button" id="btn" value="改变"> 4 </div> 5 6 <script> 7 var oIfr = document.getElementById("ifr"); 8 var oBtn = document.getElementById("btn"); 9 10 oBtn.onclick = function(){ 11 var docIfr = oIfr.contentWindow.document.getElementById("listNum"); 12 docIfr.style.backgroundColor = "#ff0000"; 13 } 14 </script> 15 16 // iframe页面index.html 17 var oSelectBtn = document.getElementById("selectBtn"); 18 selectBtn.onclick = function(){ 19 var parentIfr = window.parent.document.getElementById("ifr"); 20 parentIfr.style.width = "500px"; 21 parentIfr.style.height = "300px"; 22 }
2、window.top为找到最顶层,例如:index页面嵌套一个iframe为demo1.html,demo1中又嵌套一个iframe为demo2,这样如果在demo2中使用window.parent,其实找到的demo1这个页面,如果使用window.top就可以直接找到index.html这个页面
三、iframe的onload事件
当iframe加载完后也就是onload事件,但在IE要执行onload必须得使用事件绑定才可以兼容
1 window.onload = function(){ 2 var oIfr = document.getElementById("ifr"); 3 oIfr.src = "index.html"; 4 5 addEvent(oIfr, "load", function(){ 6 alert("aa"); 7 }) 8 9 function addEvent(obj, type, fn){ 10 if(obj.addEventListener){ 11 obj.addEventListener(type, fn, true); 12 } 13 else{ 14 obj.attachEvent("on"+type, fn); 15 } 16 } 17 }
十四、onMouseEnter、onMouseLeave与onMouseOver、onMouseOut的区别
在IE下解决问题很简单,用onMouseEnter、onMouseLeave来代 替onMouseOver、onMouseOut就行了,他们的作用基本相同,前者不会发生冒泡。
区别:
不论鼠标指针离开被选元素还是任何子元素,都会触发 mouseout 事件。
只有在鼠标指针离开被选元素时,才会触发 mouseleave 事件。
1 <style> 2 .select { width: 300px; position: relative;} 3 .sel_nav { width: 298px; height: 28px; border: 1px #ccc solid; text-align: center; line-height: 28px; cursor: pointer; } 4 .sel_box { width: 298px; border: 1px #ccc solid; position: absolute; top: 29px; left: 0; display: none; } 5 .sel_box li { width: 298px; height: 30px; line-height: 30px; border-bottom: 1px #ccc solid; background:#90BEDB; text-align: center;} 6 .sel_box li.active { background: #414244;} 7 </style> 8 <div class="select" id="sel"> 9 <div class="sel_nav"> 10 选择内容 11 </div> 12 <div class="sel_box"> 13 <ul> 14 <li>1111</li> 15 <li>2222</li> 16 <li>3333</li> 17 <li>4444</li> 18 <li>5555</li> 19 </ul> 20 </div> 21 </div> 22 23 <script src="jquery-1.7.1.min.js"></script> 24 <script> 25 function Select(id){ 26 this.sel = $(id); 27 this.nav = $(".sel_nav", this.sel); 28 this.box = $(".sel_box", this.sel); 29 30 this.init(); 31 } 32 33 Select.prototype.init = function(){ 34 var o = this; 35 var isOpen = false; 36 37 o.sel.mouseleave(function(){ // 如果使用mouseout比如鼠标移到文字“选择内容”时也会触发 38 o.box.slideUp(300); isOpen = true; 39 }) 40 41 o.nav.click(function(){ 42 if(isOpen){ 43 o.box.slideUp(300); 44 isOpen = false; 45 } 46 else{ 47 o.box.slideDown(300); 48 isOpen = true; 49 } 50 }) 51 52 $("li", o.sel).mouseover(function(){ 53 $(this).addClass(o.active).siblings().removeClass(o.active); 54 }) 55 56 $("li", o.sel).click(function(){ 57 var val = $(this).html(); 58 o.nav.html(val); 59 o.box.slideUp(300); 60 isOpen = false; 61 }) 62 } 63 64 var sel = new Select("#sel") 65 </script>
十五、jQuery的音频插件jquery.jmp3,消耗CPU的问题
原理,调用一次,在html标签插入一个<object>标签来创建一个flash的播放器,在读出声音
项目中使用jquery音频插件来读取音,当循环多次时会极度消耗cpu解决方法:
1、如果是循环的创建只输出一次
2、每次创建前将元素内清空,这样最后播放的时候只播放一次,而不是循环的多次.
十六、JavaScript中双叹号“!!”作用
!!一般用来将后面的表达式强制转换为布尔类型的数据(boolean),也就是只能是true或者false;
var b=!!a;
a默认是undefined。!a是true,!!a则是false,所以b的值是false,而不再是undefined,也非其它值,主要是为后续判断提供便利。
十七、在一个元素上同时绑定click和dbclick事件的处理方法
双击(dblclick)的流程是:mousedown,mouseout,click,mousedown,mouseout,click,dblclick;
在双击事件(dblclick),触发的两次单击事件(click)中,第一次的单击事件(click)会被屏蔽掉,但第二次不会。也就是说双击事件(dblclick)会返回一次单击事件(click)结果和一次双击事件(dblclick) 结果。而不是一次双击事件(dblclick)结果和两次单击事件结果(click)。
所以可以使用计时的方式去除掉一个多余的单击事件就行了
1 <script> 2 3 var TimeFn = null; 4 $("#div1").click(function () { 5 clearTimeout(TimeFn); 6 TimeFn = setTimeout(function(){ 7 console.log("a"); 8 },200); 9 }); 10 11 $('#div1').dblclick(function () { 12 clearTimeout(TimeFn); 13 console.log("b"); 14 }) 15 16 </script>