编写高质量的代码注意的问题

一、网站重构

  Html标签只负责承载内容,样式交给css,行为交给Javascript。做到“精简,重用,有序”。

二、团队合作  

欲精一行,必先通十行

 “经常听到做前端开发的朋友抱怨要学的东西太多,东学一点,西学一点,什么都会,但是都不精,于是有人认为‘通十行不如精一行’,而在前端领域,这句确行不通,对于前端来说,你不通十行,就无法精一行。” 看到作者的这句话,我总算有点成就感。O(∩_∩)O~

这章主要说明前端需要了解的语言,html, css要精通、及其重要,js,及架构div+css,RIA富媒体应用,jquery,YUI,ps,AI设计等等

团队之间的开发要注意:

1:增加代码可读性----注释

2:重用性---公共组件和私有组件的维护

3:冗余和精简的矛盾---选择集中还是选择分散   (合理的前端架构中css和js都会提取公共组件,如何组织需要权衡,完美的解决方案不存在,只能在冗余和精简中尽量找到最佳平衡点)

4:前期的构思很重要。

5:制定规范

6:团队合作最大难度不是技术,而是人

三、高质量的html

1)标签的语义:大家都懂的。

2)标签布局

table布局网页的缺点:1:代码量大,结构混乱。2:标签语义不明确,对搜索引擎不友好

css布局(div+css):弱化了标签的布局能力,将布局完全放到样式中去控制,而标签重新恢复了原来语义的作用。它与table布局相比具有代码量少,结构精简,语义清晰等优点。

总结:在布局的过程中,Html结构才是重点,css是用来修饰结构的,正确的做法是:先确定html,确定语义的标签,再来选用合适的css.

3)如何确定你的标签是否语义良好

在做完一个页面后,去掉样式,看网页结构是否组织良好有序,是否仍然有很好的可读性

"css裸体日"这个日子的目的就是为了提醒大家选用合适的HTML标签的重要性。------------显然我是第一次听说哈。

4)标题和内容的实例

这个实例用的是Html中无语义的标签-----div分隔 和span范围 来架构的, 而正确的做法一般使用h2,p,a来架构

需要特别说明的是:当页面内标签无法满足设计需要时,才会适当添加div和span等无语义的标签来辅助实现。

5)表单和表格

表单

一般需要直接submit的内容需要放置在表单内,为了有清晰的语义,一般表单域要用fieldset标签包起来,并用legend标签说明表单的用途,若不想要它自带的默认样式,可将border:none;不想显示设置 display:none,以此来兼顾语义和设计两方面的需求。

每个input标签对应的说明文本都需要使用label标签,并通过为input设置id属性,在label中设置“for=someId”来将对应的label和input关联起来。

表格

虽然在css布局中table不推荐用来布局,但它在二维数据展示方面确实最好的选择。

一般用table,我常使用它的<table><tr><th><td>标签,在有需要的时候还可以用他的其他标签,表格标题用caption,表头用thead包围,主体部分tbody包围,尾部tfoot包围

总结:语义化标签应注意的一些问题

1:尽可能少地使用无语义标签div和span

2:语义不明显,既可用P也可用div的地方,建议尽量用p,因为有上下间距,可读性好。(还是视情况而定)

3:不要使用纯样式标签,如:b,font,u等 ,直接写进css设置。

四、高质量的css

 

1)怪异模式和标准模式

 

在标准模式中,浏览器根据规范表现页面;而怪异模式通常模拟老式浏览器的行为以防止老站点无法工作。

 

他们两者之间的差异比较典型的表现在IE对盒模型的解析:在标准模式中,网页元素的宽度=padding+border+width;而在怪异模式中,width本身就包括了padding和border。

 

在怪异模式中:设定width,然后margin:0 auto;是无法居中的。标准模式中可以正常工作。

 

于是我们尽量避免怪异模式,而选用标准模式,这样就出现了DTD(文档类型定义Document Type Definition);

 

DTD(文档类型定义Document Type Definition):是一种保证html文档格式正确的有效方法,一个DTD文档包含元素的定义规则、元素间关系的定义规则,元素可使用的属性,可使用的实体或符号规则。

 

DTD声明:如果漏写DTD声明,FF仍然会按照标准模式来解析,但在IE中(6.7.8)就会触发怪异模式,现在一般可以直接手动设置为标准模式

 

 

 

2)如何组织css

 

笔者按照职能分为三大类:base、common、page 这三者不是并列结构,而是层叠结构。

 

