从菜鸟到高手的路还有多远?---javascript

javascript学习二
  
  Professional JavaScriptfor Web Developers
  Nicholas C. Zakas
  
  
  文献
  [2] Netscape的DevEdge网站:http://devedge.netscape.com,有很多对web开发者有用的信息和脚本工具。
  [3] DHTML Part 3: Browser Object Model,http://www.webdevelopersjournal.com/articles/dhtml3/dhtml3.html
  [4] JavaScript and Browser Objects Quick Reference
  
  1 ECMAScript基础
  区分大小写。
  
  虽然变量是弱类型的,但是在一个变量中始终存放相同类型的数据才是好的编码习惯。
  
  Primitive valuereference value的区别:
  注意:String在ECMAScript中是primitive的,很多语言中是referenced。
  因为,referenced value的大小是会改变的,典型的例子就是对象。该类数据应该
  分配在堆heap,而不是stack。
  
  typeofnull,返回的是object
  这是javascript实现时的一个错误,被带进了ECMAScript。现在,null被认为是一个空对象,这样来解释返回的object值。但是,注意:技术上,null还是primitive的。
  
  关于变量有var与没有var的区别
  变量有两个方法来声明declaration:
  1. 用关键字var声明,
  2. 直接赋值:a = “3”, b = a +”4”;对于没有用var声明的变量会自动为其声明一个全局的变量。
  虽然可以不声明而直接赋值,但是声明而直接以其他方法使用,比如使用其他的运算符(=可以算是赋值运算符)会报错,除了typeof。
  也就是说,目前来看:为声明的变量可以直接用在=和typeof上。
  比如,alert(typeof aV)正确,而alert(aV)错误,解释程序会说aV未定义。
  
  两个特殊的数据类型:
  Undefined,只有一个值undefined。
  Null,只有一个值null。
  undefined是从null派生的,所以ECMAScript认为两者是相等的。虽然两者相等,
  但是表示的意义是不同,
  undefined:声明而没赋值。
  null:不存在的对象。
  
  String是唯一没有固定大小的primitive类型数据。
  
  运算符
  void:一个技巧性运算符,返回undefined,通常用于避免输出不应该输出的值。
  >>:有符号右移,
  >>:无符号右移,
  +:一元加,即加号,对字符串有特殊作用,可以将字符串转换为数字,如
  var sNum=”23”;
  var iNum=+sNum;//结果是数字的23。
  但是在处理八进制表示的字符串和十六进制表示的字符串时有不同,前者还是被认
  为是十进制的。
  +”010”,结果是10,
  +”0xB”,结果是11。
  -:一元减,与一元加类似。
  ==:如果比较不同类型的数据,会先做类型转换,
  ===:全等号,不进行类型转换下的比较。
  
  语句
  with:运行缓慢,避免使用。
  
  函数
  没有重载机制:
  一种替代,可以定义多个名字和参数相同的函数,只有最后定义的那个会其作用。
  参数:ECMAScript不会检查传递给函数的参数个数,因此在函数中可以使用
  arguments[i]直接操作传递进来的参数。
  函数是功能完整的对象Function,这是ECMAScript很重要的一点。
  函数名只是指向函数对象的变量。
  
  闭包closure
  一种函数,其词法表示包括不需要计算的变量,如
  var sMessage= “Hello World!”;
  function sayHelloWorld() {
  alert(sMessage);
  }
  sayHelloWorld();
  上面的脚本在载入后没有为函数计算sMessage变量,sMessage将在调用sayHelloWorld的时候被赋值。
  
  在一个函数中定义另一个函数会让闭包更复杂。
  闭包是ECMAScript中非常强大的部分。
  
  2 对象基础
  javascript到底是基于对象还是面向对象?
  
  使用预定义的对象:
  
  对象的类型:
  1. 本地对象native,
  Object Function Array String
  Boolean Number Date RegExp
  Error EvalError RangeError ReferenceError
  SyntaxError TypeError URIError
  2. 内置对象build-in
  由ECMAScript实现提供的,
  独立于宿主环境的所有对象,
  在ECMAScript执行时就出现的。
  
  只有两个内置对象:
  [1] Global
  在ECMAScript中没有单独存在的函数,所有函数都是某个对象的方法。
  那些看起来是独立的函数,像parseInt,都是Global的方法。
  注意这个可能是ECMAScript中最强的方法
  eval:该方法就像是ECMAScript的解释程序,其参数是一个字符串,它将
  该字符串作为一个ECMAScript代码直接插入eval所在位置。
  Global对象的所有属性,注意,所有本地对象的构造函数都是其属性:
  
  [2] Math
  3. 宿主对象host
  ECMAScript实现的宿主环境提供的对象,像所有BOM,DOM对象。
  
  
  作用域问题:
  ECMAScript所有属性和方法都是公有的。但是约定,将_property_这种方式定义的属性看作应该是私有的。
  
  this:
  指向调用该方法的对象。
  
  创建新对象:
  var oCar = new Object;
  oCar.color = “red”;
  oCar.doors = 4;
  oCar.mpg = 23;
  oCar.showColor = function () {
  alert(this.color);
  };
  有创建新对象方法如上,问题:如果要创建第二个相同对象,则需要将上述代码再写一次。
  
  工厂方法
  function createCar() {
  var oTempCar = new Object;
  oTempCar.color = “red”;
  oTempCar.doors = 4;
  oTempCar.mpg = 23;
  oTempCar.showColor = function () {
  alert(this.color)
  };
  return oTempCar;
  }
  var oCar1 = createCar();
  var oCar2 = createCar();
  修改一下,可以传递参数:
  function createCar(sColor, iDoors, iMpg) {
  var oTempCar = new Object;
  oTempCar.color = sColor;
  oTempCar.doors = iDoors;
  oTempCar.mpg = iMpg;
  oTempCar.showColor = function () {
  alert(this.color)
  };
  return oTempCar;
  }
  var oCar1 = createCar(“red”, 4, 23);
  var oCar1 = createCar(“blue”, 3, 25);
  上面的问题:
  每次创建一个新对象的时候,其中的方法,比如showColor,都会创建一次,即每个对象都有自己的showColor,而实际上操作都是一样的,这样的话浪费了空间。
  再改,将方法对应的函数在外面定义
  function showColor() {
  alert(this.color);
  }
  function createCar(sColor, iDoors, iMpg) {
  var oTempCar = new Object;
  oTempCar.color = sColor;
  oTempCar.doors = iDoors;
  oTempCar.mpg = iMpg;
  oTempCar.showColor = showColor;
  return oTempCar;
  }
  var oCar1 = createCar(“red”, 4, 23);
  var oCar2 = createCar(“blue”, 3, 25);
  问题:方法的定义方式看起来有点别扭,
  再改,构造函数方式
  function Car(sColor, iDoors, iMpg) {
  this.color = sColor;
  this.doors = iDoors;
  this.mpg = iMpg;
  this.showColor = function () {
  alert(this.color)
  };
  }
  var oCar1 = new Car(“red”, 4, 23);
  var oCar2 = new Car(“blue”, 3, 25);
  利用了this,没有像工厂方法在内部创建一个对象,然后设置,然后返回改对象。
  问题:同工厂方法一样,方法showColor在创建对象时被创建多次,
  因此,也可以将showColor在外部创建。
  原型方法:解决方法如果在内部定义会被多次创建,在外部定义又不自然的问题。
  利用了对象的prototype属性。
  function Car() {
  }
  Car.prototype.color = “red”;
  Car.prototype.doors = 4;
  Car.prototype.mpg = 23;
  Car.prototype.showColor = function () {
  alert(this.color);
  };
  var oCar1 = new Car();
  var oCar2 = new Car();
  原理:调用new创建新对象时,其原型的所有属性会被赋值给改对象,所有对象也是指向一个方法的。
  问题:没有参数。
  构造函数和原型方法的结合hybrid constructor/prototype
  利用构造函数定义非函数属性,用原型方法定义方法method。
  function Car(sColor, iDoors, iMpg) {
  this.color = sColor;
  this.doors = iDoors;
  this.mpg = iMpg;
  this.drivers = new Array(“Mike”, “Sue”);
  }
  Car.prototype.showColor = function () {
  alert(this.color);
  };
  var oCar1 = new Car(“red”, 4, 23);
  var oCar2 = new Car(“blue”, 3, 25);
  oCar1.drivers.push(“Matt”);
  alert(oCar1.drivers); //outputs “Mike,Sue,Matt”
  alert(oCar2.drivers); //outputs “Mike,Sue”
  动态原型方法
  进一步完美,对熟悉其他语言如java的人来说,上面的混合方法还是不自然。
  原理:跟混合方式的思路一样,只是方法定义的位置不一样。
  function Car(sColor, iDoors, iMpg) {
  this.color = sColor;
  this.doors = iDoors;
  this.mpg = iMpg;
  this.drivers = new Array(“Mike”, “Sue”);
  if (typeof Car._initialized == “undefined”) {
  Car.prototype.showColor = function () {
  alert(this.color);
  };
  Car._initialized = true;
  }
  }
  检查if (typeof Car._initialized == “undefined”)如果未定义,就定义其原型的方法。
  混合工厂方法hybrid factory paradigm:
  在不能应用上面的方法的时候,作为一个变通之法,
  目的:创建假构造函数,只返回另一种对象的新实例。
  构造函数部分的代码与工厂方法一样,区别在如何使用构造函数创建对象。
  function Car() {
  var oTempCar = new Object;
  oTempCar.color = “red”;
  oTempCar.doors = 4;
  oTempCar.mpg = 23;
  oTempCar.showColor = function () {
  alert(this.color)
  };
  return oTempCar;
  }
  var car = new Car();
  因为构造函数Car内有new运算符,所以会忽略var car=new Car()中的new运算符(为什么)。最后从构造函数返回oTempCar。
  这种方式与经典方式(单独的构造函数,原型方法)有同样的问题,除非万不得已不要使用。
  选择哪种方式?
  使用最广的是混合的构造函数/原型方式,此外动态原型也使用。
  
  关于String的一个技巧
  因为String是长度不变的,所以用String做字符串连接的时候是一个件很耗时间和资源的事情,跟java中的String一样。
  改进:创建一个StringBuffer,如下:
  function StringBuffer()
  {
  this._strings_ = new Array;
  }
  
  StringBuffer.prototype.append = function(str){
  this._strings_push(str);
  }
  
  StringBuffer.prototype.toString = function(){
  return this._strings_.join(“”);
  }
  
  var buffer = new StringBuffer();
  buffer.append(“hello”);
  buffer.append(“world”);
  buffer.append(“!”);
  ...
  var result = buffer.toString();
  这样做的性能,比直接做,高很多,在一万个字符串连接中节省50%到60%的时间。
  var str = “hello”;
  str += “world”;
  ...
  
  修改对象的行为:
  在定义好了对象之后,还可以修改对象的行为。
  创建新方法:
  利用prototype。
  比如,如果想给ECMAScript的每个native对象添加一个新方法,可以在object对象的prototype上定义,如定义:
  Object.prototype.showValue = function (){
  alert(this.valueOf() );
  }
  
  var str=”hello”;
  var iNum = 25;
  str.showValue();//hello
  iNum.showValue();//25
  重定义已有方法:
  因为方法只是指向函数的指针,因此改变指针就能重定义方法。如果想在新方法中调用原方法的话,可以先记录原方法的指针,然后在新方法中引用就是了,如想Function的toString方法,并且在新方法中调用原toString方法。
  Function.prototype.originalToString = Function.prototype.toString;
  Function.prototype.toString = function (){
  Function.prototype.originalToString();
  ...
  }
  在创建了一个具体的新对象之后,再改变该对象的方法,但不建议使用,因为很难跟踪和记录:
  var o = new someObject();
  someobject.prototype.newMethod = function(){}
  
  
  继承机制
  四种方式实现继承:prototype链(原有设计),对象冒充,混合式,其他方式。
  1. 原型链
  ECMAScript的继承原本是基于原型链的。
  
  2. 对象冒充
  3. 混合式
  4. 其他方式
  ECMAScript的继承机制实际上不完善,比如没有私有的概念等。因此有一些开发者创造了一些方法来解决这些问题。
  比如zlnherit库,http://nczonline.net/downloads。
  xbOjbects,目的:提供更强的面向对象范型,http://archive.bclary.com/xbProjects-docs/xbobject 。
  3 浏览器中的javascript
  3.1用标签,将javascript引入html
  可以直接在script中嵌代码,也可以在script中指明js文件的路径。
  Script标签通常可以放在head中,如果放在body中,只要脚本所属的页面部分被装载就会调用一次脚本,通常不这样用,除了特殊效果。
  但是当如果指明了js的路径,那么以嵌入方式存在的代码可能无效(有浏览器决定)。
  
  
  
  
  
  <script type="text/javascript"> <BR>  function openNewWin() <BR>  { <BR>  window.open("http://www.baidu.com", ""); <BR>  } <BR>  function helloworld() <BR>  { <BR>  alert("inner"); <BR>  } <BR>  </script>
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  // outer.js
  function helloworld()
  {
  alert("outer");
  }
  上例中,点击submit按钮之后,起作用的是outer.js中的helloworld函数。
  
  
  用来提示浏览器不支持脚本。
  
  3.2 Svg
  作为在web上绘制矢量图的xml语言,发展势头很好,svg能用javascript对其进行操作。
  
  3.3 BOM Browser Obejct Model
  
  4e80ced7b9f64799b6199282029844f6.png
  
  javascript用来与浏览器,而非网页内容交互的对象模型。
  window
  表示整个浏览器窗口,
  可以用来移动浏览器窗口,
  改变浏览器窗口大小,
  打开窗口,关闭窗口,
  弹出系统对话框(三类:警告对话框alert,确认对话框confirm,输入对话框prompt),
  操作状态栏,
  控制时间间隔,时间暂停:可以用来实现特殊效果,比如鼠标在某个元素上停留大于1秒的时候就显示提示信息等。
  访问浏览器的访问历史:类似浏览器工具栏上的前进,后退按钮。
  另外,如果页面是frameset的,可以通过window来操作每个frame,所有的frame构成一个frame集合。
  Window的特权:作为BOM的核心,不用明确引用它,在引用函数,对象等的时候,程序会自动查看window对象里面是否有对应的函数,对象等。比如frame[0],就是window.frame[0].
  parent,self,
  
  document
  是window的属性,是dom的核心。
  因为BOM没有实现的统一标准,因此对document这个属性的实现是不一致的。
  从bom的观点看document是一些集合组成,这些集合包括:
  anchors,
  applets,
  embeds,所有嵌入式对象的集合,
  forms,
  images,
  links
  
  location
  比较常用的对象,
  是window和document的属性,
  可以表示和操作窗口的url。
  没有实现标准。
  
  Navigator
  Window的属性。
  没有标准。
  关于web浏览器的信息,比如
  浏览器的版本信息,是否启用了java,cookie,可以识别的mime类型,操作系统的语言等。不是每个浏览器都实现了前面几个方面的信息。
  常利用navigator判断浏览器的类别。
  
  Screen
  出于安全的考虑,用户系统的信息大多数被隐藏了,但是screen可以操作用户的屏幕。
  是window的属性。
  
  4 DOM基础
  关于sax和dom的区别:
  DOM最先被用于xml。
  Xml出现之后首先是用编程语言来解析,比如java的SAX(simple api for xml)。Sax以事件为基础,以文本方式从文件头开始读,每当遇到起始标志或结束标志、特性、文本、其他xml语法就触发一个事件,然后交由程序员决定做何处理。
  因为基于文本的解析,所以sax是轻量级的。但是缺点是:没法停止、后退、或者不从头读文件而是直接访问指定部分。
  
  DOM是基于树的api。不光解析xml,并且将xml的内容用相互关联的对象来表示。因为表示为了对象,所以可以直接读、修改。但是还是要先解析一次代码以创建一个DOM树模型(有时可以用sax来完成解析工作)。DOM模型建立之后就意味着完全将xml表示为了树模型。显然,为了创建基于对象的树会需要更多的开销。但是因为DOM的方便性,这是值得的。
  
  注意:DOM是语言无关的API
  
  DOM的常识
  参见
  [1] DOMXSLTXPathRef
  
  [2] Document Object Model (DOM)
  http://www.w3.org/DOM/
  
  DOM是树结构的,所以其API处理的就是节点以及节点的层次问题。
  DOM由DOM对象object组成,每个对象还有属性property,和方法methods。
  
  DOM可以用来解析很多种xml,比如svg,xhtml,因此存在core DOM的概念,是其他面对特定应用的DOM(如svg,smil,animation,mathml,见http://www.w3.org/TR/MathML2等)的基础。
  
  另外,因为现有多数html不是合法的xml,所以浏览器多对html采取宽容的态度,以解析html为DOM文档。
  
  浏览器对DOM的支持是不一样的。
  
  Core DOM:
  对象:
  Document,
  DocumentType:指DTD引用部分,
  DocumentFragment,
  Element,
  Attr,
  Text:起始和结束标志间,或CData Section内的普通文本。
  CDataSection:的对象表现形式,
  Entity,DTD中的实体定义,如,
  EntityReference,
  ProcessingInstruction:代表一个PI,
  Comment,
  Notation:DTD中定义的记号。
  
  接口:
  Node:
  代表了文档树中的一个节点,
  所有的DOM对象都实现了Node接口。
  Node的一些property:
  nodeName:根据节点类型的不同而定义不同,
  nodeValue:同上,
  nodeType:节点的类型常量之一。
  
  助手对象helper objects,帮助访问和遍历:
  NodeList:按数值索引,表示一个元素的子节点。
  NamedNodeMap:同时用数值和名字进行索引的节点表,用于表示元素的attribute。
  
  
  DOM的使用:
  访问节点,
  检查节点类型,
  处理属性attribute:Node接口有attributes方法,但是注意只有Element节点有attribute。Element节点的attributes其实是
  NamedNodeMap,提供了访问和管理Element内容的方法。
  访问指定节点,
  创建和操作节点。
  HTML DOM
  DOMLEVEL1
  
  大多数Html元素的属性attribute可以作为DOM中相应对象的属性property。
  就是说””中的src和border是可以作为property在html dom中直接操作的。
  Alert( oImg.getAttribute(“src”));
  等价于
  alert( oImg.src );
  唯一的attribute和property不一样的是class这个attribute。因为class在ECMAScript中是保留字,所以不能直接使用它,改为:className。
  

  中,这样来操作div的class:
  alert( oDiv.className );
  
  另外,IE在setAttribute上的支持有问题,你不能确保setAttribute总是正确执行,因此在要操作html元素的attribute的时候最好使用dom中相
  应对象的property。
  另外,有人这样来做的:
  
  为了支持通过dom来创建table的方式更简洁,html dom为table,tbody,tr加入了一些property和methods。
  
  DOMLEVEL2
  
  注意:level2的支持浏览器实现了的不多,
  遍历DOM:
  
  DOM一致性检查
  DOM中,document有一个property,implementation,用来检查某具体的DOM实现是否实现了某特性,比如css是否支持,支持的版本等这类问题。
  
  但注意:用implementation来检查,其结果未必和实际相符合。即implementation告诉你实现了某特性,不是说DOM真的实现了,只是一个参考价值
  的结果,因为程序员可以强制对某特性返回支持的结果。
  
  5正则表达式 regular expression
  狂复杂的。
  
  ECMAScript对正则表达式的支持:RegExp对象,构造RegExp对象时有两个参数,一是进行匹配的字符串,二是处理指令。
  RegExp对象的创建:
  显式的构造函数,
  字面量literals:使用字面量比构造函数方便,体现在:
  var aReg = new RegExp(“//?”);//出于转义的原因,//?对/转义之后才是/?
  等价于
  var aReg = /?;
  
  RegExp对象的使用:
  直接用RegExp对象的test方法可以匹配字符串,
  用String对象的方法,以RegExp对象做参数来匹配字符串。
  
  正则表达式中的保留符号,元字符:
  ()[]{}/^?$*+.
  可以利用/转义的功能使用ascii和unicode。
  一些特殊字符,如/t,/r,/n
  
  e601ca9409104e79ac32caecbf15b58c.png
  
  23a070d40c9b45b782acc96ea8fc0390.png
  
  量词quantifier:
  指定某个模式出现的次数。
  简单quantifier:
   *
  +
  {n}
  {n,m}
  {n,}
  贪婪量词greedy
  惰性量词reluctant
  支配性量词possessive
  浏览器对greedy,reluctant,possessive支持不够。
  
  分组:如
  /(dog)?/:dog出现0或多次。
  分组使用(),但是()不光只用于分组,还可以表示“捕获性分组”。即使用正则表达式匹配一个字符串之后,字符串中符合()指定的pattern的匹配结果就会被记到内存,这时可以用类似$1,$2再次引用,比如alert。1,2的编号是根据()是从左到右第几个()来确定的。这就是所谓反向引用:在匹配之后的再引用。
  
  反向引用:
  backreference
  如,再匹配结束之后,再次引用()指定的pattern所匹配的结果字符串。
  var sToChange = “1234 5678”;
  var reMatch = /(/d{4}) (/d{4})/;
  var sNew = sToChange.replace(reMatch, “$2 $1”);
  alert(sNew); //outputs “5678 1234”
  候选:
  alternation
  /a|b|c/
  非捕获性分组
  non-capturing groups
  对应的是捕获性分组,在分组中使用()时,正常情况都是捕获性的,因为可以使用backreference来再次引用。
  在分组中以?;开头的就是非捕获性的,如/#(?:/d+)/是对”#1234567890”的,这个匹配的模式有两部分,由#和一个分组(?:/d+)组成的。(?:/d+),首先是一个分组,其次是一个非捕获性分组。
  前瞻lookahead:
  指定需要匹配的字符必须在某个字符或字符串后面出现时才匹配。如/bed(?=room)/,目的是匹配room,但是必须出现在bed之后的room才匹配。
  后瞻:
  javascript未支持。
  边界boundary:
  用来界定pattern应该出现的位置,比如行头^<或行尾$,单词的边界/b,非单词的边界/B。
  多行模式multiline mode:
  上面的边界匹配的时候,对如下情况有问题:
  var sToMatch = “First second/nthird fourth/nfifth sixth”
  var reLastWordOnLine = /(/w+)$/g;
  var arrWords = sToMatch.match(reLastWordOnLine);
  匹配只能返回sixth,
  所以通过指令,采用多行模式,改为/(/w+)$/gm
  
  
  关于RegExp对象的更多信息:
  RegExp是对象,所以考察一下它的属性:
  global:是否设置了全局选项
  ignoreCase:boolean
  lastIndex:下次匹配开始的index
  multiline:boolean
  source:正则表达式的字符串表示。
  静态属性:
  记录一些匹配过程的信息。
  
  0e239a9c570c4e7097a8bb66d38fecc8.png
  
  
  
  正则表达式的实际应用:
  通常用正则表达式在提交给server前,验证用户输入。
  
  验证日期:
  验证信用卡,
  URL,
  EMAIL
  6 浏览器和操作系统检测
  先讲一下,现在的浏览器的历史:
  netscape navigator是最早流行的,其engine是基于mozilla的,其user-agent string声明为
  Mozilla/AppVersion (Platform; Security [; OS-or-CPU-Description])。
  其中Security有I,U,N,三种,分别代表低,高,没有安全度。
  之后IE3.0推出,为了表示它与netscape是兼容的,IE也是以mozllia开头声明,
  Mozilla/2.0 (compatible; MSIE [IEVersion]; [OS])。
  IE3.0为什么声明为Mozilla/2.0就不知道为什么了。
  1997netscape navigator改名为netscape communicator 4.0,其uer-agent string为:
  Mozilla/AppVersion (Platform; Security [; OS-or-CPU-Description])
  之后,IE4.0推出,其声明为:
  Mozilla/4.0 (compatible; MSIE [IEVersion]; [OS]),
  注意,IE4.0与Mozilla/4.0已经对应起来了。
  IE5.0及更高版本,还是声明为Mozilla/4.0,
  Mozilla/4.0 (compatible; MSIE [IEVersion]; [OS])。
  Netscape6,称为Mozilla,改变了user-agent string的声明:
  Mozilla/MozillaVersion ( Platform ;Security ;OS-or-CPU ;Localization information
  [; PrereleaseVersion] *[; Optional Other Comments] ) Gecko/GeckoVersion
  [ApplicationProduct/ApplicationProductVersion]
  adf7e51e54894527b70a98b4c14f0e51.png
  
  6024728415ce43f9b3bd1516e38a7a8d.png
  
  但是,注意netscape6声明为Mozilla5,如下:
  Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:0.9.4) Gecko/20011128
  Netscape6/6.2.1
  Netscape7.1是最后的netscape系列的浏览器。之后AOL解散了netscape团队。
  但是mozilla项目还是有新版本,一个更友好的mozilla版本firefox。
  
  Opera作为ie和mozilla的替代者,有自己的user-agent string,
  Opera/AppVersion (OS; Security) [Language],
  但是通过菜单上的设置opera可以把自己伪装成其他浏览器,比如伪装为mozilla5,Mozilla/5.0 (Windows NT 5.1; U) Opera 7.54。
  
  safari,2004apple推出的自己的浏览器,基于KHTML。
  Apple从KHTML创建了apple web kit,是apple上的web开发平台,safari也是从中创建的。Safari的格式是:
  Mozilla/5.0 (Platform; Security; OS-or-CPU; Language)
  AppleWebKit/AppleWebKitVersion (KHTML, like Gecko) Safari/SafariVersion
  
  
  浏览器检测
  1. 利用navigator对象,检查user-agent string来判断浏览器类型
  比较浏览器的版本,版本号通常是0.9.8.8等,为了比较两个版本号,通常这样:
  function compareVersions(sVersion1, sVersion2) {
  
  //先将版本分解成数字数组,
  var aVersion1 = sVersion1.split(“.”);
  var aVersion2 = sVersion2.split(“.”);
  //将两个数组的长度设为一样,
  if (aVersion1.length >aVersion2.length) {
  for (var i=0; i   aVersion2.push(“0”);
  }
  } else if (aVersion1.length   for (var i=0; i   aVersion1.push(“0”);
  }
  }
  //依次比较数组中的数字以确定两个版本的大小关系。
  for (var i=0; i   if (aVersion1[i]   return -1;
  } else if (aVersion1[i] >aVersion2[i]) {
  return 1;
  }
  }
  return 0;
  }
  
  如,
  alert(compareVersions(“0.9.2”, “0.9”)); //returns 1
  alert(compareVersions(“1.13.2”, “1.14”)); //returns –1
  alert(compareVersions(“5.5”, “5.5”)); //returns 0
  
  然后的内容是根据不同的浏览器类型来进行检测,详见各网络资源。
  2. 利用浏览器的object/feature来检测
  通过检查object是否存在来,来反映浏览器具有的feature。比如,
  if (document.getElementById) {
  //the method exists, so use it here
  } else {
  //do something else
  }
  
  平台和操作系统的检测
  方法:
  先判断平台(win,unix,mac),之后是操作系统的信息(win和unix都有版本信息,而macintosh没有,能提供的信息跟处理器等硬件有关)。
  通常只要平台信息就可以了,但是有时更多的操作系统信息也是有用的。
  1. 利用navigator.platform判断平台
  因为不同的浏览器platform对相同的平台返回的不同,(For instance, IE and
  Netscape Communicator return “Win32” for Windows 32-bit systems, “Mac68k” or “MacPPC” (depending
  on the processor) for Macintosh systems. It returns the actual name of the operating system for Unix
  systems. On the other hand, Mozilla returns “Windows” for all Windows systems, “Macintosh” for all
  Macintosh systems, and “X11” for all Unix systems.)又对unix,返回的字符串可能会很不一样,因此,如下来判断三种平台:
  
  var isWin = (navigator.platform == “Win32”) || (navigator.platform == “Windows”);
  var isMac = (navigator.platform == “Mac68K”) || (navigator.platform == “MacPPC”)
  || (navigator.platform == “Macintosh”);
  var isUnix = (navigator.platform == “X11”) &&!isWin &&!isMac;
  2. 操作系统:
  对windows,
  90f1140c52f34824b258ada04bc75238.png
  
  对macintosh,浏览器不会告诉具有的操作系统版本,只会给出是否是68000处理器,或powerpc处理器,
  400e7f4efb7248029246b29a1e30f5fb.png
  
  对unix系统,一方面很简单,一方面很复杂。
  复杂是因为unix平台的操作系统很多,
  简单是因为,所有的浏览器在user-agent string都包括进一个字符串,该字符串是unix命令行uname –sm的结果,因此对所有浏览器来说都是
  一样的。
  
  下面是浏览器类型,平台和操作系统检测的js,注意检测的步骤的顺序是很重要的。
  另外,在www.koders.com上面可以搜到另外一些具有相同用处的js,
  
  var sUserAgent = navigator.userAgent;
  var fAppVersion = parseFloat(navigator.appVersion);
  function compareVersions(sVersion1, sVersion2) {
  var aVersion1 = sVersion1.split(“.”);
  var aVersion2 = sVersion2.split(“.”);
  if (aVersion1.length >aVersion2.length) {
  for (var i=0; i   aVersion2.push(“0”);
  }
  } else if (aVersion1.length   for (var i=0; i   aVersion1.push(“0”);
  }
  }
  for (var i=0; i   if (aVersion1[i]   return -1;
  } else if (aVersion1[i] >aVersion2[i]) {
  return 1;
  }
  }
  return 0;
  }
  var isOpera = sUserAgent.indexOf(“Opera”) >-1;
  var isMinOpera4 = isMinOpera5 = isMinOpera6 = isMinOpera7 = isMinOpera7_5 = false;
  if (isOpera) {
  var fOperaVersion;
  if(navigator.appName == “Opera”) {
  fOperaVersion = fAppVersion;
  } else {
  var reOperaVersion = new RegExp(“Opera (//d+//.//d+)”);
  reOperaVersion.test(sUserAgent);
  fOperaVersion = parseFloat(RegExp[“$1”]);
  }
  isMinOpera4 = fOperaVersion >= 4;
  isMinOpera5 = fOperaVersion >= 5;
  isMinOpera6 = fOperaVersion >= 6;
  isMinOpera7 = fOperaVersion >= 7;
  isMinOpera7_5 = fOperaVersion >= 7.5;
  }
  var isKHTML = sUserAgent.indexOf(“KHTML”) >-1
  || sUserAgent.indexOf(“Konqueror”) >-1
  || sUserAgent.indexOf(“AppleWebKit”) >-1;
  var isMinSafari1 = isMinSafari1_2 = false;
  var isMinKonq2_2 = isMinKonq3 = isMinKonq3_1 = isMinKonq3_2 = false;
  if (isKHTML) {
  isSafari = sUserAgent.indexOf(“AppleWebKit”) >-1;
  isKonq = sUserAgent.indexOf(“Konqueror”) >-1;
  if (isSafari) {
  var reAppleWebKit = new RegExp(“AppleWebKit///(//d+(?://.//d*)?)”);
  reAppleWebKit.test(sUserAgent);
  var fAppleWebKitVersion = parseFloat(RegExp[“$1”]);
  isMinSafari1 = fAppleWebKitVersion >= 85;
  isMinSafari1_2 = fAppleWebKitVersion >= 124;
  } else if (isKonq) {
  var reKonq = new RegExp(“Konqueror///(//d+(?://.//d+(?://.//d)?)?)”);
  reKonq.test(sUserAgent);
  isMinKonq2_2 = compareVersions(RegExp[“$1”], “2.2”) >= 0;
  isMinKonq3 = compareVersions(RegExp[“$1”], “3.0”) >= 0;
  isMinKonq3_1 = compareVersions(RegExp[“$1”], “3.1”) >= 0;
  isMinKonq3_2 = compareVersions(RegExp[“$1”], “3.2”) >= 0;
  }
  }
  var isIE = sUserAgent.indexOf(“compatible”) >-1
  &&sUserAgent.indexOf(“MSIE”) >-1
  &&!isOpera;
  var isMinIE4 = isMinIE5 = isMinIE5_5 = isMinIE6 = false;
  if (isIE) {
  var reIE = new RegExp(“MSIE (//d+//.//d+);”);
  reIE.test(sUserAgent);
  var fIEVersion = parseFloat(RegExp[“$1”]);
  isMinIE4 = fIEVersion >= 4;
  isMinIE5 = fIEVersion >= 5;
  isMinIE5_5 = fIEVersion >= 5.5;
  isMinIE6 = fIEVersion >= 6.0;
  }
  var isMoz = sUserAgent.indexOf(“Gecko”) >-1
  &&!isKHTML;
  var isMinMoz1 = sMinMoz1_4 = isMinMoz1_5 = false;
  if (isMoz) {
  var reMoz = new RegExp(“rv:(//d+//.//d+(?://.//d+)?)”);
  reMoz.test(sUserAgent);
  isMinMoz1 = compareVersions(RegExp[“$1”], “1.0”) >= 0;
  isMinMoz1_4 = compareVersions(RegExp[“$1”], “1.4”) >= 0;
  isMinMoz1_5 = compareVersions(RegExp[“$1”], “1.5”) >= 0;
  }
  var isNS4 = !isIE &&!isOpera &&!isMoz &&!isKHTML
  &&(sUserAgent.indexOf(“Mozilla”) == 0)
  &&(navigator.appName == “Netscape”)
  &&(fAppVersion >= 4.0 &&fAppVersion <5.0);
  var isMinNS4 = isMinNS4_5 = isMinNS4_7 = isMinNS4_8 = false;
  if (isNS4) {
  isMinNS4 = true;
  isMinNS4_5 = fAppVersion >= 4.5;
  isMinNS4_7 = fAppVersion >= 4.7;
  isMinNS4_8 = fAppVersion >= 4.8;
  }
  var isWin = (navigator.platform == “Win32”) || (navigator.platform == “Windows”);
  var isMac = (navigator.platform == “Mac68K”) || (navigator.platform == “MacPPC”)
  || (navigator.platform == “Macintosh”);
  var isUnix = (navigator.platform == “X11”) &&!isWin &&!isMac;
  var isWin95 = isWin98 = isWinNT4 = isWin2K = isWinME = isWinXP = false;
  var isMac68K = isMacPPC = false;
  var isSunOS = isMinSunOS4 = isMinSunOS5 = isMinSunOS5_5 = false;
  if (isWin) {
  isWin95 = sUserAgent.indexOf(“Win95”) >-1
  || sUserAgent.indexOf(“Windows 95”) >-1;
  isWin98 = sUserAgent.indexOf(“Win98”) >-1
  || sUserAgent.indexOf(“Windows 98”) >-1;
  isWinME = sUserAgent.indexOf(“Win 9x 4.90”) >-1
  || sUserAgent.indexOf(“Windows ME”) >-1;
  isWin2K = sUserAgent.indexOf(“Windows NT 5.0”) >-1
  || sUserAgent.indexOf(“Windows 2000”) >-1;
  isWinXP = sUserAgent.indexOf(“Windows NT 5.1”) >-1
  || sUserAgent.indexOf(“Windows XP”) >-1;
  isWinNT4 = sUserAgent.indexOf(“WinNT”) >-1
  || sUserAgent.indexOf(“Windows NT”) >-1
  || sUserAgent.indexOf(“WinNT4.0”) >-1
  || sUserAgent.indexOf(“Windows NT 4.0”) >-1
  &&(!isWinME &&!isWin2K &&!isWinXP);
  }
  if (isMac) {
  isMac68K = sUserAgent.indexOf(“Mac_68000”) >-1
  || sUserAgent.indexOf(“68K”) >-1;
  isMacPPC = sUserAgent.indexOf(“Mac_PowerPC”) >-1
  || sUserAgent.indexOf(“PPC”) >-1;
  }
  if (isUnix) {
  isSunOS = sUserAgent.indexOf(“SunOS”) >-1;
  if (isSunOS) {
  var reSunOS = new RegExp(“SunOS (//d+//.//d+(?://.//d+)?)”);
  reSunOS.test(sUserAgent);
  isMinSunOS4 = compareVersions(RegExp[“$1”], “4.0”) >= 0;
  isMinSunOS5 = compareVersions(RegExp[“$1”], “5.0”) >= 0;
  isMinSunOS5_5 = compareVersions(RegExp[“$1”], “5.5”) >= 0;
  }
  }
  
  7 事件
  事件流:
  为了让多于一个的元素能够对同一事件响应,提出了event flow的概念。
  事件流有两种方式:
  从下而上的冒泡bubbling,
  从上而下的捕获capturing,
  ie似乎不支持capturing,待考。
  
  指定事件的处理器handler,即处理函数:
  1. 在html中直接指定,格式略。
  2. 在js中指定,
  1. IE,
  [Object].attachEvent(“name_of_event_handler”, fnHandler);
  [Object].detachEvent(“name_of_event_handler”, fnHandler);
  2. DOM
  [Object].addEventListener(“name_of_event”, fnHandler, bCapture);
  [Object].removeEventListener(“name_of_event”, fnHandler, bCapture);
  
  
  利用Event对象来获取事件的信息,
  1. IE中,用window.event来获得该对象,
  2. DOM中,event对象是事件处理函数的第一个参数,用arguments[0]来获得。
  这些信息包括:
  哪些键被按下,
  事件发生的坐标,
  作为事件源的元素,
  事件类型,
  等。
  
  事件的类型:
  mouse,
  鼠标事件的顺序问题:
  1. mousedown
  2. mouseup
  3. click
  4. mousedown
  5. mouseup
  6. click
  7. dblclick
  注意:当一个按钮被focus,此时按enter也是一个click事件。
  keyboard,
  键盘事件的顺序:
  对字符键
  1. keydown
  2. keypress
  3. keyup
  对非字符键
  1. keydown
  2. keyup
  如果按着字符键不放,keydown,keypress,就会不断被激发。
  如果按照非字符键不放,keydow会不断被激发。
  html,
  load,unload,
  abort,
  error,
  select:文本输入框或区里面的文字被选中,
  reset,
  submit,
  change:input,textarea,select元素中的文本变化,
  resize,
  scroll,
  focus,blur
  mutation:DOM变化引起,
  DOMNodeInserted,
  DOMNodeRemoved,
  DOMNodeRemovedFromDocument
  DOMNodeInsertedIntoDocument,
  DOMSubtreeModified
  
  因为IE和DOM在对事件对象的操作上不一致,
  1. 尤其是在增加和减少事件处理器时。所以创建一个工具对象,来减轻这个问题。
  var EventUtil= new Object;
  EventUtil.addEventHandler= function (oTarget, sEventType, fnHandler) {
  if (oTarget.addEventListener) { //for DOM-compliant browsers
  oTarget.addEventListener(sEventType, fnHandler, false);
  } else if (oTarget.attachEvent) { //for IE
  oTarget.attachEvent(“on” + sEventType, fnHandler);
  } else { //for all others
  oTarget[“on” + sEventType] = fnHandler;
  }
  };
  EventUtil.removeEventHandler= function (oTarget, sEventType, fnHandler) {
  if (oTarget.removeEventListener) { //for DOM-compliant browsers
  oTarget.removeEventListener(sEventType, fnHandler, false);
  } else if (oTarget.detachEvent) { //for IE
  oTarget.detachEvent(“on” + sEventType, fnHandler);
  } else { //for all others
  oTarget[“on” + sEventType] = null;
  }
  };
  2. 另外,在拥有的属性和方法上,也不一致,如下:
  a6b985994ac54b658e1bbbb72d24bbe6.png
  
  c519ebb8771e487fb4004496eefa3ab3.png
  
  a1f1f1178b4c4bdcbab967ad8cc416a2.png
  
  因此,改进如下:
  EventUtil.formatEvent = function (oEvent) {
  if (isIE &&isWin) {
  }
  return oEvent;
  };
  3. IE和DOM获取事件对象的方式不一样
  EventUtil.getEvent = function() {
  if (window.event) {
  return this.formatEvent(window.event);
  } else {
  return EventUtil.getEvent.caller.arguments[0];
  }
  };
  
  评论:没有评论。
  发表评论
  姓 名:
  主 页:
  校验码: 看不清,换一张
  
  登录

本文转自
http://blog.csdn.net/yethyeth/archive/2007/04/14/1564936.aspx
阅读更多
个人分类: JS+CSS+Dhtml
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