第7章 浏览器环境
7.1 在HTML页面中引入JavaScript代码
7.2概述BOM与DOM(页面以外事物对象和当前页面对象)
7.3 BOM
7.3.1 window对象再探(所以JavaScript核心函数也都是window对象的方法)
7.3.2 window.navigator(浏览器信息,如window.navigator.userAgent)
7.3.3 Firebug的备忘功能
7.3.4 window.location(当前页面的URL信息,如设置href属性可实现页面跳转)
7.3.5 window.history(访问页面的历史记录信息,三个方法:forward、back、go)
7.3.6 window.frames(当前页面中所有frame的集合)
7.3.7 window.screen(浏览器以外的桌面信息)
7.3.8 window.open()/close()(打开/关闭新窗口,现已被页面内自定义弹窗代替)
7.3.9 window.moveTo()/resizeTo()(移动浏览器窗口位置/调整浏览器窗口大小,不建议使用)
7.3.10 window.alert()/prompt()/confirm()(系统弹窗)
7.3.11 window.setTimeout()/setInterval()(系统定时器,延时/循环)
setTimeout("alert('boo')",2000);//首参数是一个可以被eval执行的字符串,但应尽量避免 setTimeout(function(){alert('boo')},2000)//推荐:将相关函数调用封装在另一个函数中
7.3.12 window.document(等同于DOM)
7.4 DOM
7.4.1 Core DOM与HTML DOM(XML与HTML)
7.4.2 DOM节点的访问(nodeType、nodeName、nodeValue、documentElement(根节点)、childNodes(子节点集合)、parentNode(父节点)、attributes(节点的属性集合)、textContent(获取节点中的文本内容)、innerHTML)
(getElementByTagName、getElementByName,getElementById、nextSibling、previousSibling、firstChild、lastChild)
7.4.3 DOM节点的修改(属性赋值或innerHTML覆盖)
7.4.4 新建节点(creatElement、creatTextNode并appendChild,或使用innerHTML、choneNode(true/false)、insertBefore)
7.4.5 移除节点(removeChild,replaceChild,innerHTML设置为‘’)
7.4.6 只适用于HTML的DOM对象(document.body等价于document.getElementByTagName('body')[0])
(document.images/applets/links/anchors/forms,document.write,document.cookies/title/referrer/domain)
7.5 事件
7.5.1 内联HTML属性法
<div onclick="alert('boo!')">click</div>
7.5.2 元素属性法
<div id="mybtn">click</div> //以下JS部分 document.getElementById('mybtn').onclick=function(){alert('boo!')}
7.5.3 DOM的事件监听器(DOM level 2)
var btn=document.getElementById('mybtn'); btn.addEventListener('click',function(){alert('boo!')})
7.5.4 捕捉法与冒泡法(addEventListener的第三个参数为false只使用冒泡法,stopPropagation阻止冒泡 )
7.5.5 阻断传播(removeEventListener移除事件,由匿名函数定义的监听器是不能被移除的)
7.5.6 防止默认行为(preventDefault)
7.5.7 跨浏览器事件监听器(IE中没有addEventListener用attachEvent,没有target用srcElement,没有stopPropagetion用cancelBubble为true,没有preventDefault用returnValue为false,没有removeEventListener用detachEvent)
7.5.8 事件类型
7.6 XMLHttpRequest对象
7.6.1 发送请求
var xhr=new XMLHttpRequest(); xhr.onreadystatechange=myCallback; xhr.open('GET','somfile.txt',true); xhr.send('')
7.6.2 处理响应(当readystate为4时响应返回,继续检测xhr.status服务器返回代码,并进行下一步处理)
7.6.3 在早于7的IE版本中创建XMLHttpRequest对象
var xhr=new ActiveXObject('MSXML2.XMLHTTP.3.0');
7.6.4 A代表异步
7.6.5 X代表XML
7.6.6 实例示范
7.7 本章小结
7.8 练习题
第8章 编程模式与设计模式
8.1 编程模式
8.1.1 行为隔离(HTML、CSS与JS隔离,即内容、外观与行为隔离)
8.1.2 命名空间(只定义一个全局变量,其他变量和方法定义为该变量的属性,很多库都实现的namespace方法如下)
var MYAPP={} MYAPP.namespace=function(str){ var parts=str.split('.'),curr=MYAPP; for(var i in parts){ if(!curr[parts[i]]){curr[parts[i]]={}} curr=curr[parts[i]] } } MYAPP.namespace('dom.style'); //等价于如下 var MYAPP={ dom:{ style:{} } }
8.1.3 初始化分支(根据浏览器特性,一开始判断后再给特定功能函数赋值,当这些功能函数被调用,就用不需要再做探测了,兼容性初始化)
var MYAPP={}; MYAPP.event={ addListener:null, removeListener:null } if(typeof window.addEventListener==='function'){ MYAPP.event.addListener=function(el,type,fn){el.addEventListener(type,fn,false);} MYAPP.event.removeListener=function(el,type,fn){el.removeEventListener(type,fn,false);} }else if(typeof document.attachEvent==='function'){//IE MYAPP.event.addListener=function(el,type,fn){el.attachEvent('on'+type,fn);} MYAPP.event.removeListener=function(el,type,fn){el.detachEvent('on'+type,fn);} }else{//older browsers MYAPP.event.addListener=function(el,type,fn){el['on'+type]=fn;} MYAPP.event.removeListener=function(el,type,fn){el['on'+type]=null;} }
8.1.4 延迟定义(同初始化分支。第一次调用时被定义)
var MYAPP={}; MYAPP.myevent={ addListener:function(el,type,fn){ if(typeof window.addEventListener==='function'){ MYAPP.myevent.addListener=function(el,type,fn){el.addEventListener(type,fn,false);} }else if(typeof document.attachEvent==='function'){//IE MYAPP.myevent.addListener=function(el,type,fn){el.attachEvent('on'+type,fn);} }else{//older browsers MYAPP.myevent.addListener=function(el,type,fn){el['on'+type]=fn;} } MAAPP.myevent.addListener(el,type,fn); } }
8.1.5 配置对象(用于有很多参数的函数,用对象替代参数可以不考虑参数顺序,跳过默认值,扩展性更强,可读性更好)
8.1.6 私有属性和方法(对象方法内部定义的局部变量和函数)
8.1.7 特权函数(建立公共函数,它们可以访问内部私有属性和方法,如get/set方法)
8.1.8 私有函数的公有化
var MYAPP={}; MYAPP.dom=(function(){ var _setStyle=function(el,prop,value){console.log('setstyle')} var _getStyle=function(el,prop){console.log('getstyle')} return { setstyle:_setStyle, getstyle:_getStyle, yetAnother:_setStyle } })() MYAPP.dom.setstyle=function(){alert('b')}//外部可以改写setstyle方法,yetAnother依然指向_setStyle
8.1.9 自执行函数(保证命名空间,特别适合执行一次性初始化任务)
8.1.10 链式调用(前一个方法的结果(即返回对象)来调用下一个方法)
8.1.11 JSON
8.2 设计模式
8.2.1 单件模式1
var single={};//最基本的单件模式
8.2.2 单件模式2(仅生成一个对象)
function Logger(){ if(typeof global_log==='undefined'){global_log=this;} return global_log; } var a=new Logger(); var b=new Logger(); alert(a===b);//true,此方法产生全局变量global_log
var Logger=(function(){ var instance=null; return { getinstance:function(){ if(instance==null){instance=this}; return instance; } } })() var a=Logger.getinstance(); var b=Logger.getinstance(); alert(a===b);//true,利用只执行函数和闭包来实现
8.2.3 工厂模式(创建对象模式)
//假如我有三个不同的构造器,它们功能相似,但处理细节不同 var MYAPP={}; MYAPP.dom={}; MYAPP.dom.Text=function(){ this.insert=function(where){ var txt=document.createTextNode(this.url); where.appendChild(txt) } }; MYAPP.dom.Link=function(){ this.insert=function(where){ var link=document.createElement('a'); link.href=this.url; link.appendChild(document.createTextNode(this.url)); where.appendChild(link) } }; MYAPP.dom.Image=function(){ this.insert=function(where){ var img=document.createElement('img'); img.src=this.url; where.appendChild(img) } }; //使用三个构造器的方法都一样,设置URL属性并调用insert方法 var t=new MYAPP.dom.Text(); t.url='http://www.baidu.com/1.jpg'; t.insert(document.body) //如果我们暂时不知道创建哪种对象,需要根据用户触发按钮来决定,按钮提供type表示创建类型,如下 var o; if(type ==='Text'){o=new MYAPP.dom.Text(); }else if(type ==='Link'){o=new MYAPP.dom.Link(); }else if(type ==='Image'){o=new MYAPP.dom.Image();} o.url='http://...'; o.insert(); //添加工厂方法factory,避免如上判断 MYAPP.dom.factory=function(type){return new MYAPP.dom[type];} var o=MYAPP.dom.factory(type); o.url='http://...'; o.insert();
8.2.4 装饰者模式(结构性模式,主要拓展对象的功能)
//装饰者模式实例:装饰一颗圣诞树 var tree={ decorate:function(){console.log('确保树不会倒');} }; //接着定义方法用于添加额外的装饰器,装饰器实现为构造器函数,都继承tree tree.getDecorate=function(deco){tree[deco].prototype=this;return new tree[deco];}//让每一个装饰后对象的原型等于父级 //下面创建装饰器,将他们设置为tree的属性(保证命名空间),它们提供decorate方法,单先调用父类的decorate tree.RedBalls=function(){ this.decorate=function(){this.RedBalls.prototype.decorate();console.log('上面放一些红球')} } tree.BlueBalls=function(){ this.decorate=function(){this.BlueBalls.prototype.decorate();console.log('下面放一些蓝球')} } tree.Angel=function(){ this.decorate=function(){this.Angel.prototype.decorate();console.log('在树顶放一个小天使')} } //再把所有的装饰器都添加到基础对象上 tree=tree.getDecorate('BlueBalls') tree=tree.getDecorate('Angel') tree=tree.getDecorate('RedBalls') tree.decorate(); //tree.Redballs的原型为父类tree.Angel的原型为父类tree.RedBalls的原型为父类tree(初始状态) console.log(tree.getDecorate('BlueBalls').getDecorate('Angel').getDecorate('RedBalls'))
8.2.5 观察者模式(行为模式,主要用于处理不同对象间的交互通信)
//先创建一个观察者,它包含由回调函数构成的订阅者数组,用于增加和删除订阅者的方法,publish授受并传递数据给订阅者,make将任意对象转变为发行商并添加上述方法 var observer={ addSubscriber:function(callback){this.subscribers[this.subscribers.length]=callback;}, removeSubscriber:function(callback){ for(var i=0; i<this.subscribers.length;i++){if(this.subscribers[i]==callback){delete(this.subscribers[i])}} }, publish:function(what){ for(var i=0; i<this.subscribers.length;i++){if(typeof this.subscribers[i]==='function'){this.subscribers[i](what)}} }, make:function(o){ for(var i in this){o[i]=this[i];o.subscribers=[]} } } //接下来我们创建一些订阅者,它们可以是任意对象,唯一职责就是在发生重要事件时调用publish方法 var blogger={ writeBlogPost:function(){var content='今天是'+new Date();this.publish(content);} } var latimes={ newIssue:function(){var paper='火星人来地球了!';this.publish(paper);} } //它们都很容易转变为发行商 observer.make(blogger); observer.make(latimes); //于此同时,准备两个简单对象 var jack={ read:function(what){console.log('我刚看了'+what)} } var jill={ gossip:function(what){console.log('你没听到我的话,但'+what)} } //将他们订阅blogger,只需要提供事件发生时的回调函数 blogger.addSubscriber(jack.read); blogger.addSubscriber(jill.gossip); //当blogger写了新的博客时,jack和jill都会收到通知 blogger.writeBlogPost(); //任何时候jill都可以取消订阅,再推送时将不再收到通知 blogger.removeSubscriber(jill.gossip); blogger.writeBlogPost(); //jilly也可以订阅latimes,因为一个订阅者可以对应多个发行商 latimes.addSubscriber(jill.gossip); latimes.newIssue()
--完--