1:base层:最底层,基本层,一般放置粒度最小的通用类--原子类,这一层会被所有页面引用,力求精简,通用,有高度可移植性。

 

  推荐代码:.fl{float:left;} .fr{float:right} .mt15{margin-top:15;} .mb15{margin-bottom:15;} .w50{width:50px;}  .cb{clear:both;} .cr{clear:right;}

 

       .pr20{padding-right:20;}

 

  从上面的代码可以看出来,base层结构粒度非常小,非常实用于组合类的结构。

 

base层可分为两大部分:css reset 和 通用原子类

 

css reset:就是重定义标签的样式,覆盖掉浏览器提供的默认样式;如*{margin:0;padding:0;},因为浏览器默认的边距是最影响css布局的。最好不要使用*,而是罗列出要覆盖的标签元素:如:ol li ul h1 dl dt .... 将冲突降至最低。

 

从笔者的经验来看,base层的类虽然粒度小,但它非常有用,设置大量这样的类有利于减少page层的代码量,也为css的模块化提供了帮助

 

2:common层:中间层,高度重用的模块,视为组件(类似于网站内一个一个的同样式的板块)。

 

 模块化层面:将页面内的元素拆分成一小块一小块功能和样式相对独立的小模块。类比于 房子(网站),不同的门窗、门窗有不同的小元件(common),这一层最好由一个人负责统一管理。

 

 common层是网站级的,不同的网站有不同的common层,而同一个网站只有一个common层。

 

3:page层:页面级的样式,最高层。对重用性没要求.(就好像房间里的画,不同房间会挂不同的画。)

 

 

 

3)如何划分css模块

 

一句话概括:封装、多用组合少用继承的原则。

 

将相似部分提取出来,再进一步拆分成更小的模块。

 

原则:

 

1:模块和模块之间尽量不要包含相同的部分,如果有相同部分,应将他们提取出来,拆分成一个独立的模块.

 

2:模块应在保证数量尽可能少的原则下,做到尽可能简单,以提供重用性.

 

 

 

4)css命名

 

1:使用英语

 

2:骆驼命名法(从第二个单词开始首字母大写,如dropMenu,subNavMenu)

 

3:划线命名法(- 或_ 如drop-menu ,sub_nav_menu)

 

使用骆驼命名法和划线命名法可以清楚的将单词区分开来,提高可读性。推荐这两种方式组合使用。一般骆驼命名法用于区别不同单词,划线用于表明从属关系。

 

4:子选择符:滥用子选择符容易留下冲突隐患,为了降低风险,不推荐轻易使用子选择符。

 

总结:base层和common层是公共的,一般由一个人负责,不会出现冲突,page层多人合作使用,容易冲突,在命名css时,如果模块多次反复出现,应该将它归为common层,不用考虑冲突问题,出现次数少,则归为page层。

 

5:page层多人合作很容易引起命名冲突,为解决这一问题,一般每个人会分配一个标识符,然后给命名加上该前缀的方法来解决。

 

 

 

5)多用组合,少用继承

 

继承的思路是将一个复杂且包含变化的类,拆分成几个复杂但稳定的子类。首先明确一个抽象的父类,父类有着几乎所有的方法和属性,子类继承父类,根据需求添加新的方法和属性,覆盖掉与父类有变化的方法和属性。

 

缺点:使用继承的话,任何一点小的变化也需要重新定义一个类,很容易引起类的爆炸式增长,产生一大堆有着细微不同的子类.

 

组合的思路是将一个复杂的类分成容易产生变化的部分和相对稳定的部分,将容易变化的部分拆分出去,每一种可能的变化设计成一个个单独的类,类之间没有继承关系,遵循了面向对象设计的"单一职责"原则.这些容易变化的类的实例赋值给主体类作为一个属性,实现了类的组合.

 

用组合的方式,可大大减少类的数量.
注:margin是一个有点特殊的样式,相邻的margin-left;margin-right不会重合,但是相邻的margin-top;margin-bottom会产生重合,所以相邻的标签不要混合使用,统一使用margin-top或margin-bottom。

 

 

 

6)低权重原则

 

标签权重是 1;class权重是 10;id 权重是100

 

当有冲突时,会选择权重高的样式,权重一样时,就近原则,最后定义的为最近,css权重应该尽可能的低。

 

 

 

