《Javascript高级程序设计》学习笔记

第三章:基本概念

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.createElementgetElementById等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

初始值必须放到