第三章:基本概念
typeof(对象 or null),返回object
typeof(函数),返回function,虽然function也算是一种对象
Boolean():任何对象都会返回true,即使是空对象var o = new Object();
永远不要测试某个浮点数值,因为精度的原因。
null和underfined没有toString()方法。
同时使用两个逻辑非操作符,实际上就会模拟Boolean()转型函数。
经常利用逻辑或来避免变量赋值Null或者undefined。
var o = "23" < "3";//true
比较字符串的时候千万注意。
任何值与NaN比较都会返回false
使用===
不会进行隐式类型转换,为了保持代码中数据的完整性可以考虑这个。
for-in语句:枚举对象的属性:
但是属性名的顺序是不可预测的,当变量值为Null或者underfined的时候还会抛出错误,因此使用之前先检测对象的指是不是Null或者underfined。
switch语句使用的是全等操作符,因此不会发生类型转换。
第四章:变量,作用域和内存问题
因为基本类型的大小固定,所以存储在栈中。复制的时候前后两者独立。
因为引用类型的大小不固定,所以存储在堆中。复制的时候前后两者指向同一个地址,不相互独立。
检测基本类型用typeof,检测引用类型用instanceof。
执行环境只有全局执行环境和函数执行环境,标识符按照作用域链解析,没有块级作用域。
不用的全局对象及时设置为Null清除。
第五章:引用类型
可以通过对象向函数传入大量的可选参数:
function displayInfo(argus){
var output = "";//说明输出是字符串
if((typeof argus.name) == "string" ){
output += "Name" + ":" + argus.name + "\n";
}
if((typeof argus.age) == "number" ){
output += "Age" + ":" + argus.age + "\n";
}
alert(output);
}//函数声明不加分号
displayInfo({
name : "gui-heng",
age : 23
});
数组的length属性不是只读的。通过设置这个属性,可以在数组末尾移除项或者添加项。
函数名实际上是指向函数对象的指针。
解析器会率先读取函数声明,使其在执行任何代码之前可用。
可以像传递参数一样传递函数给另一个函数:
function callSomeFunction(someFunction,someArgument){
return someFunction(someArgument);
}//无论传递进来的是什么函数,都会返回执行第一个参数后的结果
function add10(num){
return num + 10;
}
var result = callSomeFunction(add10,20);
alert(result);//30
也可以从一个函数中返回另一个函数
arguments对象有一个callee属性,指向拥有这个arguments对象的函数。
函数的apply()和call()方法可以扩充函数赖以生存的作用域。(在指定的作用域中调用函数)
第八章:BOM
BOM提供了用JS操作浏览器的能力。
尝试访问未声明的变量会抛出错误,但是通过查询windows对象,可以知道某个可能未声明的变量是否存在:
alert(window.s);//underfined
alert(s);//报错
窗口位置:很难跨浏览器取得窗口的精确位置。
打开窗口:
window.open("http://www.baidu.com", "_blank");//URL不要忘记加协议,不然默认是file
定时器:一般不用间歇调用,因为可能后一个间歇可能在前一个间歇结束之前调用,可以I改为用超时调用模拟。:
var num = 0;
var max = 10;
function foo(){
num++;
if(num < max){
setTimeout(foo,500);
}else{
alert("Done");
}
}
setTimeout(foo,500);
location指明当前网页地址:
var a = window.location;
alert(a);
navigator对象中的userAgnet指明当前浏览器的版本信息:
var a = window.navigator.userAgent;
注意scrollTop有兼容性问题,这样处理:
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
var scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft;
第九章:客户端检测
不到万不得已,不要使用。先设计最通用的方案,然后在使用根据特定的浏览器增强的方案。
1.能力检测(最常用):
不必顾及浏览器具体的版本和平台,只要确定浏览器支持的能力。
注意:一是要先检测达成目的最常用的特性;二是要必须测试实际用到的特性(不能因为a存在,就确定b存在)。
可靠的能力检测,最好检测某个对象是不是一个函数:
typeof window.document.getElementById == "function"
千万啊注意IE8及其之前的DOM方法。DOM对象是宿主对象,IE8及其更早的版本中的宿主对象是通过COM而非JScript实现的。因此像document.createElement
和getElementById
等DOM方法会返回object,IE9+才返回function。
第十章:DOM
DOM提供了让JS操作浏览器的能力;
注意IE中的所有DOM对象都是以COM对象的形式实现的,这意味着IE中的DOM对象与原生JS对象不一致,特别注意。
1.Node类型:
所有节点类型君继承自Node类型。
检测节点类型:nodeType:
alert(document.documentElement.nodeType == 1);
对于getElementById,IE7及其较低版本中,name特性与给定ID匹配的表单元素也会被返回,因此最好的办法是不让表单字段的name特性与其他元素的ID相同。
2.Element类型:
访问标签名:
alert(oDiv.tagName.toLowerCase() == "div");//toLowerCase()转为小写比较
修改className,相应的CSS样式在页面中改变。
取得特性:用"."访问,不要用getAttribute访问:
var oDiv = document.getElementById("div1");
alert(oDiv.className);
设置特性:还是用".":
var oDiv = document.getElementById("div1");
oDiv.className = "www";
alert(oDiv.className);// www
添加节点:
var images = document.createElement("img");
images.src = "images/1.jpg";
document.body.appendChild(images);
遍历子节点:
千万注意浏览器的差异,在执行操作之前最好先检查nodeType:
var oElement = document.documentElement.childNodes;
var num = 0;
for (var i = 0; i < oElement.length; i++) {
if (oElement[i].nodeType == 1 ) {
num++;
}
}
3.动态脚本(插入外部文件的方法)
var oBtn1 = document.getElementById("btn1");
function loadScript(url){//动态插入脚本函数
var script = document.createElement("script");
script.type = "text/javascript";
script.src = url;
document.body.appendChild(script);
}
oBtn1.onclick = function(){
loadScript("hah.js");
}
4.动态样式(插入外部文件的方法)
var oBtn1 = document.getElementById("btn1");
function loadStyles(url){//动态插入样式函数
var link = document.createElement("link");
link.rel = "stylesheet";
link.type = "text/css";
link.href = url;
var head = document.getElementsByTagName("head")[0];
head.appendChild(link);//必须加到head中而不是body中
}
oBtn1.onclick = function(){
loadStyles("style.css");
}
第十三章:事件
事件是JS与HTML交互的桥梁。
几乎所有浏览器都实现了"DOM2级事件"的核心部分,IE8是最后一个仍然使用其专有事件系统的主要浏览器。
1.事件流
事件冒泡:从最具体的事件开始接收,逐级向上传,直到Document对象。
事件捕获:与冒泡相反,很少用。
2.事件处理程序
(1).HTML事件处理程序(用的少)
事件处理程序的代码在执行时,有权访问全局作用域中的任何代码
(2).DOM0级事件处理程序
btn.onclick = function(){
// ...
}
这时候的事件处理程序在元素(btn)的作用域中运行。
以这种方式添加的事件处理程序在事件流的冒泡阶段被处理。
删除事件:
btn.onclick = null;
(3).DOM2级事件处理程序
注意事件的名字前面没有on。
addEventListener添加的事件必须用removeEventListener删除
添加的函数不要是匿名函数,因为删除的时候没法删。
最大的好处是支持对一个对象添加多个事件,事件会按照他们的顺序被触发。
function changeColor(){
oDiv1.style.background = "green";
}
oBtn1.addEventListener("click", changeColor, false);//添加事件,false指明事件在冒泡阶段被触发
oBtn1.removeEventListener("click", changeColor, false);//删除事件,
(3).IE事件处理程序
事件的名字前面有on。
添加多个事件的时候,事件按照他们相反的顺序被触发。
切记事件的作用域是全局作用域,因此this等于window,因此编写跨浏览器代码的时候特别注意。
oBtn1.attachEvent("onclick", changeColor);//添加事件
oBtn1.detachEvent("onclick", changeColor);//删除事件,
(4).跨浏览器的事件处理程序
var EventUtil = {
addHandler : function(element, type, handler){
if(typeof element.addEventListener == "function"){//DOM2级
element.addEventListener(type, handler, false);
}else if(typeof element.attachEvent == "function"){//IE
element.attachEvent('on' + type, handler);
}else{// DOM0,在现代浏览器中,应该不会执行这里的代码
element['on' + type] = handler;
}
},
removeHandler : function(element, type, handler){
if(typeof element.removeEventListener == "function"){//DOM2级
element.removeEventListener(type, handler, false);
}else if(typeof element.detachEvent == "function"){//IE
element.detachEvent('on' + type, handler);
}else{// DOM0,在现代浏览器中,应该不会执行这里的代码
element['on' + type] = null;
}
}
};
EventUtil.addHandler(oBtn1, "click", changeColor);
3.事件对象
触发DOM上的某个元素,会产生一个事件对象Event,这个对象包含着所有与事件有关的信息,浏览器的支持情况不同。
全兼容的事件对象:
var EventUtil = {
addHandler : function(element, type, handler){
//...
},
removeHandler : function(element, type, handler){
//...
},
getEvent : function(event){
return event ? event : window.event;
},
preventDefault : function(event){
if(event.preventDefault){
event.preventDefault();
}else{
event.returnValue = false;
}
},
stopPropagation : function(event){
if(event.stopPropagation){
event.stopPropagation();
}else{
event.cancelBubble = true;
}
}
};
4.事件类型
包括IE9在内的主流浏览器都支持DOM2级事件,IE9页支持DOM3级事件。
(1):UI事件
- load
页面完全加载完后在window上面触发。
图像加载完毕之后也会触发。 - resize
当页面大小发生变化时在window上面触发。
FF只有用户停止调整窗口大小的时候才会触发resize事件,除此之外的浏览器每发生1PX的变化就触发resize事件,因此次事件中不要放大量计算的代码。
(2):焦点事件
- focus
元素获得焦点时触发,不冒泡。 - blur
元素失去焦点时触发,不冒泡。
(3):鼠标与滚轮事件
下面所有事件都会冒泡
- mousedown
按下任意鼠标按钮时触发。 - mouseup
释放鼠标按钮时触发。 - click
点击(mousedown+mouseup)鼠标左边按钮之后触发。 - dbclick
双击(click+click)鼠标左边按钮之后触发。 - mouseover
从元素外部移入元素内部之后触发。 - mouseout
从元素内部移出触发。
鼠标按钮:对于mousedown和mouseup来说,其evenet对象存在一个button属性。
- 0:鼠标主按钮;
- 1:中间鼠标按钮;
- 2:次鼠标按钮;
IE模型与DOM的event对象不同,因此需要函数映射为一致:
getButten : function(event){
if(document.implementation.hasFeature("mouseEvent", "2.0")){
return event.button;
}else{
switch(event.button){
case 0:
case 1:
case 3:
case 5:
case 7:
return 0;
case 2:
case 6:
return 2;
case 4:
return 1;
}
}
}
鼠标滚轮事件代码:
getWheelDelta : function(event){
if(event.wheelDelta){//非FF
return (client.engine.opera && client.engine.opera<9.5 ?
-event.wheelDelta : event.wheelDelta);// 如果opeara的版本低于9.5,那么把正负号反向
}else{//FF
return -event.detail * 40;
}
}
注意FF与其他浏览器对应的事件不同,FF会触发DOMMouseScroll事件,最终会冒泡到window对象。
其他浏览器会触发mousewheel,会冒泡到window对象,只有IE8冒泡到document对象。
调用代码:
EventUtil.addHandler(document, "mousewheel", handleMouseWheel);//非FF
EventUtil.addHandler(document, "DOMMouseScroll", handleMouseWheel);//FF
(3):键盘与文本事件
三个键盘事件(支持任何元素,但只有用户通过文本框输入文本时最常用到):
- keydown:按下任意键触发,不放手会连续触发;
- keyup:释放键盘上的键时触发;
- keypress:按下字符键和Esc键触发;
文本事件:
- textInput:文本插入文本框之前触发。而keyup是插入文本之后触发的。只有在可编辑区域输入实际字符**才会触发。而keypress按下能够影响文本显示的键时也会触发,比如退格键。有data属性,值是用户输入的实际字符串(不是字符串编码)。注意这个属性的兼容性不好,FF不支持。
键码:在按下keydown和keyup时触发。
特殊情况:分号键在IE,safari,chrome中为186,在FF和opera为59.
5.内存和性能
添加到页面上的处理程序越多,越占用内存,页面也会越卡。使用时间委托可以解决页面处理程序过多的问题。
事件委托利用了事件冒泡,只需要指定一个事件处理程序就可以管理一类所有的事件。只需要在DOM树中尽量最高的层次上添加一个事件处理程序。
mouseover和mouseout也冒泡,但是不适合用事件委托,因为经常需要计算元素的位置。
移除事件处理程序:
内存中保留的过时不用的"空事件处理程序"程序是造成Web应用程序内存与性能问题的主要原因。
很多时候页面某些部分被innerHTML替换或者纯粹DOM操作,那么原来添加到元素中的事件处理程序很可能无法被当作垃圾回收。
两种办法解决:
- innerHTML替换某个元素的时候先手动移除事件处理程序:
btn.onclick = null;
- 通过事件委托,指定较高层次的元素,同样能够处理该区域的事件。
第十四章:表单脚本
禁止重复提交表单:
- 第一次提交后禁用提交按钮;
- 利用onsubmite事件处理程序取消后续的提交操作;
重置表单(reset):重置的时候会触发submite事件,利用你这个机会可以必要时取消重置。
1.表单字段
每个表单都有elements(不是element)属性,他是一个有序列表,包含表单中的所有字段,可以按照name属性和
位置来访问。
(1).共有的表单字段属性
- disabled:当前字段是否被禁用;
- form:指向当前字段所属表单的指针,只读;
- name:字段名称;
- readonly:当前字段是否只读;
- type:字段类型;
- value:当前字段将被提交给服务器的值;
除了form之外都可以动态修改,所以可以动态的操作表单。比如侦听submit事件来防止用户重复提交。
`var form = document.getElementById("myForm"); EventUtil.addHandler(form, "submit", function(event){ var event = EventUtil.getEvent(event); var target = EventUtil.getTarget(event);//target指事件的实际目标,这里的target为整个表单 var btn = target.elements["submitBtn"]; btn.disabled = true; });
注意不能通过click事件侦听,因为浏览器的click和submite事件的顺序不一致。
(2).共有的表单字段方法
- focus():激活表单字段,可以相应键盘事件。
例如,在页面加载完毕后在表单的第一个字段上调用focus()方法:
var form = document.getElementById("myForm");
EventUtil.addHandler(window, "load", function(event){
form.elements[0].focus();
});
- blur():移除焦点
(3).共有的表单字段事件
- blur
- focus
- change:对于input和textarea元素,在失去焦点且value改变的时候触发。对于select,在其选项改变的时候触发。
通常用前俩事件以某种方式改变用户界面,像用户发出提示。change一般用于验证用户字段输入。
注意blur和change发生的顺序不能确定。
2.文本框脚本
(1):input文本框
size:显示字符数
value:文本框初始值
maxlength:可接受的最大字符数
input type="text" value="firstName" size="10" maxlength="8" />
(2):textarea
初始值必须放到与
之间。
(3):选择文本
- 选择全部文本,select()方法只要文本框获得焦点就会选择所有文本。
EventUtil.addHandler(firstNameInput, "focus", function(event){
event = EventUtil.getEvent(event);
firstNameInput.select();
});
选择文本框中的文本会触发select事件。
第二十一章:Ajax与Comet
Ajax可以向服务器请求额外的数据而无需卸载页面。核心是XHR对象。通过XHR获取新数据,然后通过DOM将数据插入到页面中。
1.XHR使用
var xhr = new XMLHttpRequest();
//启动一个请求已备发送,最后的参数是是否异步发送的布尔值
xhr.open("get", "example.php", false);
//请求被分配到服务器,因为请求的同步的,
//所以JS代码会等到服务器响应之后再执行,
//收到响应后,数据会自动填充XHR对象的属性。
xhr.send(null);
if(xhr.status == 200){
alert(xhr.responseText);
}