7)css sprite---将网站的多张背景图片合并到一张大图片上。

 

  可以解决a标签hover状态时背景图片出现瞬间空白的问题。

 

  每次图片加载会发出http请求,一张图片需要一条http请求,http请求数越多,访问服务器的次数越多,那server压力就越大,css sprite亦可以减小服务器的压力

 

1:只能用于作为背景的图片background-image

 

2:不能用于对于横向和纵向都平铺的图片。至于原因,自己去测试就知道了。

 

 

 

8)haslayout----是IE浏览器一个专有的属性,用于css的解析引擎。有时候网页会在ff下面运行正常,但到IE下就不正常,往往是因为hasLayout没有触发的原因。

 

可以通过设置width,height,position:relative来触发,但一个更好的解决办法是zoom:1可以触发hasLayout

 

 

 

9)块级元素和行内元素(可通过修改display属性来切换块级和行内)

 

块级元素:可设置宽高,margin padding 正常使用,独占一行

 

行内元素:不能设置宽高,margin padding 在水平方向会产生边距效果,竖直方向不会产生边距效果,在同一行内。

 

display:inline-block 行内块级元素;可设置宽高,可设置margin;padding;却不独占一行  (IE6/7不支持,可通过触发haslayout来解决)

 

 

 

10)relative,absolute和float

 

1:只有设置了position属性时,left top right bottom z-index属性才会激活,否则设置了也无效。

 

2:position设置为relative或absolute都可以改变元素在文档流中的位置。网页虽然看起来是二维结构,也是有z轴的,默认所以元素都在z-index:0这一层----这就是文档流

 

3:不设置position,那么网站的默认position:static .

 

4:position设置为relative或absolute都会让元素“浮”起来,也就是z-index>0时,它会改变正常情况下的文档流。

 

     position:relative---会保留自己在z-index:0层的占位,属于占位上浮。

 

     position:absolute---会完全脱离文档流,不保留占位,这样就通常会覆盖掉下面的元素。

 

     他们的left,bottom,top.right四个值是相对于 自己最近设置了position的祖先元素,若都没有设置,则想对于body元素

 

5:float:左右浮动,它也能改变文档流,但是不会上浮,只会在z-index:0层改变正常的文档流排列,影响周围的元素。

 

 

 

11)水平居中的几种方式

 

1:文字、图片等行内元素的水平居中------text-align:center   这个属性能够解决在文字,图片,行内元素的水平居中。

 

2:确定宽度的块级元素水平居中-----margin:0 auto;

 

3:不确定宽度的块级元素水平居中

 

  A:table 标签实现----table本身不是块级元素,不设定宽度的话,它的宽度由内部元素的宽度“撑起”,仅需设置margin:0 auto就可以居中(例:分页<table><tr><td><ul><li>)

 

   缺点:增加了无语义标签,加深了标签的嵌套层数。

 

  B:将要居中的标签转换为行内元素,然后在父标签设置text-align:center;  缺点:因为转换成了行内元素,可能会带来一些限制。

 

      C:通过给父标签设置float,position:relative;left:50%,子标签设置position:relative;left:-50%;来实现

 

12)竖直居中

 

1:父标签不确定,子标签为文本,图片,块级元素的竖直居中------通过给父标签设置相同的上下边距来实现居中padding

 

2:父标签高度确定,单行文本竖直居中----------line-heiht来实现

 

3:父标签高度确定,多行文本,图片,块级元素竖直居中

 

  A:vertical-align属性:当父元素为td或th是,改属性才会生效,后来查证,对inline-block等内联元素都有效。对于块级元素可设置display:table-cell来激活这个属性使之有效

 

  B:通过给父子两层元素分别设置top:50%,top:-50%来实现,也需要设置position:relative;position:absolute;

 

 

 

13)网格布局

 

一个页面经常会分为好几块,每次呈现给用户的最好先是主体内容,这样视觉效果会好些,感觉网速很快。

 

在设计的过程中,需要把主体内容的html标签要保证在其他内容之前加载,(就是将主体内容的html标签顺序靠前)

 

14)z-index

 

该属性在设置position后激发,z-index越大, 元素位置越靠上。也可设置为负数,这样,元素会在body之下。

五、高质量的javascript

1)如何避免JS冲突

A:匿名函数

在多人合作一个网站时,每个人都会写自己的JS代码,定义变量的时候有可能会引起命名冲突,如何避免这种冲突隐患呢?

一种最简单有效的办法是“匿名函数”将脚本包起来,让变量的作用域控制在匿名函数之内

匿名函数:(function (){})() 前面的括号内是函数体,后面的()表示执行。

