JavaScript高级程序设计整体概述


第1章 JS简介
DOM1 组成:DOM Core 与 DOM HTML
DOM1 的目标主要是映射文档的结构,扩展支持XML命名空间
DOM2 扩充了鼠标和用户界面时间,范围,遍历,对CSS支持
DOM3 统一加载和保存文档方法,新增验证文档方法

第2章 在页面中使用JS
"<" 符号在HTML里正常,在XHTML里会被解析为标签,一定要用就用<![CDATA[ ]>
如果页面不兼容XHTML那么
//<![CDATA[

//]>

第3章 基本概念
不用把一个变量显式的设置为undefined,但是变量要保存空对象,就一定要设置为null
这样做不仅可以体现null作为空推向指针的惯例,而且可以区分null和undefined

n/a not application 不适用

0.1+0.2=0.30000000000000004 ,二进制的问题,就像十进制无法显示3/10

parseInt("123blue") => 123

后置递增或递减实在包含他们的语句被求值之后才执行的

一元操作符的规则与Number()类似

值类型:变量——值
引用类型:变量——地址——值

第4章 变量,作用域和内存
执行环境(执行上下文),变量对象
一旦数据不用,可设置为NULL,降低内存

第5章 引用类型
对象是某个特定引用类型的实例(解释了字面量?)
栈:先进先出 队列:先进后出
正则:出现多个“\”双重转义,是因为在 new RexExp()里面使用,避免字符串的引号
reg.exec(xxx);返回第一个匹配项数组,结果拥有INDEX与INPUT属性,如果是全局,lastIndex会变化
搜索结果是全局搜索
test(),只返回TRUE或者FALSE;
正则构造函数包含了一些属性,用两种名字(input-$)长属性名要加方括号(参见P108)
多达9个捕获组,RegExp.$1,RegExp.$2,调用exec()或test()时,这些属性会自动填充
var text = "this has been a short summer"
var pattern = /(..)or(.)/g
if(pattern.text(text)){
    alert(RegExp.$1) //sh
    alert(RegExp.$2) //t
}

Object构造函数会想工厂方法一样,根据传入值的类型返回相应的实力
var obj = new Object("new obj")
obj instanceof String //true

typeof Number(value)//number
typeof new Number(value)//object

valueof () //返回适当的类型
toString()//返回字符串

字符串:slice() substring() substr()
在负值的时候,不一样
slice,直接弄
substring,会把第一个转为负值,第二个为0
substr,会把所有复制都转为0

encodeURI()主要用于整个URI,不会对本身属于URI的特殊字符串编码
encodeURIComponent()用于对URI某一段,会对发现的任何非标准字符编码(用的多)
前者能够编码Unicode字符,而escape只能编码ASCII字符

通过eval执行的代码可以引用当前环境的变量,而 new Function()却永远是全局的
在严格模式下,外部不能访问eval内的变量

Global对象属性表(P133)
ECMAScript虽然没有指出如何直接访问Global对象,但游览器都是将这个对象作为window对象的一部分实现的
window对象还承担了很多别的任务

第6章 面向对象的程序设计
关于对象特性,用来描述属性:
数据属性:[[Configurable][[Enumerable][[Writable][[Value]
访问器属性:[[Configurable][[Enumerable][[Get][[Set]

关于创建对象:
工厂模式:解决了创建多个相似对象的问题,未解决对象识别的问题(怎样知道一个对象的类型)
构造函数模式:问题是每个方法都要在每个实例上重新创建一遍
原型模式:问题是其共享的本性所致
混合使用原型和构造函数:可行
动态原型模式:检测是否存在属性,不存在动态原型创建
寄生构造函数模式:感觉没什么用
稳妥构造函数模式:老道出品,比较安全,可以演变用作继承,除了调用sayName外没有方法可以访问到其中的元素(这不就是闭包么?!)
function Person(){
    var o = new Object();
    o.sayName= function(){
    
    }
    return o
}

new 实际的步骤:
1 创建一个对象
2 将构造函数的作用域赋给新对象(this)
3 执行构造函数中的代码(为这个新对象添加属性)
4 返回对象
(应该还要加一个赋原型吧,个人理解)

重写原型会切断现有原型与任何之前已经存在的对象实例之间的联系,它的引用仍然是最初的原型
构造函数 Person 实例 friend
friend.[[Prototype] 指向了 Person.prototype 过程就是对象赋值,
当Person.prototype重写时,friend.[[Prototype]内的地址依旧是原来内存中的对象
变量中的地址是指向的是内存中的对象,而非Person.prototype
这就是 变量——地址——值 的真谛!
所以,一旦一个变量赋值给另一个变量,那么:
如果是值类型:原先对象的改变不影响
如果是引用类型:1)原先对象直接改变了地址,那不影响,2)原先对象通过地址改变了内存中的值,有影响。
所以,创建一个引用类型的过程是:
先在内存中创建一个真正的值,然后把地址赋给变量,变量里拥有的知识地址,充分理解了变量的变化O YEAH~

关于继承
所有的对象最终的原型指向
Object.prototype:
constructor
hasOwnProperty
isPrototypeOf
propertyIsEnumerable
toLocalString
toString
valueOf

原型链继承的2个问题:
1 引用类型的更改会影响
2 不能向超类的构造函数中传递参数

借用构造函数:解决引用和参数问题
function SuperType(name){
    this.color=["a","b","c"];
    this.name = name;
}
//SubType.prototype = new SuperType()(原先是这样的)
替换为:
function SubType(){
    SuperType.call(this,"king")//还可以传参
}

var instance = new SubType()
这样,解决了color属性共享的问题,说白了就是用SuperType在构造
但是:函数无法复用,超类中定义的方法对子类不可见,改进如下

组合继承
1 用原型链实现对原型属性的方法继承,用构造函数来实现对实例属性的继承
2 问题是调用了两次构造函数,而且会导致属性重叠
instanceof isPrototypeOf() 可以识别
//继承属性
function SubType(){
    SuperType.call(this,argument)
}
//继承方法
SubType.prototype = new SuperType()


原型式继承:出自老道
1 适用于没有必要兴师动众创建构造函数,只想让一个对象与另一个对象保持类似的情况
2 浅复制,还是引用共享值的问题
function Object(obj){
    var F = function(){};
    F.prototype = obj;
    return new F()
}

寄生组合式继承
1 无非是要超类的原型的一个副本,不用直接new 超类
2 值调用一次构造函数,原型链还能不变
function inheritPrototype(subType,superType){
    var prototype = Object(superType);//创建
    prototype.constructor = subType;//增强
    subType.prototype = prototype;//指定
}
function subType(){
    superType.call(this,argument)
}
inheritPrototype(subType,superType)

第7章 函数表达式
匿名函数又叫拉达姆函数

内存泄漏:
1 闭包中如果包含HTML元素,将会无法垃圾回收
2 光提取还不够,闭包会追溯变量,所以最后要删除掉element
function assignHandler(){
    var element = document.getElementById("element");
    var id = element.id//赋值
    element.onclick = function(){
        alert(id)
    }
    element = null//删除
}

模块模式:
一般写JS的手段?布局?
var module= function(){
    var ss //
    ss.a = function(){}
    ss.b = function(){}
    return ss
}()

第8章 BOM
元素的集合
window.frames
document.all
document.images
document.links
document.forms
document.anchors
获取框架 用top.frames而非window.frames,保证是最外层
除非最高层窗口通过window.open()打开 不然name没有值
在使用框架情况下,会有多个Global,会包含多个原生的构造函数
比如:top.Object != top.frame[0].Object,这个会影响到跨框架传递对象使用instanceof

var leftPost = (typeof window.screenLeft =="number")?
                window.screenLeft:window.screenX
这种模式可用于游览器的一般兼容写法

window.open(URL,name,feature,replace)
window.close()只能关闭window.open打开的窗口

一般都用setTimeOut,很少用setInterval,可以通过递归模拟
window.location document.location是一个东西

插件检测:navigator.plugins[i].name | new ActiveXObject(name) //ie
history.go()//-1

第9章 客户端检测
一般检测都是通过 typeof xxx == "function"
IE中宿主对象是通过COM而非JScript实现的,所以typeof document.createElement == "Object"
IE不能typeof ActiveXObject 会显示“unknown”
function isHostMethod(object,property){
    var t = typeof object[property];
    return t=="function"||(!!t=="object"&&object[property])||t=="unknown";
}
其实没必要一定要检测出是什么游览器,只要检测这个游览器拥有可以执行代码的功能即可
var hasNSPlugins = !!(navigator.plugins&&navigator.plugin.lenth)
用户代理检测(navigator.userAgent),最后的选择,发展历史(P222)

var support = function(){
    
    return{
    //所有支持的值
    }
}()

document.implementation.hasFeature

第10章 DOM
所有的节点都继承node的属性
对于元素节点,nodeName始终表示元素的标签名,nodeValue的值始终为null

nodeList是一个动态的查询结果,而不是第一次访问他们的某个瞬间拍下来的快照
nodeList转数组
function convertToArray(nodes){
    var array = null;
    try{
        array = Array.prototype.slice.call(nodes,0)
    }catch(e){
        //IE8以下nodeList是COM对象,所以只能手动枚举
        array = new Array();
        //i<length 或 i<=length-1
        for(var i=0,len = nodes.length;i<len;i++){
            array.push(nodes[i])
        }
    }
    return array
}

节点关系
childNodes() parentNode() nextSibling() previousSibling()
hasChildNodes()
owerDocument 返回节点所在文档,任何节点只能在一个文档里

操作节点
appendChild() insertBefore(newNode,beforeNode||last) replaceChild(newNode,oldNode) removeChild()
cloneNode()不能复制JS属性比如:事件,但IE会,所以要先移除

document是HTMLDocument的一个实例
document.documentElement 指向HTML标签 document.body 指向BODY标签

document.referrer 表示是从哪个页面调过来的,信息存于HTTP请求头部
document.domian 这三个中只有这个可以改,但只能改为所在的域,不能跨域,两个域改成一样可以通信,
                域名如果是松散的,不能改为紧绷
document.URL

查找元素
getElementById()在IE8以下不分大小写,name与ID一样的表单元素也会被找到
getElementByTagName 返回一个HTMLCollection 与nodeList很相似
HTMLCollection.nameItem('name') == HTMLCollection['name']

HTMLElement
所有HTML元素由HTMLElement来表示,继承自Element,添加了一些属性:id title lang dir className
HTML元素不是自定义的特性都可以通过点来访问:div.align

元素特性的名称不区分大小写,自定义特性以data-开头
有两种特殊的特性:style 和 事件处理程序(onclik),用getAttribute只返回字符串,一般不用
内建的叫特性,属性包含特性
直接给属性赋值可以设置特性的值 div.id=“aaa”,但是自定义的属性不行div.mycolor=“red”
ie6及之前,不支持removeAttribute

element.attributes ,只有element有这属性,一般都用setAttribute,遍历的时候可以用

在IE7及更早中,createElement支持html代码模式,用来规避一些创建表单的问题

元素的子节点
在非IE游览器中,子节点会多出文本节点,要通过遍历去识别type为1的节点,或者直接getElementsByTagName

文本节点
可以通过nodevalue或者data属性获得文本,还有appendData deleteData insertData等属性
normalize()用来处理两个文本节点相邻导致混乱的情况,合并成一个文本节点
splitText()把一个文本节点分隔成两个,参数为分隔位置

DOM操作技术之动态脚本
1插入文件的形式 已经说过了
2直接插入代码(实际和eval是一样的)
function loadScriptString(code){
    var script = document.createElement("script");
    script.type="text/javascript";
    try{
        script.appendChild(document.createTextNode(code));
    }catch(e){
        secrpt.text = code //for ie IE视script为特殊对象不能进行dom操作
    }
    document.appendChild(script)
}

DOM操作技术之动态样式
1插入文件形式 同上,不过一定要插在HEAD里
2直接插入代码
function loadStyleString(code){
    var style = document.createElement("style");
        style.type="text/css"
    try{
        style.appeldChild(document.createTextNode(code));
    }catch(e){
        style.stylesheet.cssText =code;//for ie
    }
    document.getElementsByTags("head")[0].appendChild(style)
}

DOM操作技术之表格
var table = document.createElement("table");
table.border=0;
table.width="100%"
var tbody = document.createElement("tbody");
table.appendChild(tbody)
tbody.insertRow(0)
tbody.rows[0].insertCell(0)
tbody.rows[0].cells[0].appendChild(document.createTextNode("xxx"))

NodeList NamedNodeMap(attributes) HTMLCollection ,是从整体透彻理解DOM关键所在

第11章 DOM扩展
兼容模式 document.compatMode 兼容:CSS1Compat,混杂:BackCompat

script style 等元素在IE里是无作用域的元素,innerHTML之前要加一个空白符之类的东西

document.documentMode IE8以上的文档模型

判断某个节点是不是另一个节点的后代
refNode.contains(otherNode)//for ie
refNode.compareDocumentPosition(other)&16//DOM 3
实在不行就遍历
do{
    if(node=== refNode){
        return true
    }else{
        node = node.parentNode
    }
}while(node != null)

获取文本
innerText textContent

第12章 DOM2 和 DOM3
DOM2 DOM3 的关键是源于命名空间 xmlns (xml name space)
不能用关键字float cssFloat styleFloat//for ie

style 是CSSStyleDeclaration的实例
style.cssText
计算样式(都是只读)(没有返回border,因为是综合属性)
document.defaultView.getComputedStyle(ele,hover)
ele.currentStyle // for ie

offset 偏移量:元素在屏幕上所有可见区域,包括:内边距 滚动条 边框(p321)
获取元素在页面的偏移量
function getElementLeft(ele){
    var offsetLeft = ele.offsetLeft();
    var current = ele.offsetParent();
    while(current!=null){
        offsetLeft += current.offsetLeft();
        current = current.offsetParent();
    }
    return offsetLeft
}

client 客户区大小 元素内容及内边距所占据的大小,不包括:滚动条 边框
一般用在确定游览器的大小
if(document.compatMode == "BackCompat"){
    document.body.clientHeight
}else{
    //for ie7 and earlier
    document.documentElement.clientHeight
}

scroll 滚动大小 也要区分 documentElement和body
scrollTop 是滚动掉的部分

获取偏移量
(这写法太牛逼了,减少判断,最终合一,但是好像不能获得相对偏移量,只是相对屏幕)
function getOffset(el, doc, docElem, box) {
      try {
     box = el.getBoundingClientRect();
     } catch(e) {}


     if ( !box || !zChain.contains(docElem, el) ) {
     return box ? {top: box.top, left: box.left} : {top: 0, left: 0};
     }


     var body = doc.body,
     win = getWindow(doc),
     clientTop = docElem.clientTop || body.clientTop || 0,
     clientLeft = docElem.clientLeft || body.clientLeft || 0,
     scrollTop = win.pageYOffset || docElem.scrollTop || body.scrollTop,
     scrollLeft = win.pageXOffset || docElem.scrollLeft || body.scrollLeft,
     top = box.top + scrollTop - clientTop,
     left = box.left + scrollLeft - clientLeft;


     return {top: top, left: left};
}

第12章 事件
click是事件,onClick是事件处理程序(事件侦听器),我们要编写的是后者
某个元素支持的每种事件,都可以使用一个与相应时间处理程序同名的HTML特性来指定。
这个特性的值应该是能够执行的JS代码。

DOM0的事件处理程序是将一个函数赋给一个事件处理程序
使用DOM0级的方法指定的时间处理程序被认为是元素的方法,这个时候事件处理程序是在元素的作用域进行
btn.onclick = null //删除事件处理程序

DOM2 addEventListener,removeEventListener
如果ADD的函数是匿名的将无法移除,remove的参数必须和add参数相同
IE attachEvent detachEvent
其中的this指向的是全局,而非元素
事件的处理程序不是添加他们的顺序执行,而是倒序

this 和 currentTarget 始终是一样的 但是target是要看具体点击到哪个元素 ,一个是绑定的元素,一个是激发的元素,不是同一个的原因是“冒泡”,这3个的值是DOM元素,可直接用DOM方法

常用event属性 :
event.type event.currentTarget event.target event.preventDefault() event.stopPropagation
evet.cancelBubble event.srcElement event.type event.returnValue //for ie

DOM0级的event是通过window.event而其他包括ie也是通过传参

window.onload 写在HTML里就是<body onload="xxx">,因为HTML无法访问WINDOW元素

坐标
在没有滚动的情况下,pageX pageY 与 clientX clienY 相等的
IE8以前不支持pageX,可以计算
pageX = event.pageX
if(!pageX){
    pageX = event.clientX+(document.documentElement.scrollLeft||document.body.scrollLeft)
}
screenX screenY
表示游览器相对屏幕的坐标

特殊键(修改键)
e.shiftKey e.ctrlKey e.altKey e.metaKey(windows上的windows苹果机上的cmd,ie8之前不支持)

按键事件
keydown keypress keyup
event.keyCode
退格 8 回车13

移除事件
在移除元素 removeChild replaceChild 或者更多情况下innerHTML 事件还在 要手动删除
btn.onclick = null

DOM事件模拟
createEvent() dispatchEvent()
createEventObject() fireEvent() //for ie

第13章 表单
表单属于 HTMLFormElement 继承自 HTMLElement 继承自Element 继承自 Node
form submit 事件 form.submit()不会触发submit事件

解决重复提交的办法:
1 提交表单后禁用提交按钮
2 利用onsubmit程序取消后续的表单提交

表单重置 <input type="reset"> form.reset() 会触发reset事件

用form.elements[0],form.elements[name]来访问表单中的元素

共有的表单字段属性
disabled form name readOnly tabIndex type value

input和button 的 type 可以动态修改 select的type是只读的

特有的方法:focus() blur()
特有的时间:focus blur change

input初始值在value中
textarea初始值在标签里
不建议改初始值,因为不一定会显示在DOM中
size和maxLength的区别:可见长度和可输入长度

选择文本
当用户获取焦点的时候就选中,方便操作
ele.addEvent("focus",function(e){
    var target = e.target;
    target.select()
})

取得选择文本(textbox为当前文本框)
textbox.value.substring(textbox.selectStart,textbox.selectEnd)
document.selection.createRange().text//for ie

选择部分文本
textbox.setSelectionRange(start,end)
//for ie
range = textbox.createTextRange()
range.collapse(true)
range.moveStart("character",0)
range.moveEnd("character",3)
range.select()

过滤输入
过滤所有输入,文本框变成只读
textbox.bind("keypress",function(e){
    e.preventDefault()
    //如果是数字,下面的函数是转UNI字符为真实的字符
    if(!/\d/.test(String.formCharCode(e.target.keyCode))){

    }
})

选择框
var selectbox = document.forms[0].elements["location"];
var text = selectbox.option[0].text;
var value = selectbox.option[0].value;
选择选中项
selectbox.options[selectbox.selectedIndex]
选中一个项
selectbox.option[0].selected = ture


自动切换焦点
代码很有趣,首先定位自己,然后定位相对后面的元素
if(target.value.length == target.maxLength){
    for(var i =0,len = form.elements.length;i<len;i++){
        if(target == form.elements[i]){
            if(form.elements[i+1]){
                form.elements[i+1].focus()
            }
             return
        }
    }
}

表单序列化
转为JSON传给服务器(很重要)

第17章 错误的处理与调试

复习一下继承
custumError.prorotype = new Error()
new custumError("xxx")

error必须使用DOM0绑定,不会传递event参数,
只传递,错误消息,错误所在URL,行号

最常见的就是数据类型的错误 比如不是数组要用sort()
基本类型的检验用 typeof
对象的值 用 instanceof

ie中event是window的属性 当函数用完event销毁,但如果函数内有闭包 将会发生错误

使用window.onerror事件处理程序,可以接受try-catch不能处理的所有错误
window.onerror = function(message,url,line){}
貌似除了window.onerror 就只有image.onerror


第18章 javascript与xml

第20章 JSON
JSON.stringify(a,b,c)
a为要序列化的JSON
b为过滤
c为缩进

stringify parse 都支持function(key,value){},为参数

第21章 AJAX
XMLHttpRequest
第一个方法是open(get,"example.php",false),并不发送请求,而是启动一个请求准备发送
然后是send(null),到这是发送
关键的4个属性
xhr.responseText
xhr.responseXML
xhr.status
xhr.readyState 0:为初始化 1:启动 2:发送 3:接收 4:完成

xhr.onreadystatechange =function(){
    if(xhr.readyState == 4){
        if(xhr.status>200&&xhr.status<300||xhr.stats === 304){
            //success
        }
    }
}
xhr.abort()取消请求
由于内存原因 不建议保存XHR重用

AJAX跨域(CORS)
基本思想:使用自定义的HTTP头部让游览器与服务器进行沟通
举个栗子:Origin:http://www.aaa.com
          Access-Control-Allow-Orign:http://www.aaa.com
两个头匹配就可以跨域 但不能传输COOKIE

IE8以上 用XDR XDomainRequest
其他游览器 都OK
通用方案:
function createCORSRequest(method,url){
    var xhr = new XMLHttpRequest();
    if("withCredent" in xhr){
        xhr.open(method,url,true)
    }else if(typeof XDomianRequest != "undefined"){
        xhr = new XDomainRequest()
        xhr.open(method,url)
    }else{
        xhr = null
    }
    return xhr
}
var request = createCORSRequest("get","htttp://xxxx");
if(request){
    request.onload = function(){

    }
    request.send();
}

其他跨域技术
1.图像PING
只能发送GET请求,无法访问服务器响应文本,只是通过onload onerror 交互
2.JSONP
其实就是把回调函数放在JSON中
3.comet(服务器推送)
长轮询:发起一个请求,服务器一直打开,发送完数据后,关闭,然后再打开
短轮询:每过一段时间请求一下,有就推送
流:只有一个HTTP请求,检测readyState 是否为 3,就可以实现流,为4时就停止
receive = 0
result += xhr.responseText.subString(receive)
receive = result.length
IE不行,你妹!
sse服务器发送时间
主要是单向链接,只读
MIME类型就是设定某种扩展名的文件用一种应用程序来打开的方式类型
支持上面的COMET的所有,

web socket
协议变了:http:// => ws:// https:// => wss://
自定义协议的好处是,发送的字段少,HTTP会发送很多头信息

如果没有自由度建立服务器,因为WEB SOCKET改变HTTP
如果没有必要双向通信
就选SSE

安全
1.SSL方式访问XHR 我也不明白
2.每次出现验证码
3.发送POST而不是GET 容易伪造
4.检查URL来源 容易伪造
5.COOKIE验证 容易伪造


第22章 高级技巧

类型检测:
typeof 不靠谱
instanceof 如果包含多个全局作用域不靠谱
Object.prototype.toString.call(value)
1.因为IE为COM对象,所以检测不出Function,只会是Object
2.只能检测原生的

作用域安全的构造函数:
function Person(){
    if(this instanceof arguments.callee){

    }else{
        return new arguments.calee
    }
}

惰性载入:
1:在处理时载入
function createXHR(){
    if(typeof XMLHttpRequest !="undefined"){
        createXHR = XXX
    }else if(typeof ActiveXObject !="undefined"){
        createXHR = XXX
    }
}

2:申明时就载入
var createXHR = (function(){
    
 if(typeof XMLHttpRequest !="undefined"){
        return function(){...}

    }else if(typeof ActiveXObject !="undefined"){
        return function(){...}
    }

})()

函数绑定
绑定函数中的this
function bind(fn,context){
  return function(){
       return fn.apply(context,arguments)
    }
}

函数柯里化
与函数绑定一样,不同的是返回的函数需要设置一些参数
function curry(fn){
    var args = Array.prototype.slice.call(arguments,1)
    return function(){
        var innerArgs = Array.prototype.slice.call(arguments,0);
        var finalArgs = args.concat(innerArgs);
        return fn.apply(null,finnalArgs)
    }
}

自定义事件
听重要就是监听模式,自己搞一搞



第24章 最佳实践
需要注释的:函数,大段代码,算法,HACK
命名:函数名应为动词开始,返回布尔值的函数以IS开头

松散耦合
解耦 :HTML与JS CSS与JS 用class 应用逻辑与事件处理程序

避免与NULL比较
如果值是引用 ,用instanceof 比较
如果值是值 ,用typeof

用大写的变量表示常量 :MAX = 100

性能
尽量使用变量和数组,用对象会先查找属性
[5,10]
{first:5,second:10}

减值循环

猜你喜欢

转载自zhyp29.iteye.com/blog/2303894