这一节是继上一节高质量的Javascript
7)编程实用技巧
1:弹性
从 一个标签区和内容区的实例(就是点击不同的标签菜单显示不同的内容块)来说明不需要每个tabmenu都设置onclick事件,为了让程序更有弹性,可 以将所有的点击时间封装成一个函数,变化的标签作为参数传入实现点击不同的标签显示对应的内容块,这样标签的数量可自适应,可增可减,而js代码可以不用 变动,只需要修改html的标签就可以。-------------这点我想做过项目的伙伴们都能深深的体会到。
琐碎知识点:
js 一个非常经典的问题:在遍历数组时对DOM监听事件(如上例中的菜单单击事件tabMenus[i].onclick),索引值始终等于遍历结束后的值。 解决这一问题可以用闭包,或者给每个DOM节点添加Index属性用来标示区别。---------我遍历一般用jquery的each函数,挺好用的, 没用过Dom,但是还是一个值得注意的地方。
2:可复用性
因为一个html页面的id只能出现一次,所以,如果你的程序需要被多次复用,就一定不能用id来标识,不能用id作为js获得DOM节点的挂钩,更好的应该是使用class. 在做项目过程中,我们可以通过class取得一组Dom标签,但由于id的唯一性,只适合取某个固定的标签。
组件需要指定根节点,以保持每个组件之间的独立性。--------具体的可能要看书上的代码示例来体会。(就是尽管有相同的class来标识同类结构(一个组件),但是不同的组件与组件之间还需要指定一个父节点来区别对待)。
3:通过参数实现定制
如果每个tab标签需要不同的样式,这个时候就只能根据每个tabMenu的唯一标示作为参数传到处理函数,在函数中根据不同的标识来选择不同的样式
如果一个函数内某个因素很不稳定,我们可以将它从函数内部分离出来,以参数的形式传入,从而将不稳定的因素和函数解耦。---与第一点弹性有相似处
4:this关键字的指向
在js全局域中 this指向window.
A:JavaScript伪协议和内联事件对于this的指向不同:
伪协议:a标签的href="javascript:alert(this==window)" 结果:弹出true 伪协议中this指向window
内联事件:onclick="alert(this.tagName)" 结果:弹出a 内联事件的this指向当前的Dom元素
B:setTimeout和setInterval也会改变this的指向,在这两个函数中 this指向window
C:DomNode.on***事件也会改变this的指向,this会指向当前调用时间的Dom结点。
使用匿名函数可将B,C中this的指向改变。
例:
1 <script type="text/javascript"> 2 var name = "global-name"; 3 var btn = document.getElementById("btn"); 4 var test = { 5 name: "test-name", 6 say: function () { 7 alert(this.name); 8 } 9 } 10 test.say(); //输出test-name 11 setTimeout(function () { test.say() }, 1000); //输出test-name 匿名函数会改变指向,谁调用就指向谁 12 /* 13 setTimeout和setInterval两个函数的调用方式,第一个参数应该是函数指针,或字符串 14 */ 15 setTimeout('test.say()', 1000); //作为字符串,还是由test调用,输出test-name 16 setTimeout(test.say, 1000); //作为函数指针,输出global-name setTimeout的直接调用指向方式,this指向window 17 18 setInterval(function () { test.say() }, 1000); //输出test-name 19 setInterval('test.say()', 1000); //输出test-name ,由test主调 20 setInterval(test.say, 1000); //输出global-name 21 setTimeout(function () { alert(this == window) }, 1000); //输出true, this为全局window对象 22 23 $(function () { 24 var btn2 = document.getElementById("btn"); 25 btn2.onclick = function () { test.say() }; //输出test-name 26 btn2.onclick = function () { alert(this == btn2) }; //输出true, this为DOM元素对象 27 }); 28 </script> 29 <input type="button" name="btn-name" id="btn" value="test-btn" />
总结:1:如果setTimeout和setInterval调用的处理函数,该处理函数是“直接调用的函数”,那么this指向window
2:DomNode.on***关联的处理函数,该处理函数是“直接调用的函数”,this指向Dom结点
3:如果将上面的处理函数用匿名函数封装起来,那么,处理函数的调用方式:由直接调用变为间接调用;这样就不会受到外面调用函数的影响,this该指向谁就指向谁
自己的话:一般来说,this----该动作是谁调用的,那么this就指向调用者,只有碰到setTimeout、setInterval、btn.on***等会改变指向的函数,才不会遵守基本规则,但是也可以通过匿名函数的间接调用来解决,这样又变回了一般状态。
另外还可以通过call和apply函数来改变处理函数的this指向,test.say.call(btn); test.say.apply(btn); this 直接指定的。
5:预留回调接口
我所理解的就是,在函数中预留出一个参数handler,该参数的实参是一个函数。 判断回调函数是否有用,if(handler){ handler(参数);//调用回调函数;}
添加回调的接口可以参加代码的可扩展性。
6:编程中的DRY规则
DRY----don't repeat yourself,强调在程序中不要将相同的代码重复编写多次,更好的做法是只写一次,然后多次引用。提高重用率,减少代码量
7:用hash传参
比较: 普通传参方式:参数量大,而且参数的顺序很重要,
hash传参方式:hash是一个key-value的集合,可包含任意类型的数据,用hash对象传参,可以提高函数调用的灵活性,没有顺序的控制
如: test函数有6个参数,调用时有些为空,有些不为空,普通传参: test(null,null,null,null,null,"hello"); 用hash传参: test( {str:"hello"} ) 显然第二种简单直观.
8)面向对象编程
1:面向对象
将数据和处理函数定义到了一个对象的内部,作为这个对象的属性和行为存在,在对象的内部,属性和行为通过this关键字来访问,在对象的外部,用对象的属性和对象的行为来调用。 额~,抽象的概念,理解起来就是别扭。
它的思维过程是定义一个对象,对象有自己的属性和行为。属性和行为都从属于对象,于是有对象内,和对象外的概念。
整个程序可以由一堆对象组成,对象和对象之间可能会有通讯,为了能相互访问,于是就有了私有和公有的概念。
2:js的面向对象
A:类的概念
一般的类定义,都要class关键字,js中的类没有class关键字,它是用函数来充当类的。函数在js中既可以用作普通函数,也可以当类来使用,当充当类时,又担负着构造函数的作用。
fuction test(){//code...}
调用方式: 函数充当普通函数,直接使用()进行调用。 如:test()
函数充当类时,使用new来实例化。如:var c=new test();
B:原型(prototype)
原型在js中是一个很重要的概念,因为JS是基于原型的语言,通过new实例化出来的对象,其属性和行为来自于两部分:1:构造函数(既函数本身),2:原型
原型的概念及由来:我们只需要知道“在声明一个类的时候,同时就生成了一个对应的类的原型。”------------我理解为同样的生命周期。
通过test.prototype就可以指向这个原型, 而原型也可以通过它的constructor属性指向test类,具体指的是test类的构造函数。
只有当函数作为类使用,new出来一个对象时,原型才具有它存在的价值(个人观点)
原型是个hash对象,也可以分开定义
如:test.prototype={ 分开定义:
name:"***", test.prototype.name="***";
type:"***", test.prototype.type="***";
say:function(){.....} test.prototype.say=function(){...};
}
C :优先级
当构造函数和原型中都定义了同名的属性和行为,则构造函数中的属性和行为优先级要高,它会覆盖原型中的属性和行为。
this关键字无论是出现在构造函数还是原型中它指代的都是实例对象。能改变this指向的函数就另当别论。
D:公有和私有
js中没有public,protect,private等关键字,js中的公有还是私有是通过作用域来实现的。
用this.***定义的属性都是公有的(原因:在构造和原型中的this 都是同一个实例对象,在原型中可以访问得到),而用var ***定义的属性都是私有的(在原型中访问不到)。方法的公有私有也一样用this区别开来。
习惯: 定义类时,一般我们会把属性(变量)放在构造函数里------方便构造函数接收参数,而行为(方法)放在原型里。
原因:因为在内存中一个类的原型只有一个,写在原型中的行为可以被所有实例共享,实例化时不会在实例的内存中复制一份;而写在类中的行为,会每个实例都会复制一份。--------------为了减少内存消耗。
自己的话:实例化时,原型中的属性和行为是引用,构造函数中的属性和行为是复制。
3:继承
琐碎知识点:在js中,fuction作为普通函数存在时,直接使用()进行调用,函数内的this是指向window;
function作为类存在时,通过new实例化,类里面的this指向实例对象。
A:构造函数中属性和行为的继承
为了实现构造函数中属性和行为的继承,可以通过call/apply方法来实现。
如:
当实例化B的对象时就能访问到构造函数里的属性和方法。
B:原型中属性和行为的继承
琐碎知识点:js中的传值和传址。
在js中,赋值语句会用传值和传址两种不同的方式进行复制,如果是数值型,布尔型,字符型等基本数据类型,将复制一份数据进行赋值。---传值
如果是"数值,hash对象等复杂类型"(数值,hash对象可包含简单类型数据),在进行赋值时会直接使用内存地址赋值。-----传址
原型中的属性和行为的继承,我们可以直接将A的原型赋值给B的原型,如B.prototype=A.prototype
这样B的实例及B.prototype 是可以访问A类原型中的say方法,但是prototype本质上是一个hash对象,它的赋值是传址的方式,当我们在B的原型中在添加一个AddFunctionForB()方法时,由于传址方式,在A的原型中也会添加B的AddFunctionForB()方法.
如:
为了解决这个问题,我们用另一种方法实现prototype的传值-new somefunction() ----new出基类的对象,然后重新定向B类的构造。
在上面的代码中我们定义了A类及A类的原型、B类;我们只需要将B类的原型中指向A类的实例对象(这时,B类会继承A类的构造及原型中的属性和方法),这样就可以访问A类原型中的方法。但由于这种赋值会使得B.prototype.constructor指向了A类的构造,所以我们要将它纠正,重新指向B类。
这样就解决了上面的问题,既能从原型继承方法,又不会引起不必要的混乱。
9)prototype和内置类
从第8点知道了prototype和类的关系,现在看看prototype和js自带的内置类的关系。
Js的内置类包括Array,String,Function等 如Array提供length属性,push.pop方法,String提供length属性,replace.split方法 ,Fuction提供Call.apply方法.
一般内置类我们一般不用New实例化,习惯更简单的方式,如 var a="sssss"; 而不是 var a=new String("sssss");
只要是类就会有原型,So 我们可以对内置类的原型进行修改,以重写或扩展它的功能。
如:常见的有 Array.prototype.each=function(){....} Array.prototype.map=function(){.....}
里面可能会涉及到this指针的指向,我们只需要记住,在类的构造函数和原型中的this ,都是指向该类实例化的对象。
注意:内置类的方法可以重写,比如toString()方法,但是 他的属性不能重写,如length;
10)标签的自定义属性
在html语言中的标签一般有自己的属性,如 id,class href等,我们有时候会用到自定义的属性
为了从兼容性来考虑,用JS来读取属性时,笔者建议对于常规属性,统一使用node.***的方式读取,对于自定义属性,统一使用node.getAttribute("***")读取。
自定义属性一个非常有用的技巧:----将普通字符串转换为hash对象或数组
字符串的反序列化-----------通过eval函数来实现。
如在a标签里自定义一个属性<a id="a" userinfo="name:'alice' , age:22 , pwd:'123456' " ></a>
取到a的userinfo属性值 var info = document.getElementbyId("a").getAttribute("userinfo");
alert(typeof info) //string 类型 访问info.name info.age都访问不到
info=eval("("+info+")"); 转化为对象类型; 访问info.name 输出alice ; 访问 info.age 输出22;
11) 标签的内联事件和event对象
在IE下,event是window对象的一个属性,是全局作用域下的; 在FF中,event对象是作为事件的参数存在。
在标签的内联事件中,FF下,使用arguments[0]可以访问到event对象。不是内联事件,可以给处理函数传参来访问 funtion(e){...}
关于event对象前面的章节中也有讲过,这里就不再描述了。
12)一些规则
基本在前面的内容中均已描述过,包括css命名规则、注释规则、html规范、css规范、js规范。
到这全书就结束了。将学习笔记记录下来希望对看到该文的朋友有一定的帮助。O(∩_∩)O~
转载请注明出处
原文地址:http://www.cnblogs.com/Joans/archive/2012/09/14/2685110.html