如:(function(){

  var name,user="test";      //包含在这个匿名函数中的变量,作用域不再是window,而是局限在函数内部。 因为各自包在不同的匿名函数内,也就不再互相冲突了。

  })();

用匿名函数将脚本包起来,可以有效的控制全局变量,避免冲突隐患。

B:解决匿名函数之间的通信问题

上面的匿名函数确实解决了冲突,但是如果两个代码段之间需要访问彼此的变量,那就被分隔开了,没法访问对方作用域中的变量.

一个比较好的解决办法是"在window的作用域下定义一个全局变量",但是从上面的冲突来看,全局变量是引起冲突的杀手,如果又这样定义,就违背了我们使用匿名函数的初衷,所以应该严格控制全局变量的数量!

为了控制全局变量的数量,用Hash对象作为全局变量。  var GLOBAl={};  //一个对象类型的变量作为全局变量,扩展性好

定义好对象类型变量后,在匿名函数A中定义GLOBAL的属性:GLOBAL.str1="aaa";   在匿名函数B中可以直接访问var b = BLOBAl.str1;

这样又出现了一个问题,当在匿名函数B中它也定义一个属性BLOBAl.str1="bbb"; 这个时候就会把A块中的属性str1给覆盖掉.如何避免这种冲突呢?不可能每个开发者在使用GLOBAL对象之前,都要查找一下绑定了哪些属性。

这时,命名空间就出现了,它是一种特殊的前缀,在js中它其实是通过一个{}对象来实现的。我们可以给每个匿名函数声明不同的命名空间,然后每个匿名函数中GLOBAL对象的属性不要直接挂在GLOBAl对象上,而是挂在此匿名函数的命名空间下,既:window全局的GLOBAL.命名空间.属性变量  ,这样申明属性名称的时候,即使同名,空间不一样也不会引起冲突。如: GLOBAL.A={};// 定义命名空间; GLOBAL.A.str1="aaa";//定义属性变量

复杂的匿名函数中,你还可以生产二级命名空间,如GLOBAL.A={};//一级命名空间,GLOBAL.A.CAT={};GLOBAL.A.DOG={};//二级命名空间;

生成命名空间是一个很常用的功能,可以将其封装为一个函数。

var GLOBAL={};

GLOBAL.namespace=function(str){

  var arr=str.split("."),o=GLOBAL;

  for(i=(arr[0]=="GLOBAL")?1:0; i<arr.length; i++){

    o[arr[i]]=o[arr[i]] || {};

    o=o[arr[i]];

  }

}    

调用: GLOBAL.namespace(A.DOG);      GLOBAL.namespace(GLOBAL.B);

总结:解决js冲突-------命名空间+全局变量+匿名函数 很好的结合使用才能更好的解决冲突。

C:注释

添加必要的代码注释,可大大提高可维护性,对团队合作来说,是很重要的。

注释添加的信息包括:功能说明;工程师姓名;工程师联系方式;代码最后修改时间;

让JS不产生冲突,需要避免全局变量的泛滥,合理使用命名空间,以及给代码添加注释。

 

2)JS代码程序统一入口---------window.onload和DOMready

JS从功能上分为两部分:1 框架部分(提供的是对JS代码的组织作用,包括定义全局变量,命名空间等,和具体的应用无关,该部分被每个页面都包括的)---base层,common层;

                               2 应用部分(页面功能逻辑的代码段,不同页面有不同的功能,不同页面应用部分的代码也不同)---page层;

而应用部分的代码最好包在一个约定好的入口函数里,(程序统一入口,是针对js应用部分来说的),一般初始化的部分会放在一个统一的函数function init(){}中,然后在页面完全加载完毕后触发;

window.onload:需要在页面完全加载完成时才会触发,包括图片,flash等富媒体,

DOMReady:只需要判断页面内所有的DOM节点是否已经完成加载,至于节点的内容加载是否完成,他并不关心。

所以DOMReady触发的速度比window.onload快,它比window.onload更适合用来调用初始化函数。【我一般使用jquery,所以页面加载完毕会用$(fuction(){...});或$(document).ready(function(){...});】

另外一种模拟Ready的效果是将初始化的js代码块 放在body结束标签的前面js ...</body>,这样会按照顺序来载入。

 

3)CSS放在页头,JS放在页尾

浏览器加载网页,加载到JS时,由于脚本比较多,而html代码还没有加载,这是页面会显示空白,脚本阻塞了html的加载,等到毫不容易加载完成了,有时候会发现这些网页元素没有样式,所以一个好的习惯是,CSS放在页头,JS放在页尾(先加载css,再加载html,再加载js.) 这样就能适时的将界面呈现给用户,减少页面空白的时间。

 

4)引入编译的概念---------文件压缩

JS压缩通常的做法是去掉空格和换行,去掉注释,将复杂变量名替换为简单的变量名;压缩后的js文件的确变小了,但是压缩后的文件无法反压缩恢复成原来的模样。

压缩命名规则:原名.js    压缩后的名为:原名-min.js

 

5)js 如何分层----------(为了使代码更清晰,减少冗余,提高代码重用率)

和css分层一样:base层:最低端,两个职责:1:不同浏览器之间js的差异提供统一接口;2:扩展js语言底层提供的接口,比如tostring...  

            base层是给common层和page层提供接口的。

        common层:提供可复用的组件,和页面内具体功能无关。common层的功能是给page层用的;

        page层:最顶端,该层主要是完成各个页面内的功能需求。

A:base层

琐碎知识点:

1:IE和FF下获取childNodes会不一样,因为FF会将包括空白、换行等文本信息业当做childNodes中的一员,而IE会忽略它,只将DOM节点作为childNodes的一员。

2:document.all是IE支持的属性,FF 不支持,通常也可以用来判断浏览器的种类。

3:nextSibling:获取某个节点的下一个同级节点。

获得某个父节点下面的子节点,然而浏览器不一样,为了兼容,会根据浏览器做出一些处理,将处理封装成函数,像这类函数将放到base层。如:getnextnode(node)

4:透明度:IE通过滤镜实现,FF通过css的opacity属性实现。node.style.filter='alpha(opacity='3')'; nodel.style.opacity=0.3;

5:event:  IE中event对象是window的属性,作用于全局作用域,在FF中,event是作为事件的参数存在。

一般 function(e){e=window.event||e;}//这样e在IE和FF下都指向event对象。

event对象的部分属性名在IE和FF下也是不同的,比如"触发事件的对象(标签)"在IE下通过srcElement属性访问,在FF下通过target访问

var element= e.srcElement ||e.target;

6: 冒泡:两个重叠的标签点击其中一个时,另外一个也会被触发点击事件,JS 将这种先触发子容器监听事件,再触发父容器监听事件的现象称为冒泡。

    为了业务需要,我们需要阻止事件冒泡,在IE中通过设置event对象的cancelBubble属性为true实现,在FF下通过调用Event对象的stopPropagation方法实现的。

if(document.all){

   e.cancelBubble=true;//IE

}else{

  e.stopPropagation();//FF

}

7:on、attachEvent和addEventListener

我们在定义事件时,往往on**只能定义一次,如果再次定义,就会覆盖前面定义好的方法。最常见的为onclick事件。

on***的监听方式没有叠加效果,最后定义的on***会将前面的事件覆盖掉。

attachEvent(IE支持的方法)、addEventListener(FF支持的方法),他们支持监听处理函数的叠加。

例:btn.attachEvent("onclick",function(){...});     btn.addEventListener("click",function(){...});

总结:以上1-7点中,为了兼容不同浏览器而封装的一些兼容处理函数,像这类函数将放到base层。如:getnextnode(node)

8:扩展Js语言底层的接口

例:trim() isNumber() isString() get() $() extend()等等 

base层的JS和页面里的具体应用逻辑无关,属于框架级的。

B:common层

common层本身依赖于base层提供的接口,需要先加载base层代码。它与base层的区别是:common层不是简单的接口,而是相对庞大的组件。(如:设置和获取cookie,就可以封装cookie组件). 它和具体功能相关,如果页面里不需要相关功能,就没必要加载,而一个易用性和可扩展性都好的组件,代码量会偏高,所以一般common层的js要按功能分成一个个单独的文件,如common_cookie.js,common_drag.js,common_tab.js.

common层的JS和页面里的具体应用逻辑无关,属于框架级的。

C:page层

base层和common层都是属于框架级的,page层是属于应用级的,它可以调用base层的接口和common层的组件 

 

6) js类库

常见的有prototype. Dojo, Mootools,Extjs,jquery,yui等。项目中用的最多的是jquery库,要好好深入研究。

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规范。

原文地址:http://www.cnblogs.com/Joans/archive/2012/09/14/2685110.html

 

posted @ 2015-10-10 16:43  雪莉06  阅读(565)  评论(0编辑  收藏  举报