第1章 JavaScript概述javascript
第2章 使用JavaScriptphp
第3章 语法、关键保留字及变量css
第4章 数据类型html
第5章 运算符前端
第6章 流程控制语句java
第7章 函数node
//没有参数的函数 function box(){ alert('只有函数被调用,我才会被执行'); } box(); //带参数的函数 function box(name, age){ alert('你的姓名:'+name+',年龄:'+age); } box('小明',25); //带return 返回值 function box(){ return '我被返回了!'; } alert(box()); function box(name, age){ return '你的名字:'+name+',年龄:'+age; } alert('小明',28); //把函数的返回值赋给一个变量,而后经过变量进行操做。 function box(num1, num2){ return num1*num2; } var num = box(10,5); alert(num); //退出当前函数 function box(num){ if(num<5) return num; return 100; } alert(box(10)); //arguments对象 function box(){ return arguments[0]+'|'+arguments[1]; } alert(box(1,2,3,4,5,6)); //arguments对象的length属性能够获得参数的数量。 function box(){ return arguments.length; } alert(box(1,2,3,4,5,6)); //实现一个加法运算,将全部传进来的数字累加,而数字的个数又不肯定。 function box(){ var sum = 0; if(arguments.length ==0) return sum; for (var i =0; i <=arguments.length; i++) { sum = sum + arguments[i]; } return sum; } alert(box(5,9,12)); //ECMAScript中的函数,没有像其余高级语言那种函数重载功能。 function box(num){ return num +100; } function box(num) { return num+200; } alert(box(50));
第8章 对象和数组程序员
//object类型 //使用new运算符建立Object var box = new Object(); box.name = '小明'; box.age = 28; //new关键字能够省略 var box = object(); //使用字面量方式建立Object var box = { name : '小明'; age:28 }; //属性字段也能够使用字符串星矢 var box = { 'name' : '小明'; 'age' : 28; }; //使用字面量及传统复制方式 var box = {}; box.name = '小明'; box.age =28; //两种属性输出方式 alert(box.age); alert(box['age']); //PS:在使用字面量声明Object对象时,不会调用Object()构造函数(Firefox除外)。 //给对象建立方法 var box = { run : function(){ return '运行'; } } alert(box.run()); //使用delete删除对象属性 delete box.name; //字面量也是向函数传递大量可选参数的首选方式。 function box(obj){ if(obj.name != undefined) alert(obj.name); if(obj.age != undefined) alert(obj.age); } box({ name : '小明'; age :25 }); /*Array类型*/ //使用new关键字建立数组 var box = new Array(); var box = new Array(10); var box = new Array('小明',28,'学生','中国'); //以上三种方法,能够省略new关键字 var box = Array(); //使用字面量方式建立数组 var box = []; var box = ['小明',28,'学生','中国']; var box = [1,2,]//禁止这么作,IE会识别3个元素 var box = [,,,,,,];//一样,IE的会有识别问题 //PS:和Object同样,字面量的写法不会调用Array()构造函数。(Firefox除外)。 //使用索引下标来读取数组的值 alert(box[2]); box[2] = '学生'; box[4] = '计算机'; //使用length属性获取数组元素量 alert(box.length); box.length = 10; box[box.length] = 'JS技术';//经过length给数组增长一个元素 //建立一个稍微复杂一点的数组 var box = [ //第一个元素是一个对象 { name : '小明', age :28, run: function(){ return 'runing'; } }, //第二个元素是数组 ['马云','李彦宏', newe Object()], '江苏', //第三个元素是字符串 25+25, //第四个元素是数值 newArray(1,2,3) //第五个元素是数组 ]; alert(box); //PS:数组最多可包含4294967295个元素,超出即会发生异常。 /*对象中方法*/ //转换方法 对象或数组都具备toLocaleString()、toString()和valueOf()方法。 var box=['小明',28,'计算机编程']; //字面量数组 alert(box); //隐式调用了toString() alert(box.toString()); //和valueOf()返回一致 alert(box.toLocaleString()); //返回值和上面两种一致 //默认状况下,数组字符串都会以逗号隔开。若是使用join()方法,则能够使用不一样的分隔符来构建这个字符串。 var box=['小明', 28,'计算机编程']; alert(box.join('|')); //小明|28|计算机编程 //栈方法 //ECMAScript为数组专门提供了push()和pop()方法。 /*push()方法能够接收任意数量的参数,把它们逐个添加到数组的末尾,并返回修改后数 组的长度。而pop()方法则从数组末尾移除最后一个元素,减小数组的length值,而后返回 移除的元素。*/ var box=['小明', 28,'计算机编程']; //字面量声明 alert(box.push('中国')); //数组末尾添加一个元素,而且返回长度 alert(box); //查看数组 box.pop(); //移除数组末尾元素,并返回移除的元素 alert(box); //查看元素 //队列方法 /*栈方法是后进先出,而列队方法就是先进先出。列队在数组的末端添加元素,从数组的 前端移除元素。经过push()向数组末端添加一个元素,而后经过shift()方法从数组前端移除 一个元素。*/ var box=['小明', 28,'计算机编程']; //字面量声明 alert(box.push('中国')); //数组末尾添加一个元素,而且返回长度 alert(box); //查看数组 alert(box.shift()); //移除数组开头元素,并返回移除的元素 alert(box); //查看数组 /*ECMAScript还为数组提供了一个unshift()方法,它和shift()方法的功能彻底相反。 unshift()方法为数组的前端添加一个元素。*/ var box=['小明', 28,'计算机编程']; //字面量声明 alert(box.unshift('中国','江苏')); //数组开头添加两个元素 alert(box); //查看数组 alert(box.pop()); //移除数组末尾元素,并返回移除的元素 alert(box); //查看数组 //PS:IE浏览器对unshift()方法老是返回undefined而不是数组的新长度。 //重排序方法 //数组中已经存在两个能够直接用来排序的方法:reverse()和sort()。 //reverse() 逆向排序 var box=[1,2,3,4,5]; //数组 alert(box.reverse()); //逆向排序方法,返回排序后的数组 alert(box); //源数组也被逆向排序了,说明是引用 //sort() 从小到大排序 var box=[4,1,7,3,9,2]; //数组 alert(box.sort()); //从小到大排序,返回排序后的数组 alert(box); //源数组也被从小到大排序了 //操做方法 /*ECMAScript为操做已经包含在数组中的元素提供了不少方法。concat()方法能够基于当 前数组建立一个新数组。slice()方法能够基于当前数组获取指定区域元素并建立一个新数组。 splice()主要用途是向数组的中部插入元素。*/ var box=['小明', 28,'中国']; //当前数组 var box2=box.concat('计算机编程'); //建立新数组,并添加新元素 alert(box2); //输出新数组 alert(box); //当前数组没有任何变化 var box=['小明', 28,'中国']; //当前数组 var box2=box.slice(1); //box.slice(1,3),2-4之间的元素 alert(box2); //28,中国 alert(box); //当前数组 //splice中的删除功能: var box=['小明', 28,'中国']; //当前数组 var box2=box.splice(0,2); //截取前两个元素 alert(box2); //返回截取的元素 alert(box); //当前数组被截取的元素被删除 //splice中的插入功能: var box=['小明', 28,'中国']; //当前数组 var box2=box.splice(1,0,'计算机编程','江苏'); //没有截取,但插入了两条 alert(box2); //在第2个位置插入两条 alert(box); //输出 //splice中的替换功能: var box=['小明', 28,'中国']; //当前数组 var box2=box.splice(1,1,100); //截取了第2条,替换成100 alert(box2); //输出截取的28 alert(box); //输出数组
第9章 时间与日期ajax
//建立一个日期对象,使用new运算符和Date构造方法(构造函数)便可 var box = new Date(); //在调用Date构造方法而不传递参数的状况下,新建的对象自动获取当前的时间和日期。 alert(box); //不一样浏览器显示不一样 //ECMAScript提供了两个方法,Date.parse()和Date.UTC()。 alert(Date.parse('6/13/2011')); //1307894400000 //若是想输出指定的日期,那么把Date.parse()传入Date构造方法里。 var box = new Date(Date.parse('6/13/2011'));//MonJun13201100:00:00GMT+0800 var box = new Date('6/13/2011'); //直接传入,Date.parse()后台被调用 //PS:Date对象及其在不一样浏览器中的实现有许多奇怪的行为。 //Date.UTC()方法一样也返回表示日期的毫秒数,但它与Date.parse()在构建值时使用不一样的信息。 alert(Date.UTC(2011,11)); //1322697600000 //若是Date.UTC()参数传递错误,那么就会出现负值或者NaN等非法信息。 alert(Date.UTC()); //负值或者NaN //若是要输出指定日期,那么直接把Date.UTC()传入Date构造方法里便可。 var box = new Date(Date.UTC(2011,11,5,15,13,16)); /*通用的方法*/ //与其余类型同样,Date类型也重写了toLocaleString()、toString()和valueOf()方法;但这 //些方法返回值与其余类型中的方法不一样。 var box = new Date(Date.UTC(2011,11,5,15,13,16)); alert('toString:'+box.toString()); alert('toLocaleString:'+box.toLocaleString()); //按本地格式输出 //PS:这两个方法在不一样浏览器显示的效果又不同,但不用担忧,这两个方法只是在 //调试比较有用,在显示时间和日期上,没什么价值。valueOf()方法显示毫秒数。 //日期格式化方法 var box = new Date(); alert(box.toDateString()); //以特定的格式显示星期几、月、日和年 alert(box.toTimeString()); //以特定的格式显示时、分、秒和时区 alert(box.toLocaleDateString()); //以特定地区格式显示星期几、月、日和年 alert(box.toLocaleTimeString()); //以特定地区格式显示时、分、秒和时区 alert(box.toUTCString()); //以特定的格式显示完整的UTC日期。 //组件方法 /*组件方法,是为咱们单独获取你想要的各类时间/日期而提供的方法。须要注意的时候, 这些方法中,有带UTC的,有不带UTC的。UTC日期指的是在没有时区误差的状况下的 日期值。*/ alert(box.getTime()); //获取日期的毫秒数,和valueOf()返回一致 alert(box.setTime(100)); //以毫秒数设置日期,会改变整个日期 alert(box.getFullYear()); //获取四位年份 alert(box.setFullYear(2012)); //设置四位年份,返回的是毫秒数 alert(box.getMonth()); //获取月份,没指定月份,从0开始算起 alert(box.setMonth(11)); //设置月份 alert(box.getDate()); //获取日期 alert(box.setDate(8)); //设置日期,返回毫秒数 alert(box.getDay()); //返回星期几,0表示星期日,6表示星期六 alert(box.setDay(2)); //设置星期几 alert(box.getHours()); //返回时 alert(box.setHours(12)); //设置时 alert(box.getMinutes()); //返回分钟 alert(box.setMinutes(22)); //设置分钟 alert(box.getSeconds()); //返回秒数 alert(box.setSeconds(44)); //设置秒数 alert(box.getMilliseconds()); //返回毫秒数 alert(box.setMilliseconds()); //设置毫秒数 alert(box.getTimezoneOffset()); //返回本地时间和UTC时间相差的分钟数 //PS:以上方法除了getTimezoneOffset(),其余都具备UTC功能,例如setDate()及getDate() //获取星期几,那么就会有setUTCDate()及getUTCDate()。表示世界协调时间。
第10章 正则表达式正则表达式
第11章 Function类型
//使用Function构造函数 var box = new Function ('num1','num2', 'return num1+num2'); //不推荐 //做为值的函数,ECMAScript中的函数名自己就是变量,因此函数也能够做为值来使用。 Function box(sumFunction, num) { return sumFunction(num); } Function sum(num) { return num +10; } var result = box(sum ,10); //函数内部属性 /*在函数内部,有两个特殊的对象:arguments和this。arguments是一个类数组对象,包 含着传入函数中的全部参数,主要用途是保存函数参数。但这个对象还有一个名叫callee的 属性,该属性是一个指针,指向拥有这个arguments对象的函数。*/ Function box(num) { if(num <=1){ return 1; }else { return num*box(num-1); //一个简单的的递归 } } /*对于阶乘函数通常要用到递归算法,因此函数内部必定会调用自身;若是函数名不改变 是没有问题的,但一旦改变函数名,内部的自身调用须要逐一修改。为了解决这个问题,我 们能够使用arguments.callee来代替。*/ Function box(num) { if(num <=1){ return 1; }else{ return num * arguments.callee(num-1);//使用callee来执行自身 } } /*函数内部另外一个特殊对象是this,其行为与Java和C#中的this大体类似。换句话说, this引用的是函数据以执行操做的对象,或者说函数调用语句所处的那个做用域。PS:当在 全局做用域中调用函数时,this对象引用的就是window。*/ //便于理解的改写例子 window.color = '红色的'; //全局的,或者varcolor='红色的';也行 alert(this.color); //打印全局的color var box = { color:'蓝色的', //局部的color sayColor:function(){ alert(this.color); //此时的this只能box里的color } }; box.sayColor(); //打印局部的color alert(this.color); //仍是全局的 //函数属性和方法 /*ECMAScript中的函数是对象,所以函数也有属性和方法。每一个函数都包含两个属性: length和prototype。其中,length属性表示函数但愿接收的命名参数的个数。*/ function box(name, age){ alert(name + age); } alert(box.length); //对于prototype属性,它是保存全部实例方法的真正所在,也就是原型。 /*prototype下有两个方法:apply()和call(),每一个函数都 包含这两个非继承而来的方法。这两个方法的用途都在特定的做用域中调用函数,实际上等 于设置函数体内this对象的值。*/ Function box(num1 ,num2){ return num1 + num2; } function sayBox(num1, num2){ return box.apply(this,[num1, num2]);//this表示做用域,这里是window //[]表示box所须要的参数 } function sayBox2(num1, num2){ return box.apply(this, arguments); //arguments对象表示box所须要的参数 } alert(sayBox(10,10)); //20 alert(sayBox2(10,10)); //20 /*call()方法于apply()方法相同,他们的区别仅仅在于接收参数的方式不一样。对于call()方 法而言,第一个参数是做用域,没有变化,变化只是其他的参数都是直接传递给函数的。*/ function box(num1, num2){ return num1 + num2; } function callBox(num1, num2){ return box.call(this, num1, num2); //和apply区别在于后面的传参 } alert(callBox(10,10)); /*事实上,传递参数并非apply()和call()方法真正的用武之地;它们常用的地方是 可以扩展函数赖以运行的做用域。*/ var color = '红色的'; //或者window.color='红色的';也行 var box - { color :'蓝色的' }; function sayColor(){ alert(this.color); } sayColor(); //做用域在window sayColor.call(this); //做用域在window sayColor.call(window); //做用域在window sayColor.call(box); //做用域在box,对象冒充 /*这个例子是以前做用域理解的例子修改而成,咱们能够发现当咱们使用call(box)方法的 时候,sayColor()方法的运行环境已经变成了box对象里了。 使用call()或者apply()来扩充做用域的最大好处,就是对象不须要与方法发生任何耦合 关系(耦合,就是互相关联的意思,扩展和维护会发生连锁反应)。也就是说,box对象和 sayColor()方法之间不会有多余的关联操做,好比box.sayColor=sayColor;*/
第12章 变量、做用域及内存
/*在变量复制方面,基本类型和引用类型也有所不一样。基本类型复制的是值自己,而引用 类型复制的是地址。*/ var box='Lee'; //在栈内存生成一个box'Lee' var box2=box; //在栈内存再生成一个box2'Lee' //ps:box2是虽然是box1的一个副本,但从图示能够看出,它是彻底独立的。也就是说,两个变量分别操做时互不影响。 var box = new Object(); //建立一个引用类型 box.name = 'Lee'; //新增一个属性 var box2 = box; //把引用地址赋值给box2 //ps:在引用类型中,box2其实就是box,由于他们指向的是同一个对象。若是这个对象中的 //name属性被修改了,box2.name和box.name输出的值都会被相应修改掉了。 /*传递参数 ECMAScript中全部函数的参数都是按值传递的,言下之意就是说,参数不会按引用传 递,虽然变量有基本类型和引用类型之分。*/ function box(num){ //按值传递,传递的参数是基本类型 num+=10; //这里的num是局部变量,全局无效 retur nnum; } var num=50; var result = box(num); alert(result); //60 alert(num); //50 function box(num){ //按值传递,传递的参数是基本类型 num+=10; //这里的num是局部变量,全局无效 return num; } var num=50; var result=box(num); alert(result); //60 alert(num); //50 /*PS:以上的代码中,传递的参数是一个基本类型的值。而函数里的num是一个局部变 量,和外面的num没有任何联系。*/ //下面给出一个参数做为引用类型的例子。 function box(obj) { //按值传递,传递的参数是引用类型 obj.name = 'Lee'; } var p=new Object(); box(p); alert(p.name); /*PS:若是存在按引用传递的话,那么函数里的那个变量将会是全局变量,在外部也可 以访问。好比PHP中,必须在参数前面加上&符号表示按引用传递。而ECMAScript没有这 些,只能是局部变量。能够在PHP中了解一下。 PS:因此按引用传递和传递引用类型是两个不一样的概念。*/ function box(obj) { obj.name = 'lee'; var obj = new Object(); //函数内部又建立了一个对象 obj.name = 'Mr.'; //并无替换掉原来的obj } //最后得出结论,ECMAScript函数的参数都将是局部变量,也就是说,没有按引用传递。 //检测类型 //要检测一个变量的类型,咱们能够经过typeof运算符来判别。诸如: var box='Lee'; alert(typeofbox); //string //这时咱们应该采用instanceof运算符来查看。 var box=[1,2,3]; alert(box instanceof Array); //是不是数组 var box2={}; alert(box2 instanceof Object); //是不是对象 var box3=/g/; alert(box3 instanceof RegExp); //是不是正则表达式 var box4=newString('Lee'); alert(box4 instanceof String); //是不是字符串对象 //PS:当使用instanceof检查基本类型的值时,它会返回false。 //5.执行环境及做用域 //在Web浏览器中,全局执行环境被认为是window对象。 var box='blue'; //声明一个全局变量 function setBox(){ alert(box); //全局变量能够在函数里访问 } setBox(); //执行函数 //全局的变量和函数,都是window对象的属性和方法。 var box='blue'; function setBox(){ alert(window.box); //全局变量即window的属性 } window.setBox(); //全局函数即window的方法 /*PS:当执行环境中的全部代码执行完毕后,该环境被销毁,保存在其中的全部变量和 函数定义也随之销毁。若是是全局环境下,须要程序执行完毕,或者网页被关闭才会销毁。 PS:每一个执行环境都有一个与之关联的变量对象,就比如全局的window能够调用变量 和属性同样。局部的环境也有一个相似window的变量对象,环境中定义的全部变量和函数 都保存在这个对象中。(咱们没法访问这个变量对象,但解析器会处理数据时后台使用它)*/ //函数里的局部做用域里的变量替换全局变量,但做用域仅限在函数体内这个局部环境。 var box='blue'; function setBox(){ varbox='red'; //这里是局部变量,出来就不认识了 alert(box); } setBox(); alert(box); //经过传参,能够替换函数体内的局部变量,但做用域仅限在函数体内这个局部环境。 var box='blue'; function setBox(box){ //经过传参,替换了全局变量 alert(box); } setBox('red'); alert(box); //函数体内还包含着函数,只有这个函数才能够访问内一层的函数。 var box='blue'; function setBox(){ function setColor(){ var b='orange'; alert(box); alert(b); } setColor(); //setColor()的执行环境在setBox()内 } setBox(); /*PS:每一个函数被调用时都会建立本身的执行环境。当执行到这个函数时,函数的环境 就会被推到环境栈中去执行,而执行后又在环境栈中弹出(退出),把控制权交给上一级的执 行环境。 PS:当代码在一个环境中执行时,就会造成一种叫作做用域链的东西。它的用途是保 证对执行环境中有访问权限的变量和函数进行有序访问。做用域链的前端,就是执行环境的 变量对象。*/ //6.没有块级做用域 //块级做用域表示诸如if语句等有花括号封闭的代码块,因此,支持条件判断来定义变量。 if(true){ //if语句代码块没有局部做用域 var box='Lee'; } alert(box); //for循环语句也是如此 for(var i=0;i<10;i++){ //没有局部做用域 var box='Lee'; } alert(i); alert(box); //var关键字在函数里的区别 function box(num1,num2){ var sum=num1+num2; //若是去掉var就是全局变量了 return sum; } alert(box(10,10)); alert(sum); //报错 /*PS:很是不建议不使用var就初始化变量,由于这种方法会致使各类意外发生。因此初 始化变量的时候必定要加上var。*/ //通常肯定变量都是经过搜索来肯定该标识符实际表明什么。 var box='blue'; function getBox(){ return box; //表明全局box } //若是加上函数体内加上varbox='red' alert(getBox()); //那么最后返回值就是red //PS:变量查询中,访问局部变量要比全局变量更快,由于不须要向上搜索做用域链。 //一旦数据再也不有用,那么将其设置为null来释放引用,这个作法叫作解除引用。 //这一作法适用于大多数全局变量和全局对象。 var o={ name:'Lee' }; o=null; //解除对象引用,等待垃圾收集器回收
第13章 基本包装类型
/*为了便于操做基本类型值,ECMAScript提供了3个特殊的引用类型:Boolean、Number 和String。这些类型与其余引用类型类似,但同时也具备与各自的基本类型相应的特殊行为。*/ //基本包装类型概述 var box='Mr. Lee'; //定义一个字符串 var box2=box.substring(2); //截掉字符串前两位 alert(box2); //输出新字符串 /*变量box是一个字符串类型,而box.substring(2)又说明它是一个对象(PS:只有对象才 会调用方法),最后把处理结果赋值给box2。'Mr. Lee'是一个字符串类型的值,按道理它不 应该是对象,不该该会有本身的方法,好比:*/ alert('Mr.Lee'.substring(2)); //直接经过值来调用方法 //字面量写法: var box = 'Mr.Lee'; //字面量 box.name = 'Lee'; //无效属性 box.age = function(){ //无效方法 return 100; }; alert(box); //Mr.Lee alert(box.substring(2)); //.Lee alert(typeof box); //string alert(box.name); //undefined alert(box.age()); //错误 //new运算符写法 var box=new String('Mr.Lee'); //new运算符 box.name='Lee'; //有效属性 box.age=function(){ //有效方法 return 100; }; alert(box); //Mr.Lee alert(box.substring(2)); //.Lee alert(typeof box); //object alert(box.name); //Lee alert(box.age()); //100 /*无论字面量形式仍是new运算符形式,均可以使用它的内置方法。而且 Boolean和Number特性与String相同,三种类型能够成为基本包装类型。*/ /*PS:在使用new运算符建立以上三种类型的对象时,能够给本身添加属性和方法,但 咱们建议不要这样使用,由于这样会致使根本分不清究竟是基本类型值仍是引用类型值。*/ //Boolean类型 //Boolean类型没有特定的属性或者方法。 //Number类型 //Number类型有一些静态属性(直接经过Number调用的属性,而无须new运算符)和方法。 /* Number静态属性: MAX_VALUE:表示最大数 MIN_VALUE:表示最小值 NaN:非数值 NEGATIVE_INFINITY:负无穷大,溢出返回该值 POSITIVE_INFINITY:无穷大,溢出返回该值 prototype:原型,用于增长新属性和方法 */ /* 方法: toString(): 将数值转化为字符串,而且能够转换进制 toLocaleString() ::根据本地数字格式转换为字符串 toFixed() :将数字保留小数点后指定位数并转化为字符串 toExponential():将数字以指数形式表示,保留小数点后指定位数并转化为字符串 toPrecision():指数形式或点形式表述数,保留小数点后面指定位数并转化为字符串 */ var box=1000.789; alert(box.toString()); //转换为字符串,传参能够转换进制 alert(box.toLocaleString()); //本地形式,1,000.789 alert(box.toFixed(2)); //小数点保留,1000.78 alert(box.toExponential()); //指数形式,传参会保留小数点 alert(box.toPrecision(3)); //指数或点形式,传参保留小数点 //String类型 //String类型包含了三个属性和大量的可用内置方法。 /* String对象属性 length :返回字符串的字符长度 constructor :返回建立String对象的函数 prototype: 经过添加属性和方法扩展字符串定义 */ /* String也包含对象的通用方法,好比valueOf()、toLocaleString()和toString()方法,但这 些方法都返回字符串的基本值。 */ /* 字符方法 charAt(n) 返回指定索引位置的字符 charCodeAt(n) 以Unicode编码形式返回指定索引位置的字符 */ var box='Mr.Lee'; alert(box.charAt(1)); //r alert(box.charCodeAt(1)); //114 alert(box[1]); //r,经过数组方式截取 //PS:box[1]在IE浏览器会显示undefined,因此使用时要慎重。 /* 字符串操做方法 concat(str1...str2) 将字符串参数串联到调用该方法的字符串 slice(n,m) 返回字符串n到m之间位置的字符串 substring(n,m) 同上 substr(n,m) 返回字符串n开始的m个字符串 */ va rbox='Mr.Lee'; alert(box.concat('is', ' Teacher', '!')); //Mr.LeeisTeacher! alert(box.slice(3)); //Lee alert(box.slice(3,5)); //Le alert(box.substring(3)); //Lee alert(box.substring(3,5)); //Le alert(box.substr(3)); //Lee alert(box.substr(3,5)); //Lee var box='Mr.Lee'; alert(box.slice(-3)); //Lee,6+(-3)=3位开始 alert(box.substring(-3)); //Mr.Lee 负数返回所有 alert(box.substr(-3)); //Lee,6+(-3)=3位开始 var box='Mr.Lee'; alert(box.slice(3,-1)); //Le6+(-1)=5,(3,5) alert(box.substring(3,-1)); //Mr. 第二参为负,直接转0, //而且方法会把较小的数字提早,(0,3) alert(box.substr(3,-1)); //'' 第二参数为负,直接转0,(3,0) /* PS:IE的JavaScript实如今处理向substr()方法传递负值的状况下存在问题,它会返回 原始字符串,使用时要切记。 */ /* 字符串位置方法 indexOf(str,n) 从n开始搜索的第一个str,并将搜索的索引值返回 lastIndexOf(str,n) 从n开始搜索的最后一个str,并将搜索的索引值返回 */ var box='Mr.Lee isLee'; alert(box.indexOf('L')); //3 alert(box.indexOf('L',5)); //10 alert(box.lastIndexOf('L')); //10 alert(box.lastIndexOf('L',5)); //3,从指定的位置向前搜索 //PS:若是没有找到想要的字符串,则返回-1。 //示例:找出所有的L var box='Mr.Lee isLee'; //包含两个L的字符串 var boxarr=[]; //存放L位置的数组 varpos=box.indexOf('L'); //先获取第一个L的位置 while(pos>-1){ //若是位置大于-1,说明还存在L boxarr.push(pos); //添加到数组 pos=box.indexOf('L',pos+1); //重新赋值pos目前的位置 } alert(boxarr); //输出 /* 大小写转换方法 toLowerCase(str) 将字符串所有转换为小写 toUpperCase(str) 将字符串所有转换为大写 toLocaleLowerCase(str) 将字符串所有转换为小写,而且本地化 toLocaleupperCase(str) 将字符串所有转换为大写,而且本地化 */ var box='Mr.Lee isLee'; alert(box.toLowerCase()); //所有小写 alert(box.toUpperCase()); //所有大写 alert(box.toLocaleLowerCase()); // alert(box.toLocaleUpperCase()); // //PS:只有几种语言(如土耳其语)具备地方特有的大小写本地性,通常来讲,是否本 //地化效果都是一致的。 /* 字符串的模式匹配方法 match(pattern) 返回pattern中的子串或null replace(pattern,replacement) 用replacement替换pattern search(pattern) 返回字符串中pattern开始位置 split(pattern) 返回字符串按指定pattern拆分的数组 */ var box='Mr.Lee isLee'; alert(box.match('L')); //找到L,返回L不然返回null alert(box.search('L')); //找到L的位置,和indexOf类型 alert(box.replace('L','Q')); //把L替换成Q alert(box.split('')); //以空格分割成字符串 /* 其余方法 fromCharCode(ascii) 静态方法,输出Ascii码对应值 localeCompare(str1,str2) 比较两个字符串,并返回相应的值 */ alert(String.fromCharCode(76)); //L,输出Ascii码对应值 /* localeCompare(str1,str2)方法详解:比较两个字符串并返回如下值中的一个; 1.若是字符串在字母表中应该排在字符串参数以前,则返回一个负数。(多数-1) 2.若是字符串等于字符串参数,则返回0。 3.若是字符串在自附表中应该排在字符串参数以后,则返回一个正数。(多数1) */ var box='Lee'; alert(box.localeCompare('apple')); //1 alert(box.localeCompare('Lee')); //0 alert(box.localeCompare('zoo')); //-1 /* HTML方法 anchor(name) <aname="name">str</a> big() <big>str</big> blink() <blink>str</blink> bold() <b>Str</b> fixed() <tt>Str</tt> fontcolor(color) <fontcolor="color">str</font> fontsize(size) <fontsize="size">str</font> link(URL) <ahref="URL">str</a> small() <small>str</small> strike() <strike>str</strike> italics() <i>italics</i> sub() <sub>str</sub> sup() <sup>str</sup> 以上是经过JS生成一个html标签,根据经验,没什么太大用处,作个了解。 */ varbox='Lee'; // alert(box.link('http://www.yc60.com')); //超连接
第14章 内置对象
/*ECMA-262对内置对象的定义是:“由ECMAScript实现提供的、不依赖宿主环境的对 象,这些对象在ECMAScript程序执行以前就已经存在了。”意思就是说,开发人员没必要显 示地实例化内置对象;由于它们已经实例化了。ECMA-262只定义了两个内置对象:Global 和Math。*/ //Global对象 /*Global(全局)对象是ECMAScript中一个特别的对象,由于这个对象是不存在的。在 ECMAScript中不属于任何其余对象的属性和方法,都属于它的属性和方法。因此,事实上, 并不存在全局变量和全局函数;全部在全局做用域定义的变量和函数,都是Global对象的 属性和方法。 PS:由于ECMAScript没有定义怎么调用Global对象,因此,Global.属性或者Global. 方法()都是无效的。(Web浏览器将Global做为window对象的一部分加以实现)*/ //1.URI编码方法 /* URI编码能够对连接进行编码,以便发送给浏览器。它们采用特殊的UTF-8编码替换 全部无效字符,从而让浏览器可以接受和理解。 encodeURI()不会对自己属于URI的特殊字符进行编码,例如冒号、正斜杠、问号和# 号;而encodeURIComponent()则会对它发现的任何非标准字符进行编码 */ var box='//Lee 李'; alert(encodeURI(box)); //只编码了中文 varbox='//Lee李'; alert(encodeURIComponent(box)); //特殊字符和中文编码了 // /PS:由于encodeURIComponent()编码比encodeURI()编码来的更加完全,通常来讲 //encodeURIComponent()使用频率要高一些。 //使用了URI编码事后,还能够进行解码,经过decodeURI()和decodeURIComponent() //来进行解码 var box='//Lee 李'; alert(decodeURI(encodeURI(box))); //还原 varbox='//Lee 李'; alert(decodeURIComponent(encodeURIComponent(box))); //还原 /*PS:URI方法如上所述的四种,用于代替已经被ECMA-262第3版废弃的escape()和 unescape()方法。URI方法可以编码全部的Unicode字符,而原来的只能正确地编码ASCII 字符。因此建议不要再使用escape()和unescape()方法。*/ //eval()方法 /*eval()方法主要担当一个字符串解析器的做用,他只接受一个参数,而这个参数就是要 执行的JavaScript代码的字符串。*/ eval('varbox=100'); //解析了字符串代码 alert(box); eval('alert(100)'); //同上 eval('functionbox(){return123}'); //函数也能够 alert(box()); /*eval()方法的功能很是强大,但也很是危险。所以使用的时候必须极为谨慎。特别是在 用户输入数据的状况下,很是有可能致使程序的安全性,好比代码注入等等。*/ //Global对象属性 //Global对象包含了一些属性:undefined、NaN、Object、Array、Function等等。 alert(Array); //返回构造函数 //window对象 //以前已经说明,Global没有办法直接访问,而Web浏览器能够使用window对象来实现一全局访问。 alert(window.Array); //同上 /*-----------------------------------------------------------------------------*/ //Math对象 //ECMAScript还为保存数学公式和信息提供了一个对象,即Math对象。 /* 1.Math对象的属性 Math对象包含的属性大都是数学计算中可能会用到的一些特殊值。 Math.E 天然对数的底数,即常量e的值 Math.LN10 10的天然对数 Math.LN2 2的天然对数 Math.LOG2E 以2为底e的对数 Math.LOG10E 以10为底e的对数 Math.PI ∏的值 Math.SQRT1_2 1/2的平方根 Math.SQRT2 2的平方根 */ alert(Math.E); alert(Math.LN10); alert(Math.LN2); alert(Math.LOG2E); alert(Math.LOG10E); alert(Math.PI); alert(Math.SQRT1_2); alert(Math.SQRT2); //min()和max()方法 //Math.min()用于肯定一组数值中的最小值。Math.max()用于肯定一组数值中的最大值。 alert(Math.min(2,4,3,6,3,8,0,1,3)); //最小值 alert(Math.max(4,7,8,3,1,9,6,0,3,2)); //最大值 //舍入方法 /* Math.ceil()执行向上舍入,即它老是将数值向上舍入为最接近的整数; Math.floor()执行向下舍入,即它老是将数值向下舍入为最接近的整数; Math.round()执行标准舍入,即它老是将数值四舍五入为最接近的整数; */ alert(Math.ceil(25.9)); //26 alert(Math.ceil(25.5)); //26 alert(Math.ceil(25.1)); //26 alert(Math.floor(25.9)); //25 alert(Math.floor(25.5)); //25 alert(Math.floor(25.1)); //25 alert(Math.round(25.9)); //26 alert(Math.round(25.5)); //26 alert(Math.round(25.1)); //25 //random()方法 /* Math.random()方法返回介于0到1之间一个随机数,不包括0和1。若是想大于这个范 围的话,能够套用一下公式: 值=Math.floor(Math.random()*总数+第一个值) */ alert(Math.floor(Math.random()*10+1)); //随机产生1-10之间的任意数 for(var i=0;i<10;i++){ document.write(Math.floor(Math.random()*10+5)); //5-14之间的任意数 document.write('<br/>'); } //为了更加方便的传递想要范围,能够写成函数: function selectFrom(lower,upper){ varsum=upper-lower+1; //总数-第一个数+1 returnMath.floor(Math.random()*sum+lower); } for(var i=0;i<10;i++){ document.write(selectFrom(5,10)); //直接传递范围便可 document.write('<br/>'); } /* 其余方法 Math.abs(num) 返回num的绝对值 Math.exp(num) 返回Math.E的num次幂 Math.log(num) 返回num的天然对数 Math.pow(num,power) 返回num的power次幂 Math.sqrt(num) 返回num的平方根 Math.acos(x) 返回x的反余弦值 Math.asin(x) 返回x的反正弦值 Math.atan(x) 返回x的反正切值 Math.atan2(y,x) 返回y/x的反正切值 Math.cos(x) 返回x的余弦值 Math.sin(x) 返回x的正弦值 Math.tan(x) 返回x的正切值 */
第15章 面向对象与原型
//建立对象 var box=new Object(); //建立一个Object对象 box.name='Lee'; //建立一个name属性并赋值 box.age=100; //建立一个age属性并赋值 box.run=function(){ //建立一个run()方法并返回值 return this.name+this.age+'运行中...'; }; alert(box.run()); //输出属性和方法的值 /*上面建立了一个对象,而且建立属性和方法,在run()方法里的this,就是表明box对象 自己。*/ var box2=box; //获得box的引用 box2.name='Jack'; //直接改变了name属性 alert(box2.run()); //用box.run()发现name也改变了 var box2= new Object(); box2.name='Jack'; box2.age=200; box2.run=function(){ return this.name+this.age+'运行中...'; }; alert(box2.run()); //这样才避免和box混淆,从而保持独立 /*为了解决多个相似对象声明的问题,咱们能够使用一种叫作工厂模式的方法,这种方法 就是为了解决实例化对象产生大量重复的问题。*/ function createObject(name,age){ //集中实例化的函数 varobj=newObject(); obj.name=name; obj.age=age; obj.run=function(){ return this.name+this.age+'运行中...'; }; returnobj; } var box1=createObject('Lee',100); //第一个实例 var box2=createObject('Jack',200); //第二个实例 alert(box1.run()); alert(box2.run()); //保持独立 /*工厂模式解决了重复实例化的问题,但还有一个问题,那就是识别问题,由于根本没法 搞清楚他们究竟是哪一个对象的实例。*/ alert(typeof box1); //Object alert(box1 instanceof Object); //true /*ECMAScript中能够采用构造函数(构造方法)可用来建立特定的对象。类型于Object对 象。*/ function Box(name,age){ //构造函数模式 this.name=name; this.age=age; this.run=function(){ return this.name+this.age+'运行中...'; }; } var box1=new Box('Lee',100); //newBox()便可 var box2=new Box('Jack',200); alert(box1.run()); alert(box1 instanceof Box); //很清晰的识别他从属于Box /* 使用构造函数的方法,即解决了重复实例化的问题,又解决了对象识别的问题,但问题 是,这里并无newObject(),为何能够实例化Box(),这个是哪里来的呢? 使用了构造函数的方法,和使用工厂模式的方法他们不一样之处以下: 1.构造函数方法没有显示的建立对象(newObject()); 2.直接将属性和方法赋值给this对象; 3.没有renturn语句。 */ /* 构造函数的方法有一些规范: 1.函数名和实例化构造名相同且大写,(PS:非强制,但这么写有助于区分构造函数和 普通函数); 2.经过构造函数建立对象,必须使用new运算符。 既然经过构造函数能够建立对象,那么这个对象是哪里来的,newObject()在什么地方 执行了?执行的过程以下: 1.当使用了构造函数,而且new构造函数(),那么就后台执行了newObject(); 2.将构造函数的做用域给新对象,(即newObject()建立出的对象),而函数体内的this就 表明newObject()出来的对象。 3.执行构造函数内的代码; 4.返回新对象(后台直接返回)。 关于this的使用,this其实就是表明当前做用域对象的引用。若是在全局范围this就代 表window对象,若是在构造函数体内,就表明当前的构造函数所声明的对象。 */ var box = 2; alert(this.box); //全局,表明window /*构造函数和普通函数的惟一区别,就是他们调用的方式不一样。只不过,构造函数也是函 数,必须用new运算符来调用,不然就是普通函数。*/ var box = new Box('Lee',100); //构造模式调用 alert(box.run()); Box('Lee',20); //普通模式调用,无效 var o= new Object(); Box.call(o,'Jack', 200) //对象冒充调用 alert(o.run()); //探讨构造函数内部的方法(或函数)的问题,首先看下两个实例化后的属性或方法是否相等。 var box1 = new Box('Lee',100); //传递一致 var box2 = new Box('Lee',100); //同上 alert(box1.name == box2.name); //true,属性的值相等 alert(box1.run == box2.run); //false,方法其实也是一种引用地址 alert(box1.run() == box2.run()); //true,方法的值相等,由于传参一致 /*能够把构造函数里的方法(或函数)用newFunction()方法来代替,获得同样的效果,更加 证实,他们最终判断的是引用地址,惟一性。*/ function Box(name,age){ //newFunction()惟一性 this.name=name; this.age=age; this.run =new Function("return this.name+this.age+'运行中...'"); } /*咱们能够经过构造函数外面绑定同一个函数的方法来保证引用地址的一致性,但这种作 法没什么必要,只是加深学习了解:*/ function Box(name,age){ this.name=name; this.age=age; this.run=run; } function run(){ //经过外面调用,保证引用地址一致 returnt his.name+this.age+'运行中...'; } /*虽然使用了全局的函数run()来解决了保证引用地址一致的问题,但这种方式又带来了 一个新的问题,全局中的this在对象调用的时候是Box自己,而看成普通函数调用的时候, this又表明window。*/ /*-----------------------------------------------------------------------*/ //原型 /*咱们建立的每一个函数都有一个prototype(原型)属性,这个属性是一个对象,它的用途是 包含能够由特定类型的全部实例共享的属性和方法。逻辑上能够这么理解:prototype经过 调用构造函数而建立的那个对象的原型对象。使用原型的好处可让全部对象实例共享它所 包含的属性和方法。也就是说,没必要在构造函数中定义对象信息,而是能够直接将这些信息 添加到原型中。*/ function Box(){} //声明一个构造函数 Box.prototype.name='Lee'; //在原型里添加属性 Box.prototype.age=100; Box.prototype.run=function(){ //在原型里添加方法 return this.name+this.age+'运行中...'; }; //比较一下原型内的方法地址是否一致: var box1 = new Box(); var box2 = new Box(); alert(box1.run == box2.run); //true,方法的引用地址保持一致 /*在原型模式声明中,多了两个属性,这两个属性都是建立对象时自动生成的。__proto__ 属性是实例指向原型对象的一个指针,它的做用就是指向构造函数的原型属性constructor。 经过这两个属性,就能够访问到原型里的属性和方法了。 PS:IE浏览器在脚本访问__proto__会不能识别,火狐和谷歌浏览器及其余某些浏览器 均能识别。虽然能够输出,但没法获取内部信息。*/ alert(box1.__proto__); //[objectObject] //判断一个对象是否指向了该构造函数的原型对象,能够使用isPrototypeOf()方法来测试。 alert(Box.prototype.isPrototypeOf(box)); //只要实例化对象,即都会指向 /*原型模式的执行流程: 1.先查找构造函数实例里的属性或方法,若是有,马上返回; 2.若是构造函数实例里没有,则去它的原型对象里找,若是有,就返回; 虽然咱们能够经过对象实例访问保存在原型中的值,但却不能访问经过对象实例重写原 型中的值。*/ var box1 = new Box(); alert(box1.name); //Lee,原型里的值 box1.name='Jack'; alert(box.1name); //Jack,就近原则, var box2 = new Box(); alert(box2.name); //Lee,原型里的值,没有被box1修改 //若是想要box1也能在后面继续访问到原型里的值,能够把构造函数里的属性删除便可, //具体以下: delete box1.name; //删除属性 alert(box1.name); //如何判断属性是在构造函数的实例里,仍是在原型里?能够使用hasOwnProperty()函数 //来验证: alert(box.hasOwnProperty('name')); //实例里有返回true,不然返回false //in操做符会在经过对象可以访问给定属性时返回true,不管该属性存在于实例中仍是原 //型中。 alert('name'inbox); //true,存在实例中或原型中 //咱们能够经过hasOwnProperty()方法检测属性是否存在实例中,也能够经过in来判断 //实例或原型中是否存在属性。那么结合这两种方法,能够判断原型中是否存在属性。 function isProperty(object,property){ //判断原型中是否存在属性 return !object.hasOwnProperty(property)&&(propertyinobject); } var box = new Box(); alert(isProperty(box,'name')) //true,若是原型有 //为了让属性和方法更好的体现封装的效果,而且减小没必要要的输入,原型的建立能够使 //用字面量的方式: function Box(){}; Box.prototype={ //使用字面量的方式 name:'Lee', age:100, run:function(){ return this.name+this.age+'运行中...'; } }; /*使用构造函数建立原型对象和使用字面量建立对象在使用上基本相同,但仍是有一些区 别,字面量建立的方式使用constructor属性不会指向实例,而会指向Object,构造函数建立 的方式则相反。*/ var box = newBox(); alert(box instanceof Box); alert(box instanceof Object); alert(box.constructor==Box); //字面量方式,返回false,不然,true alert(box.constructor==Object); //字面量方式,返回true,不然,false // /若是想让字面量方式的constructor指向实例对象,那么能够这么作: Box.prototype={ constructor:Box, //直接强制指向便可 }; /*PS:字面量方式为何constructor会指向Object?由于Box.prototype={};这种写法其实 就是建立了一个新对象。而每建立一个函数,就会同时建立它prototype,这个对象也会自 动获取constructor属性。因此,新对象的constructor重写了Box原来的constructor,所以会 指向新对象,那个新对象没有指定构造函数,那么就默认为Object。*/ //原型的声明是有前后顺序的,因此,重写的原型会切断以前的原型。 function Box(){}; Box.prototype={ //原型被重写了 constructor:Box, name:'Lee', age:100, run:function(){ return this.name+this.age+'运行中...'; } }; Box.prototype={ age=200 }; var box = new Box(); //在这里声明 alert(box.run()); //box只是最初声明的原型 //原型对象不只仅能够在自定义对象的状况下使用,而ECMAScript内置的引用类型均可 //以使用这种方式,而且内置的引用类型自己也使用了原型。 alert(Array.prototype.sort); //sort就是Array类型的原型方法 alert(String.prototype.substring); //substring就是String类型的原型方 String.prototype.addstring=function(){ //给String类型添加一个方法 return this+',被添加了!'; //this表明调用的字符串 }; alert('Lee'.addstring()); //使用这个方法 /*PS:尽管给原生的内置引用类型添加方法使用起来特别方便,但咱们不推荐使用这种 方法。由于它可能会致使命名冲突,不利于代码维护。*/ /*原型模式建立对象也有本身的缺点,它省略了构造函数传参初始化这一过程,带来的缺 点就是初始化的值都是一致的。而原型最大的缺点就是它最大的优势,那就是共享。 原型中全部属性是被不少实例共享的,共享对于函数很是合适,对于包含基本值的属性 也还能够。但若是属性包含引用类型,就存在必定的问题:*/ function Box(){}; Box.prototype={ constructor:Box, name:'Lee', age:100, family:['父亲', '母亲', '妹妹'], //添加了一个数组属性 run:function(){ return this.name+this.age+this.family; } }; var box1 = new Box(); box1.family.push('哥哥'); //在实例中添加'哥哥' alert(box1.run()); var box2 = new Box(); alert(box2.run()); //共享带来的麻烦,也有'哥哥'了 /*PS:数据共享的缘故,致使不少开发者放弃使用原型,由于每次实例化出的数据须要 保留本身的特性,而不能共享。*/ //为了解决构造传参和共享问题,能够组合构造函数+原型模式: function Box(name,age){ //不共享的使用构造函数 this.name=name; this.age=age; this.family=['父亲', '母亲', '妹妹']; }; Box.prototype={ //共享的使用原型模式 constructor:Box, run:function(){ return this.name+this.age+this.family; } }; //PS:这种混合模式很好的解决了传参和引用共享的大难题。是建立对象比较好的方法。 /*原型模式,无论你是否调用了原型中的共享方法,它都会初始化原型中的方法,而且在 声明一个对象时,构造函数+原型部分让人感受又很怪异,最好就是把构造函数和原型封装 到一块儿。为了解决这个问题,咱们能够使用动态原型模式。*/ function Box(name,age){ //将全部信息封装到函数体内 this.name=name; this.age=age; if(typeofthis.run!='function') { //仅在第一次调用的初始化 Box.prototype.run=function(){ return this.name+this.age+'运行中...'; }; } } var box = new Box('Lee',100); alert(box.run()); /*当第一次调用构造函数时,run()方法发现不存在,而后初始化原型。当第二次调用,就 不会初始化,而且第二次建立新对象,原型也不会再初始化了。这样及获得了封装,又实现 了原型方法共享,而且属性都保持独立。*/ if(typeof this.run!='function') { alert('第一次初始化'); //测试用 Box.prototype.run=function(){ return this.name+this.age+'运行中...'; }; } var box = new Box('Lee',100); //第一次建立对象 alert(box.run()); //第一次调用 alert(box.run()); //第二次调用 var box2 =new Box('Jack',200); //第二次建立对象 alert(box2.run()); alert(box2.run()); /*PS:使用动态原型模式,要注意一点,不能够再使用字面量的方式重写原型,由于会 切断实例和新原型之间的联系。 以上讲解了各类方式对象建立的方法,若是这几种方式都不能知足需求,能够使用一开 始那种模式:寄生构造函数。*/ function Box(name,age){ var obj = new Object(); obj.name=name; obj.age=age; obj.run=function(){ return this.name+this.age+'运行中...'; }; return obj; } /*寄生构造函数,其实就是工厂模式+构造函数模式。这种模式比较通用,但不能肯定对 象关系,因此,在能够使用以前所说的模式时,不建议使用此模式。 在什么状况下使用寄生构造函数比较合适呢?假设要建立一个具备额外方法的引用类 型。因为以前说明不建议直接String.prototype.addstring,能够经过寄生构造的方式添加。*/ function myString(string){ var str = new String(string); str.addstring=function(){ return this+',被添加了!'; }; return str; } var box=new myString('Lee'); //比直接在引用原型添加要繁琐好多 alert(box.addstring()); /*在一些安全的环境中,好比禁止使用this和new,这里的this是构造函数里不使用this, 这里的new是在外部实例化构造函数时不使用new。这种建立方式叫作稳妥构造函数。*/ function Box(name,age){ var obj = new Object(); obj.run=function(){ return name+age+'运行中...'; //直接打印参数便可 }; return obj; } var box=Box('Lee',100); //直接调用函数 alert(box.run()); //PS:稳妥构造函数和寄生相似。 // /继承 /*继承是面向对象中一个比较核心的概念。其余正统面向对象语言都会用两种方式实现继 承:一个是接口实现,一个是继承。而ECMAScript只支持继承,不支持接口实现,而实现 继承的方式依靠原型链完成。*/ function Box(){ //Box构造 this.name='Lee'; } function Desk(){ //Desk构造 this.age=100; } Desk.prototype = new Box(); //Desc继承了Box,经过原型,造成链条 var desk = new Desk(); alert(desk.age); alert(desk.name); //获得被继承的属性 function Table(){ //Table构造 this.level='AAAAA'; } Table.prototype = new Desk(); //继续原型链继承 var table = new Table(); alert(table.name); //继承了Box和Desk /*若是要实例化table,那么Desk实例中有age=100,原型中增长相同的属性age=200, 最后结果是多少呢?*/ Desk.prototype.age=200; //实例和原型中均包含age /*PS:以上原型链继承还缺乏一环,那就是Obejct,全部的构造函数都继承自Obejct。而 继承Object是自动完成的,并不须要程序员手动继承。 通过继承后的实例,他们的从属关系会怎样呢?*/ alert(table instanceof Object); //true alert(desk instanceof Table); //false,desk是table的超类 alert(table instanceof Desk); //true alert(table instanceof Box); //true /*在JavaScript里,被继承的函数称为超类型(父类,基类也行,其余语言叫法),继承的 函数称为子类型(子类,派生类)。继承也有以前问题,好比字面量重写原型会中断关系,使 用引用类型的原型,而且子类型还没法给超类型传递参数。 为了解决引用共享和超类型没法传参的问题,咱们采用一种叫借用构造函数的技术,或 者成为对象冒充(伪造对象、经典继承)的技术来解决这两种问题。*/ function Box(age){ this.name=['Lee','Jack', 'Hello'] this.age=age; } function Desk(age){ Box.call(this,age); //对象冒充,给超类型传参 } var desk = new Desk(200); alert(desk.age); alert(desk.name); desk.name.push('AAA'); //添加的新数据,只给desk alert(desk.name); //借用构造函数虽然解决了刚才两种问题,但没有原型,复用则无从谈起。因此,咱们需 //要原型链+借用构造函数的模式,这种模式成为组合继承。 function Box(age){ this.name=['Lee','Jack', 'Hello'] this.age=age; } Box.prototype.run=function(){ return this.name+this.age; }; function Desk(age){ Box.call(this,age); //对象冒充 } Desk.prototype=new Box(); //原型链继承 var desk = new Desk(100); alert(desk.run()); /*还有一种继承模式叫作:原型式继承;这种继承借助原型并基于已有的对象建立新对象, 同时还没必要所以建立自定义类型。*/ function obj(o){ //传递一个字面量函数 function F(){} //建立一个构造函数 F.prototype=o; //把字面量函数赋值给构造函数的原型 return new F(); //最终返回出实例化的构造函数 } var box={ //字面量对象 name:'Lee', arr:['哥哥','妹妹','姐姐'] }; var box1 = obj(box); //传递 alert(box1.name); box1.name='Jack'; alert(box1.name); alert(box1.arr); box1.arr.push('父母'); alert(box1.arr); varbox2=obj(box); //传递 alert(box2.name); alert(box2.arr); //引用类型共享了 //寄生式继承把原型式+工厂模式结合而来,目的是为了封装建立对象的过程。 function create(o){ //封装建立过程 var f = obj(o); f.run=function(){ return this.arr; //一样,会共享引用 }; return f; } /*组合式继承是JavaScript最经常使用的继承模式;但,组合式继承也有一点小问题,就是超 类型在使用过程当中会被调用两次:一次是建立子类型的时候,另外一次是在子类型构造函数的 内部。*/ function Box(name){ this.name=name; this.arr=['哥哥','妹妹','父母']; } Box.prototype.run=function(){ return this.name; }; function Desk(name,age){ Box.call(this,name); //第二次调用Box this.age=age; } Desk.prototype = new Box(); //第一次调用Box // /以上代码是以前的组合继承,那么寄生组合继承,解决了两次调用的问题。 function obj(o){ function F(){} F.prototype=o; return new F(); } function create(box,desk){ var f = obj(box.prototype); f.constructor=desk; desk.prototype=f; } function Box(name){ this.name=name; this.arr=['哥哥','妹妹','父母']; } Box.prototype.run=function(){ return this.name; }; function Desk(name,age){ Box.call(this,name); this.age=age; } inPrototype(Box,Desk); //经过这里实现继承 var desk = new Desk('Lee',100); desk.arr.push('姐姐'); alert(desk.arr); alert(desk.run()); //只共享了方法 var desk2 = new Desk('Jack',200); alert(desk2.arr); //引用问题解决
第16章 匿名函数和闭包
//匿名函数就是没有名字的函数,闭包是可访问一个函数做用域里变量的函数。 //匿名函数 //普通函数 function box(){ //函数名是box return 'Lee'; } //匿名函数 function (){ //匿名函数,会报错 return 'Lee'; } //经过表达式自我执行 (function box(){ //封装成表达式 alert('Lee'); })(); //()表示执行函数,而且传参 //把匿名函数赋值给变量 var box = function(){ //将匿名函数赋给变量 return'Lee'; }; alert(box()); //调用方式和函数调用类似 //函数里的匿名函数 function box(){ return function(){ //函数里的匿名函数,产生闭包 return'Lee'; } } alert(box()()); //调用匿名函数 //闭包 /*闭包是指有权访问另外一个函数做用域中的变量的函数,建立闭包的常见的方式,就是在 一个函数内部建立另外一个函数,经过另外一个函数访问这个函数的局部变量。*/ //经过闭包能够返回局部变量 function box(){ var user = 'Lee'; return function(){ //经过匿名函数返回box()局部变量 return user; }; } alert(box()()); //经过box()()来直接调用匿名函数返回值 var b = box(); alert(b()); //另外一种调用匿名函数返回值 /*使用闭包有一个优势,也是它的缺点:就是能够把局部变量驻留在内存中,能够避免使 用全局变量。(全局变量污染致使应用程序不可预测性,每一个模块均可调用必将引来灾难, 因此推荐使用私有的,封装的局部变量)。*/ //经过全局变量来累加 var age = 100; //全局变量 function box(){ age++; //模块级能够调用全局变量,进行累加 } box(); //执行函数,累加了 alert(age); //输出全局变量 //经过局部变量没法实现累加 function box(){ var age=100; age++; //累加 return age; } alert(box()); //101 alert(box()); //101,没法实现,由于又被初始化了 //经过闭包能够实现局部变量的累加 function box(){ var age=100; return function(){ age++; return age; } } var b = box(); //得到函数 alert(b()); //调用匿名函数 alert(b()); //第二次调用匿名函数,实现累加 /*PS:因为闭包里做用域返回的局部变量资源不会被马上销毁回收,因此可能会占用更 多的内存。过分使用闭包会致使性能降低,建议在很是有必要的时候才使用闭包。*/ //做用域链的机制致使一个问题,在循环中里的匿名函数取得的任何变量都是最后一个值。 //循环里包含匿名函数 function box(){ var arr=[]; for(var i=0;i<5;i++){ arr[i]=function(){ return i; }; } return arr; } var b = box(); //获得函数数组 alert(b.length); //获得函数集合长度 for(var i=0;i<b.length;i++){ alert(b[i]()); //输出每一个函数的值,都是最后一个值 } /*上面的例子输出的结果都是5,也就是循环后获得的最大的i值。由于b[i]调用的是匿 名函数,匿名函数并无自我执行,等到调用的时候,box()已执行完毕,i早已变成5,所 以最终的结果就是5个5。*/ //循环里包含匿名函数-改1,自我执行匿名函数 function box(){ var arr=[]; for(var i=0;i<5;i++){ arr[i]=(function(num){ //自我执行 return num; })(i); //而且传参 } returnarr; } var b = box(); for(var i=0;i<b.length;i++){ alert(b[i]); } /*改1中,咱们让匿名函数进行自我执行,致使最终返回给a[i]的是数组而不是函数了。 最终致使b[0]-b[4]中保留了0,1,2,3,4的值。*/ //循环里包含匿名函数-改2,匿名函数下再作个匿名函数 function box(){ var arr=[]; for(vari=0;i<5;i++){ arr[i]=(function(num){ return function(){ //直接返回值,改2变成返回函数 return num; //原理和改1同样 } })(i); } return arr; } var b = box(); for(var i=0;i<b.length;i++){ alert(b[i]()); //这里经过b[i]()函数调用便可 } /*改1和改2中,咱们经过匿名函数自我执行,当即把结果赋值给a[i]。每个i,是调 用方经过按值传递的,因此最终返回的都是指定的递增的i。而不是box()函数里的i。*/ /*关于this对象 在闭包中使用this对象也可能会致使一些问题,this对象是在运行时基于函数的执行环 境绑定的,若是this在全局范围就是window,若是在对象内部就指向这个对象。而闭包却 在运行时指向window的,由于闭包并不属于这个对象的属性或方法。*/ var user='The Window'; var obj={ user:'The Object', getUserFunction:function(){ return function(){ //闭包不属于obj,里面的this指向window return this.user; }; } }; alert(obj.getUserFunction()()); //Thewindow //能够强制指向某个对象 alert(obj.getUserFunction().call(obj)); //TheObject //也能够从上一个做用域中获得对象 getUserFunction:function(){ var that=this; //从对象的方法里得对象 return function(){ return that.user; }; } /*内存泄漏 因为IE的JScript对象和DOM对象使用不一样的垃圾收集方式,所以闭包在IE中会致使 一些问题。就是内存泄漏的问题,也就是没法销毁驻留在内存中的元素。如下代码有两个知 识点尚未学习到,一个是DOM,一个是事件。*/ function box(){ var oDiv = document.getElementById('oDiv'); //oDiv用完以后一直驻留在内存 oDiv.onclick=function(){ alert(oDiv.innerHTML); //这里用oDiv致使内存泄漏 }; } box(); //那么在最后应该将oDiv解除引用来避免内存泄漏。 function box(){ var oDiv=document.getElementById('oDiv'); var text=oDiv.innerHTML; oDiv.onclick=function(){ alert(text); }; oDiv=null; //解除引用 } //PS:若是并无使用解除引用,那么须要等到浏览器关闭才得以释放。 //模仿块级做用域 //JavaScript没有块级做用域的概念。 function box(count){ for(vari=0;i<count;i++){} alert(i); //i不会由于离开了for块就失效 } box(2); function box(count){ for(vari=0;i<count;i++){} vari; //就算从新声明,也不会前面的值 alert(i); } box(2); /*以上两个例子,说明JavaScript没有块级语句的做用域,if(){}for(){}等没有做用域, 若是有,出了这个范围i就应该被销毁了。就算从新声明同一个变量也不会改变它的值。 JavaScript不会提醒你是否屡次声明了同一个变量;遇到这种状况,它只会对后续的声 明视而不见(若是初始化了,固然还会执行的)。使用模仿块级做用域可避免这个问题。*/ //模仿块级做用域(私有做用域) (function(){ //这里是块级做用域 })(); //使用块级做用域(私有做用域)改写 function box(count){ (function(){ for( var i=0; i<count; i++){} })(); alert(i); //报错,没法访问 } box(2); /*使用了块级做用域(私有做用域)后,匿名函数中定义的任何变量,都会在执行结束时被 销毁。这种技术常常在全局做用域中被用在函数外部,从而限制向全局做用域中添加过多的 变量和函数。通常来讲,咱们都应该尽量少向全局做用域中添加变量和函数。在大型项目 中,多人开发的时候,过多的全局变量和函数很容易致使命名冲突,引发灾难性的后果。如 果采用块级做用域(私有做用域),每一个开发者既能够使用本身的变量,又没必要担忧搞乱全局 做用域。*/ (function(){ var box=[1,2,3,4]; alert(box); //box出来就不认识了 })(); /*在全局做用域中使用块级做用域能够减小闭包占用的内存问题,由于没有指向匿名函数 的引用。只要函数执行完毕,就能够当即销毁其做用域链了。*/ /*私有变量 JavaScript没有私有属性的概念;全部的对象属性都是公有的。不过,却有一个私有变 量的概念。任何在函数中定义的变量,均可以认为是私有变量,由于不能在函数的外部访问 这些变量。*/ function box(){ var age=100; //私有变量,外部没法访问 } //而经过函数内部建立一个闭包,那么闭包经过本身的做用域链也能够访问这些变量。而 //利用这一点,能够建立用于访问私有变量的公有方法。 function Box(){ var age=100; //私有变量 function run(){ //私有函数 return'运行中...'; } this.get=function(){ //对外公共的特权方法 return age+run(); }; } var box = new Box(); alert(box.get()); //能够经过构造方法传参来访问私有变量。 function Person(value){ var user = value; //这句其实能够省略 this.getUser=function(){ return user; }; this.setUser=function(value){ user =v alue; }; } /*可是对象的方法,在屡次调用的时候,会屡次建立。能够使用静态私有变量来避免这个 问题。 静态私有变量 经过块级做用域(私有做用域)中定义私有变量或函数,一样能够建立对外公共的特权方 法。*/ (function(){ varage=100; functionrun(){ return'运行中...'; } Box=function(){}; //构造方法 Box.prototype.go=function(){ //原型方法 returnage+run(); }; })(); var box = new Box(); alert(box.go()); /*上面的对象声明,采用的是Box=function(){}而不是functionBox(){}由于若是用后 面这种,就变成私有函数了,没法在全局访问到了,因此使用了前面这种。*/ (function(){ var user=''; Person = function(value){ user = value; }; Person.prototype.getUser=function(){ return user; }; Person.prototype.setUser=function(value){ user=value; } })(); /*使用了prototype致使方法共享了,而user也就变成静态属性了。(所谓静态属性,即共 享于不一样对象中的属性)。 模块模式 以前采用的都是构造函数的方式来建立私有变量和特权方法。那么对象字面量方式就采 用模块模式来建立。*/ var box={ //字面量对象,也是单例对象 age:100, //这是公有属性,将要改为私有 run:function(){ //这时公有函数,将要改为私有 return'运行中...'; }; }; //私有化变量和函数: var box = function(){ var age=100; function run(){ return'运行中...'; } return{ //直接返回对象 go:function(){ return age+run(); } }; }(); //上面的直接返回对象的例子,也能够这么写: var box = function(){ var age=100; function run(){ return'运行中...'; } var obj= { //建立字面量对象 go:function(){ return age+run(); } }; return obj; //返回这个对象 }(); /*字面量的对象声明,其实在设计模式中能够看做是一种单例模式,所谓单例模式,就是 永远保持对象的一个实例。 加强的模块模式,这种模式适合返回自定义对象,也就是构造函数。*/ function Desk(){}; var box = function(){ var age=100; function run(){ return'运行中...'; } var desk = new Desk(); //能够实例化特定的对象 desk.go=function(){ return age + run(); }; return desk; }(); alert(box.go());
第17章 BOM
//BOM也叫浏览器对象模型,它提供了不少对象,用于访问浏览器的功能。 //BOM自己是没有标准的或者尚未哪一个组织去标准它。 //window对象 /*BOM的核心对象是window,它表示浏览器的一个实例。window对象处于JavaScript结 构的最顶层,对于每一个打开的窗口,系统都会自动为其定义window对象。*/ /*1.对象的属性和方法 window对象有一系列的属性,这些属性自己也是对象。*/ /* window对象的属性 closed 当窗口关闭时为真 defaultStatus 窗口底部状态栏显示的默认状态消息 document 窗口中当前显示的文档对象 frames 窗口中的框架对象数组 history 保存有窗口最近加载的URL length 窗口中的框架数 location 当前窗口的URL name 窗口名 offscreenBuffering 用于绘制新窗口内容并在完成后复制已存在的内容,控制屏幕更新 opener 打开当前窗口的窗口 parent 指向包含另外一个窗口的窗口(由框架使用) screen 显示屏幕相关信息,如高度、宽度(以像素为单位) self 指示当前窗口。 status 描述由用户交互致使的状态栏的临时消息 top 包含特定窗口的最顶层窗口(由框架使用) window 指示当前窗口,与self等效 */ /* window对象的方法 alert(text) 建立一个警告对话框,显示一条信息 blur() 将焦点从窗口移除 clearInterval(interval) 清除以前设置的定时器间隔 clearTimeOut(timer) 清除以前设置的超时 close() 关闭窗口 confirm() 建立一个须要用户确认的对话框 focus() 将焦点移至窗口 open(url,name,[options]) 打开一个新窗口并返回新window对象 prompt(text,defaultInput) 建立一个对话框要求用户输入信息 scroll(x,y) 在窗口中滚动到一个像素点的位置 setInterval(expression,milliseconds)通过指定时间间隔计算一个表达式 setInterval(function,millisenconds,[arguments])通过指定时间间隔后调用一个函数 setTimeout(expression,milliseconds)在定时器超事后计算一个表达式 setTimeout(expression,milliseconds,[arguments])在定时器超过期后计算一个函数 print() 调出打印对话框 find() 调出查找对话框 */ /*window下的属性和方法,能够使用window.属性、window.方法()或者直接属性、方法() 的方式调用。例如:window.alert()和alert()是一个意思。*/ /*2.系统对话框 浏览器经过alert()、confirm()和prompt()方法能够调用系统对话框向用户显示信息。系 统对话框与浏览器中显示的网页没有关系,也不包含HTML。*/ //弹出警告 alert('Lee'); //直接弹出警告 //肯定和取消 confirm('请肯定或者取消'); //这里按哪一个都无效 if(confirm('请肯定或者取消')) { //confirm自己有返回值 alert('您按了肯定!'); //按肯定返回true }else{ alert('您按了取消!'); //按取消返回false } //输入提示框 var num = prompt('请输入一个数字', 0); //两个参数,一个提示,一个值 alert(num); //返回值能够获得 //调出打印及查找对话框 print(); //打印 find(); //查找 defaultStatus='状态栏默认文本'; //浏览器底部状态栏初始默认值 status='状态栏文本'; //浏览器底部状态栏设置值 /*3.新建窗口 使用window.open()方法能够导航到一个特定的URL,也能够打开一个新的浏览器窗口。 它能够接受四个参数:1.要加载的URL;2.窗口的名称或窗口目标;3.一个特性字符串;4. 一个表示新页面是否取代浏览器记录中当前加载页面的布尔值。*/ open('http://www.baidu.com'); //新建页面并打开百度 open('http://www.baidu.com','baidu'); //新建页面并命名窗口并打开百度 open('http://www.baidu.com','_parent'); //在本页窗口打开百度,_blank是新建 /*PS:不命名会每次打开新窗口,命名的第一次打开新窗口,以后在这个窗口中加载。 窗口目标是提供页面的打开的方式,好比本页面,仍是新建。*/ /* 第三字符串参数 设置 值 说明 width 数值 新窗口的宽度。不能小于100 height 数值 新窗口的高度。不能小于100 top 数值 新窗口的Y坐标。不能是负值 left 数值 新窗口的X坐标。不能是负值 location yes或no 是否在浏览器窗口中显示地址栏。不一样浏览器默认值不一样 menubar yes或no 是否在浏览器窗口显示菜单栏。默认为no resizable yes或no 是否能够经过拖动浏览器窗口的边框改变大小。默认为no scrollbars yes或no 若是内容在页面中显示不下,是否容许滚动。默认为no status yes或no 是否在浏览器窗口中显示状态栏。默认为no toolbar yes或no 是否在浏览器窗口中显示工具栏。默认为no fullscreen yes或no 浏览器窗口是否最大化,仅限IE */ //第三参数字符串 open('http://www.baidu.com','baidu','width=400,height=400,top=200,left=200,toolbar=yes'); //open自己返回window对象 var box = open(); box.alert(''); //能够指定弹出的窗口执行alert(); //子窗口操做父窗口 document.onclick=function(){ opener.document.write('子窗口让我输出的!'); } /*3.窗口的位置和大小 用来肯定和修改window对象位置的属性和方法有不少。IE、Safari、Opera和Chrome 都提供了screenLeft和screenTop属性,分别用于表示窗口相对于屏幕左边和上边的位置。 Firefox则在screenX和screenY属性中提供相同的窗口位置信息,Safari和Chrome也同时 支持这两个属性。*/ //肯定窗口的位置,IE支持 alert(screenLeft); //IE支持 alert(typeof screenLeft); //IE显示number,不支持的显示undefined //肯定窗口的位置,Firefox支持 alert(screenX); //Firefox支持 alert(typeof screenX); //Firefox显示number,不支持的同上 /*PS:screenX属性IE浏览器不认识,直接alert(screenX),screenX会看成一个为声明的 变量,致使不执行。那么必须将它将至为window属性才能显示为初始化变量应有的值,所 以应该写成:alert(window.screenX)。*/ //跨浏览器的方法 var leftX=(typeof screenLeft=='number') ? screenLeft : screenX; var topY=(typeof screenTop=='number') ? screenTop : screenY; /*窗口页面大小,Firefox、Safari、Opera和Chrome均为此提供了4个属性:innerWidth 和innerHeight,返回浏览器窗口自己的尺寸;outerWidth和outerHeight,返回浏览器窗口本 身及边框的尺寸。*/ alert(innerWidth); //页面长度 alert(innerHeight); //页面高度 alert(outerWidth); //页面长度+边框 alert(outerHeight); //页面高度+边框 /* PS:在Chrome中,innerWidth=outerWidth、innerHeight=outerHeight; PS:IE没有提供当前浏览器窗口尺寸的属性;不过,在后面的DOM课程中有提供相 关的方法。 在IE以及Firefox、Safari、Opera和Chrome中,document.documentElement.clientWidth 和document.documentElement.clientHeight中保存了页面窗口的信息。 PS:在IE6中,这些属性必须在标准模式下才有效;若是是怪异模式,就必须经过 document.body.clientWidth和document.body.clientHeight取得相同的信息。 */ //若是是Firefox浏览器,直接使用innerWidth和innerHeight var width=window.innerWidth; //这里要加window,由于IE会无效 var height=window.innerHeight; if(typeof width!='number') { //若是是IE,就使用document if(document.compatMode=='CSS1Compat') { width=document.documentElement.clientWidth; height=document.documentElement.clientHeight; }else{ width=document.body.clientWidth; //非标准模式使用body height=document.body.clientHeight; } } /*PS:以上方法能够经过不一样浏览器取得各自的浏览器窗口页面可视部分的大小。 document.compatMode能够肯定页面是否处于标准模式,若是返回CSS1Compat即标准模式。*/ //调整浏览器位置 moveTo(0,0); //IE有效,移动到0,0坐标 moveBy(10,10); //IE有效,向下和右分别移动10像素 //调整浏览器大小 resizeTo(200,200); //IE有效,调正大小 resizeBy(200,200); //IE有效,扩展收缩大小 //PS:因为此类方法被浏览器禁用较多,用处不大。 /* 4.间歇调用和超时调用 JavaScript是单线程语言,但它容许经过设置超时值和间歇时间值来调度代码在特定的 时刻执行。前者在指定的时间事后执行代码,然后者则是每隔指定的时间就执行一次代码。 超时调用须要使用window对象的setTimeout()方法,它接受两个参数:要执行的代码 和毫秒数的超时时间。 */ setTimeout("alert('Lee')",1000); //不建议直接使用字符串 function box(){ alert('Lee'); } setTimeout(box,1000); //直接传入函数名便可 setTimeout(function(){ //推荐作法 alert('Lee'); },1000); //PS:直接使用函数传入的方法,扩展性好,性能更佳。 /*调用setTimeout()以后,该方法会返回一个数值ID,表示超时调用。这个超时调用的ID 是计划执行代码的惟一标识符,能够经过它来取消超时调用。 要取消还没有执行的超时调用计划,能够调用clearTimeout()方法并将相应的超时调用ID 做为参数传递给它。*/ var box = setTimeout(function(){ //把超时调用的ID复制给box alert('Lee'); },1000); clearTimeout(box); //把ID传入,取消超时调用 /*间歇调用与超时调用相似,只不过它会按照指定的时间间隔重复执行代码,直至间歇调 用被取消或者页面被卸载。设置间歇调用的方法是setInterval(),它接受的参数与setTimeout() 相同:要执行的代码和每次执行以前须要等待的毫秒数。*/ setInterval(function(){ //重复不停执行 alert('Lee'); },1000); /*取消间歇调用方法和取消超时调用相似,使用clearInterval()方法。但取消间歇调用的重 要性要远远高于取消超时调用,由于在不加干涉的状况下,间歇调用将会一直执行到页面关 闭。*/ var box=setInterval(function(){ //获取间歇调用的ID alert('Lee'); },1000); clearInterval(box); //取消间歇调用 //但上面的代码是没有意义的,咱们须要一个能设置5秒的定时器,须要以下代码: var num=0; //设置起始秒 var max=5; //设置最终秒 setInterval(function(){ //间歇调用 num++; //递增num if(num==max){ //若是获得5秒 clearInterval(this); //取消间歇调用,this表示方法自己 alert('5秒后弹窗!'); } },1000); //1秒 /*通常认为,使用超时调用来模拟间歇调用是一种最佳模式。在开发环境下,不多使用真 正的间歇调用,由于须要根据状况来取消ID,而且可能形成同步的一些问题,咱们建议不 使用间歇调用,而去使用超时调用。*/ var num=0; var max=5; function box(){ num++; if(num==max){ alert('5秒后结束!'); }else{ setTimeout(box,1000); } } setTimeout(box,1000); //执行定时器 /*PS:在使用超时调用时,不必跟踪超时调用ID,由于每次执行代码以后,若是再也不 设置另外一次超时调用,调用就会自行中止。*/ /*location对象 location是BOM对象之一,它提供了与当前窗口中加载的文档有关的信息,还提供了 一些导航功能。事实上,location对象是window对象的属性,也是document对象的属性; 因此window.location和document.location等效。*/ alert(location); //获取当前的URL /* location对象的属性 属性 描述的URL内容 hash 若是该部分存在,表示锚点部分 host 主机名:端口号 hostname 主机名 href 整个URL pathname 路径名 port 端口号 protocol 协议部分 search 查询字符串 */ /* location对象的方法 方法 功能 assign() 跳转到指定页面,与href等效 reload() 重载当前URL repalce() 用新的URL替换当前页面 */ location.hash='#1'; //设置#后的字符串,并跳转 alert(location.hash); //获取#后的字符串 location.port=8888; //设置端口号,并跳转 alert(location.port); //获取当前端口号, location.hostname='Lee'; //设置主机名,并跳转 alert(location.hostname); //获取当前主机名, location.pathname='Lee'; //设置当前路径,并跳转 alert(location.pathname); //获取当前路径, location.protocal='ftp:'; //设置协议,没有跳转 alert(location.protocol); //获取当前协议 location.search='?id=5'; //设置?后的字符串,并跳转 alert(location.search); //获取?后的字符串 location.href='http://www.baidu.com'; //设置跳转的URL,并跳转 alert(location.href); //获取当前的URL /*在Web开发中,咱们常常须要获取诸如?id=5&search=ok这种类型的URL的键值对, 那么经过location,咱们能够写一个函数,来一一获取。*/ function getArgs(){ //建立一个存放键值对的数组 var args=[]; //去除?号 var qs=location.search.length>0?location.search.substring(1):''; //按&字符串拆分数组 var items=qs.split('&'); var item=null,name=null,value=null; //遍历 for(var i =0;i<items.length;i++){ item=items[i].split('='); name=item[0]; value=item[1]; //把键值对存放到数组中去 args[name]=value; } return args; } var args=getArgs(); alert(args['id']); alert(args['search']); location.assign('http://www.baidu.com'); //跳转到指定的URL location.reload(); //最有效的从新加载,有可能从缓存加载 location.reload(true); //强制加载,从服务器源头从新加载 location.replace('http://www.baidu.com'); //能够避免产生跳转前的历史记录 /*三.history对象 history对象是window对象的属性,它保存着用户上网的记录,从窗口被打开的那一刻 算起。*/ /* history对象的属性 属性 描述URL中的哪部分 length history对象中的记录数 */ /* history对象的方法 方法 功能 back() 前往浏览器历史条目前一个URL,相似后退 forward() 前往浏览器历史条目下一个URL,相似前进 go(num) 浏览器在history对象中向前或向后 */ function back(){ //跳转到前一个URL history.back(); } function forward(){ //跳转到下一个URL history.forward(); } function go(num){ //跳转指定历史记录的URL history.go(num); } //PS:能够经过判断history.length==0,获得是否有历史记录。
第18章 浏览器检测
第19章 DOM基础
/*DOM(DocumentObjectModel)即文档对象模型,针对HTML和XML文档的API(应 用程序接口)。DOM描绘了一个层次化的节点树,运行开发人员添加、移除和修改页面的 某一部分。DOM脱胎于Netscape及微软公司创始的DHTML(动态HTML),但如今它已 经成为表现和操做页面标记的真正跨平台、语言中立的方式。*/ /*一.DOM介绍 DOM中的三个字母,D(文档)能够理解为整个Web加载的网页文档;O(对象)可 以理解为相似window对象之类的东西,能够调用属性和方法,这里咱们说的是document 对象;M(模型)能够理解为网页文档的树型结构。 DOM有三个等级,分别是DOM一、DOM二、DOM3,而且DOM1在1998年10月成为 W3C标准。DOM1所支持的浏览器包括IE6+、Firefox、Safari、Chrome和Opera1.7+。 PS:IE中的全部DOM对象都是以COM对象的形式实现的,这意味着IE中的DOM 可能会和其余浏览器有必定的差别。 1.节点 加载HTML页面时,Web浏览器生成一个树型结构,用来表示页面内部结构。DOM将 这种树型结构理解为由节点组成。*/
/*从上图的树型结构,咱们理解几个概念,html标签没有父辈,没有兄弟,因此html标 签为根标签。head标签是html子标签,meta和title标签之间是兄弟关系。若是把每一个标签*/ /*看成一个节点的话,那么这些节点组合成了一棵节点树。 PS:后面咱们常常把标签称做为元素,是一个意思。 2.节点种类:元素节点、文本节点、属性节点。 */
/*二.查找元素 W3C提供了比较方便简单的定位节点的方法和属性,以便咱们快速的对节点进行操做。 分别为:getElementById()、getElementsByTagName()、getElementsByName()、getAttribute()、 setAttribute()和removeAttribute()。*/ /*元素节点方法 方法 说明 getElementById() 获取特定ID元素的节点 getElementsByTagName() 获取相同元素的节点列表 getElementsByName() 获取相同名称的节点列表 getAttribute() 获取特定元素节点属性的值 setAttribute() 设置特定元素节点属性的值 removeAttribute() 移除特定元素节点属性 */ /*1.getElementById()方法 getElementById()方法,接受一个参数:获取元素的ID。若是找到相应的元素则返回该 元素的HTMLDivElement对象,若是不存在,则返回null。*/ document.getElementById('box'); //获取id为box的元素节点 /*PS:上面的例子,默认状况返回null,这无关是否存在id="box"的标签,而是执行顺序 问题。解决方法,1.把script调用标签移到html末尾便可;2.使用onload事件来处理JS,等 待html加载完毕再加载onload事件里的JS。*/ window.onload=function(){ //预加载html后执行 document.getElementById('box'); }; /*PS:id表示一个元素节点的惟一性,不能同时给两个或以上的元素节点建立同一个命 名的id。某些低版本的浏览器会没法识别getElementById()方法,好比IE5.0-,这时须要作 一些判断,能够结合上章的浏览器检测来操做。*/ if(document.getElementById){ //判断是否支持getElementById alert('当前浏览器支持getElementById'); } //当咱们经过getElementById()获取到特定元素节点时,这个节点对象就被咱们获取到了, //而经过这个节点对象,咱们能够访问它的一系列属性。 /*元素节点属性 属性 说明 tagName 获取元素节点的标签名 innerHTML 获取元素节点里的内容,非W3CDOM规范*/ document.getElementById('box').tagName; //DIV document.getElementById('box').innerHTML; //测试Div /* HTML属性的属性 属性 说明 id 元素节点的id名称 title 元素节点的title属性值 style CSS内联样式属性值 className CSS元素的类 */ document.getElementById('box').id; //获取id document.getElementById('box').id='person'; //设置id document.getElementById('box').title; //获取title document.getElementById('box').title='标题' //设置title document.getElementById('box').style; //获取CSSStyleDeclaration对象 document.getElementById('box').style.color; //获取style对象中color的值 document.getElementById('box').style.color='red'; //设置style对象中color的值 document.getElementById('box').className; //获取class document.getElementById('box').className='box'; //设置class alert(document.getElementById('box').bbb); //获取自定义属性的值,非IE不支持 //2.getElementsByTagName()方法 /*getElementsByTagName()方法将返回一个对象数组HTMLCollection(NodeList),这个数 组保存着全部相同元素名的节点列表。*/ document.getElementsByTagName('*'); //获取全部元素 //PS:IE浏览器在使用通配符的时候,会把文档最开始的html的规范声明看成第一个元素节点。 document.getElementsByTagName('li'); //获取全部li元素,返回数组 document.getElementsByTagName('li')[0]; //获取第一个li元素,HTMLLIElement document.getElementsByTagName('li').item(0) //获取第一个li元素,HTMLLIElement document.getElementsByTagName('li').length; //获取全部li元素的数目 /*PS:无论是getElementById仍是getElementsByTagName,在传递参数的时候,并非 全部浏览器都必须区分大小写,为了防止没必要要的错误和麻烦,咱们必须坚持养成区分大小 写的习惯。*/ /*3.getElementsByName()方法 getElementsByName()方法能够获取相同名称(name)的元素,返回一个对象数组 HTMLCollection(NodeList)。*/ document.getElementsByName('add') //获取input元素 document.getElementsByName('add')[0].value //获取input元素的value值 document.getElementsByName('add')[0].checked//获取input元素的checked值 /*PS:对于并非HTML合法的属性,那么在JS获取的兼容性上也会存在差别,IE浏 览器支持自己合法的name属性,而不合法的就会出现不兼容的问题。*/ /*4.getAttribute()方法 getAttribute()方法将获取元素中某个属性的值。它和直接使用.属性获取属性值的方法有 必定区别。*/ document.getElementById('box').getAttribute('id');//获取元素的id值 document.getElementById('box').id; //获取元素的id值 document.getElementById('box').getAttribute('mydiv');//获取元素的自定义属性值 document.getElementById('box').mydiv //获取元素的自定义属性值,非IE不支持 document.getElementById('box').getAttribute('class');//获取元素的class值,IE不支持 document.getElementById('box').getAttribute('className');//非IE不支持 /*PS:HTML通用属性style和onclick,IE7更低的版本style返回一个对象,onclick返回 一个函数式。虽然IE8已经修复这个bug,但为了更好的兼容,开发人员只有尽量避免使 用getAttribute()访问HTML属性了,或者碰到特殊的属性获取作特殊的兼容处理。*/ /*5.setAttribute()方法 setAttribute()方法将设置元素中某个属性和值。它须要接受两个参数:属性名和值。若是 属性自己已存在,那么就会被覆盖。*/ document.getElementById('box').setAttribute('align','center');//设置属性和值 document.getElementById('box').setAttribute('bbb','ccc');//设置自定义的属性和值 /*PS:在IE7及更低的版本中,使用setAttribute()方法设置class和style属性是没有效果 的,虽然IE8解决了这个bug,但仍是不建议使用。*/ //6.removeAttribute()方法 removeAttribute() //能够移除HTML属性。 document.getElementById('box').removeAttribute('style');//移除属性 //PS:IE6及更低版本不支持removeAttribute()方法。 /*三.DOM节点 1.node节点属性 节点能够分为元素节点、属性节点和文本节点,而这些节点又有三个很是有用的属性, 分别为:nodeName、nodeType和nodeValue。*/ /* 信息节点属性 节点类型 nodeName nodeType nodeValue 元素 元素名称 1 null 属性 属性名称 2 属性值 文本 #text 3 文本内容(不包含html) */ document.getElementById('box').nodeType; //1,元素节点
第20章 动态加载脚本和样式
//一.元素位置 //这节课补充一个DOM的方法:getBoundingClientRect()。 /*这个方法返回一个矩形对象,包含四个属性:left、top、right 和bottom。分别表示元素各边与页面上边和左边的距离。*/ var box = document.getElementById('box'); //获取元素 alert(box.getBoundingClientRect().top); //元素上边距离页面上边的距离 alert(box.getBoundingClientRect().right); //元素右边距离页面左边的距离 alert(box.getBoundingClientRect().bottom); //元素下边距离页面上边的距离 alert(box.getBoundingClientRect().left); //元素左边距离页面左边的距离 /*PS:IE、Firefox3+、Opera9.五、Chrome、Safari支持,在IE中,默认坐标从(2,2)开始计 算,致使最终距离比其余浏览器多出两个像素,咱们须要作个兼容。*/ /*PS:IE、Firefox3+、Opera9.五、Chrome、Safari支持,在IE中,默认坐标从(2,2)开始计 算,致使最终距离比其余浏览器多出两个像素,咱们须要作个兼容。*/ document.documentElement.clientTop; //非IE为0,IE为2 document.documentElement.clientLeft; //非IE为0,IE为2 function getRect(element){ var rect=element.getBoundingClientRect(); var top=document.documentElement.clientTop; var left=document.documentElement.clientLeft; return{ top:rect.top-top, bottom:rect.bottom-top, left:rect.left-left, right:rect.right-left } } //PS:分别加上外边据、内边距、边框和滚动条,用于测试全部浏览器是否一致。 /* 二.动态脚本 当网站需求变大,脚本的需求也逐步变大。咱们就不得不引入太多的JS脚本而下降了 整站的性能,因此就出现了动态脚本的概念,在适时的时候加载相应的脚本。 好比:咱们想在须要检测浏览器的时候,再引入检测文件。 */ var flag=true; //设置true再加载 if(flag){ loadScript('browserdetect.js'); //设置加载的js } function loadScript(url){ var script=document.createElement('script'); script.type='text/javascript'; script.src=url; //document.head.appendChild(script); //document.head表示<head> document.getElementsByTagName('head')[0].appendChild(script); } //PS:document.head调用,IE不支持,会报错! //动态执行js var script=document.createElement('script'); script.type='text/javascript'; vartext=document.createTextNode("alert('Lee')"); //IE浏览器报错 script.appendChild(text); document.getElementsByTagName('head')[0].appendChild(script); /*PS:IE浏览器认为script是特殊元素,不能在访问子节点。为了兼容,能够使用text 属性来代替。*/ script.text="alert('')"; //IE能够支持了。 /*PS:固然,若是不支持text,那么就能够针对不一样的浏览器特性来使用不一样的方法。这 里就忽略写法了。*/ /*三.动态样式 为了动态的加载样式表,好比切换网站皮肤。样式表有两种方式进行加载,一种是<link> 标签,一种是<style>标签。*/ //动态执行link var flag=true; if(flag){ loadStyles('basic.css'); } function loadStyles(url){ var link=document.createElement('link'); link.rel='stylesheet'; link.type='text/css'; link.href=url; document.getElementsByTagName('head')[0].appendChild(link); } //动态执行style var flag=true; if(flag){ var style=document.createElement('style'); style.type='text/css'; //var box=document.createTextNode(#box{background:red}');IE不支持 //style.appendChild(box); document.getElementsByTagName('head')[0].appendChild(style); insertRule(document.styleSheets[0],'#box', 'background:red', 0); } function insertRule(sheet,selectorText,cssText,position){ //若是是非IE if(sheet.insertRule){ sheet.insertRule(selectorText+"{"+cssText+"}",position); //若是是IE }elseif(sheet.addRule){ sheet.addRule(selectorText,cssText,position); } }
第24章 事件
①。事件入门
/*JavaScript事件是由访问Web页面的用户引发的一系列操做,例如:用户点击。当用户 执行某些操做的时候,再去执行一系列代码。*/ /*一.事件介绍 事件通常是用于浏览器和用户操做进行交互。最先是IE和NetscapeNavigator中出现, 做为分担服务器端运算负载的一种手段。直到几乎全部的浏览器都支持事件处理。而DOM2 级规范开始尝试以一种复合逻辑的方式标准化DOM事件。IE九、Firefox、Opera、Safari和 Chrome全都已经实现了“DOM2级事件”模块的核心部分。IE8以前浏览器仍然使用其专 有事件模型。 JavaScript有三种事件模型:内联模型、脚本模型和DOM2模型。*/ /*二.内联模型 这种模型是最传统接单的一种处理事件的方法。在内联模型中,事件处理函数是HTML 标签的一个属性,用于处理指定事件。虽然内联在早期使用较多,但它是和HTML混写的, 并无与HTML分离。*/ //在HTML中把事件处理函数做为属性执行JS代码 <input type="button" value="按钮" onclick="alert('Lee');" /> //注意单双引号 //在HTML中把事件处理函数做为属性执行JS函数 <input type="button" value="按钮" onclick="box();" /> //执行JS的函数 //PS:函数不得放到window.onload里面,这样就看不见了。 /*三.脚本模型 因为内联模型违反了HTML与JavaScript代码层次分离的原则。为了解决这个问题,我 们能够在JavaScript中处理事件。这种处理方式就是脚本模型。*/ var input=document.getElementsByTagName('input')[0]; //获得input对象 input.onclick=function(){ //匿名函数执行 alert('Lee'); }; /*PS:经过匿名函数,能够直接触发对应的代码。也能够经过指定的函数名赋值的方式 来执行函数(赋值的函数名不要跟着括号)。*/ input.onclick=box; //把函数名赋值给事件处理函数 //四.事件处理函数 //JavaScript能够处理的事件类型为:鼠标事件、键盘事件、HTML事件。 /* JavaScript事件处理函数及其使用列表 事件处理函数 影响的元素 什么时候发生 onabort 图像 当图像加载被中断时 onblur 窗口、框架、全部表单对象 当焦点从对象上移开时 onchange 输入框,选择框和文本区域 当改变一个元素的值且失去焦点时 onclick 连接、按钮、表单对象、图像映射区域 当用户单击对象时 ondblclick 连接、按钮、表单对象 当用户双击对象时 ondragdrop 窗口 当用户将一个对象拖放到浏览器窗口时 onError 脚本 当脚本中发生语法错误时 onfocus 窗口、框架、全部表单对象 当单击鼠标或者将鼠标移动聚焦到窗口或框架时 onkeydown 文档、图像、连接、表单 当按键被按下时 onkeypress 文档、图像、连接、表单 当按键被按下而后松开时 onkeyup 文档、图像、连接、表单 当按键被松开时 onload 主题、框架集、图像 文档或图像加载后 onunload 主体、框架集 文档或框架集卸载后 onmouseout 连接 当图标移除连接时 onmouseover 连接 当鼠标移到连接时 onmove 窗口 当浏览器窗口移动时 onreset 表单复位按钮 单击表单的reset按钮 onresize 窗口 当选择一个表单对象时 onselect 表单元素 当选择一个表单对象时 onsubmit 表单 当发送表格到服务器时 */ /*PS:全部的事件处理函数都会都有两个部分组成,on+事件名称,例如click事件的事件 处理函数就是:onclick。在这里,咱们主要谈论脚本模型的方式来构建事件,违反分离原 则的内联模式,咱们忽略掉。*/ /*对于每个事件,它都有本身的触发范围和方式,若是超出了触发范围和方式,事件处 理将失效。*/ //1.鼠标事件,页面全部元素均可触发 //click:当用户单击鼠标按钮或按下回车键时触发。 input.onclick=function(){ alert('Lee'); }; //dblclick:当用户双击主鼠标按钮时触发。 input.ondblclick=function(){ alert('Lee'); }; //mousedown:当用户按下了鼠标还未弹起时触发。 input.onmousedown=function(){ alert('Lee'); }; //mouseup:当用户释放鼠标按钮时触发。 input.onmouseup=function(){ alert('Lee'); }; //mouseover:当鼠标移到某个元素上方时触发。 input.onmouseover=function(){ alert('Lee'); }; //mouseout:当鼠标移出某个元素上方时触发。 input.onmouseout=function(){ alert('Lee'); }; //mousemove:当鼠标指针在元素上移动时触发。 input.onmousemove=function(){ alert('Lee'); }; //2.键盘事件 //keydown:当用户按下键盘上任意键触发,若是按住不放,会重复触发。 onkeydown=function(){ alert('Lee'); }; //keypress:当用户按下键盘上的字符键触发,若是按住不放,会重复触发。 onkeypress=function(){ alert('Lee'); }; //keyup:当用户释放键盘上的键触发。 onkeyup=function(){ alert('Lee'); }; //3.HTML事件 //load:当页面彻底加载后在window上面触发,或当框架集加载完毕后在框架集上触发。 window.onload=function(){ alert('Lee'); }; //unload:当页面彻底卸载后在window上面触发,或当框架集卸载后在框架集上触发。 window.onunload=function(){ alert('Lee'); }; //select:当用户选择文本框(input或textarea)中的一个或多个字符触发。 input.onselect=function(){ alert('Lee'); }; //change:当文本框(input或textarea)内容改变且失去焦点后触发。 input.onchange=function(){ alert('Lee'); }; //focus:当页面或者元素得到焦点时在window及相关元素上面触发。 input.onfocus=function(){ alert('Lee'); }; //blur:当页面或元素失去焦点时在window及相关元素上触发。 input.onblur=function(){ alert('Lee'); }; //submit:当用户点击提交按钮在<form>元素上触发。 form.onsubmit=function(){ alert('Lee'); }; //reset:当用户点击重置按钮在<form>元素上触发。 form.onreset=function(){ alert('Lee'); }; //resize:当窗口或框架的大小变化时在window或框架上触发。 window.onresize=function(){ alert('Lee'); }; //scroll:当用户滚动带滚动条的元素时触发。 window.onscroll=function(){ alert('Lee'); };
②。事件对象
/*JavaScript事件的一个重要方面是它们拥有一些相对一致的特色,能够给你的开发提供 更多的强大功能。最方便和强大的就是事件对象,他们能够帮你处理鼠标事件和键盘敲击方 面的状况,此外还能够修改通常事件的捕获/冒泡流的函数。*/ /*一.事件对象 事件处理函数的一个标准特性是,以某些方式访问的事件对象包含有关于当前事件的上 下文信息。 事件处理三部分组成:对象.事件处理函数=函数。例如:单击文档任意处。*/ document.onclick=function(){ alert('Lee'); }; /*PS:以上程序的名词解释:click表示一个事件类型,单击。onclick表示一个事件处理 函数或绑定对象的属性(或者叫事件监听器、侦听器)。document表示一个绑定的对象,用于 触发某个元素区域。function()匿名函数是被执行的函数,用于触发后执行。*/ //除了用匿名函数的方法做为被执行的函数,也能够设置成独立的函数。 document.onclick=box; //直接赋值函数名便可,无须括号 function box(){ alert('Lee'); } /*this关键字和上下文 在面向对象那章咱们了解到:在一个对象里,因为做用域的关系,this表明着离它最近 对象。*/ var input=document.getElementsByTagName('input')[0]; input.onclick=function(){ alert(this.value); //HTMLInputElement,this表示input对象 }; /*从上面的拆分,咱们并无发现本章的重点:事件对象。那么事件对象是什么?它在哪 里呢?当触发某个事件时,会产生一个事件对象,这个对象包含着全部与事件有关的信息。 包括致使事件的元素、事件的类型、以及其它与特定事件相关的信息。 事件对象,咱们通常称做为event对象,这个对象是浏览器经过函数把这个对象做为参 数传递过来的。那么首先,咱们就必须验证一下,在执行函数中没有传递参数,是否能够得 到隐藏的参数。*/ function box(){ //普通空参函数 alert(arguments.length); //0,没有获得任何传递的参数 } input.onclick=function(){ //事件绑定的执行函数 alert(arguments.length); //1,获得一个隐藏参数 }; /*经过上面两组函数中,咱们发现,经过事件绑定的执行函数是能够获得一个隐藏参数的。 说明,浏览器会自动分配一个参数,这个参数其实就是event对象。*/ input.onclick=function(){ alert(arguments[0]); //MouseEvent,鼠标事件对象 }; //上面这种作法比较累,那么比较简单的作法是,直接经过接收参数来获得便可。 input.onclick=function(evt){ //接受event对象,名称不必定非要event alert(evt); //MouseEvent,鼠标事件对象 }; //直接接收event对象,是W3C的作法,IE不支持,IE本身定义了一个event对象,直 //接在window.event获取便可。 input.onclick=function(evt){ vare=evt|| window.event; //实现跨浏览器兼容获取event对象 alert(e); }; /*二.鼠标事件 鼠标事件是Web上面最经常使用的一类事件,毕竟鼠标仍是最主要的定位设备。那么经过 事件对象能够获取到鼠标按钮信息和屏幕坐标获取等。 1.鼠标按钮 只有在主鼠标按钮被单击时(常规通常是鼠标左键)才会触发click事件,所以检测按钮 的信息并非必要的。但对于mousedown和mouseup事件来讲,则在其event对象存在一 个button属性,表示按下或释放按钮。*/ /* 非IE(W3C)中的button属性 值 说明 0 表示主鼠标按钮(常规通常是鼠标左键) 1 表示中间的鼠标按钮(鼠标滚轮按钮) 2 表示次鼠标按钮(常规通常是鼠标右键) */ /* IE中的button属性 值 说明 0 表示没有按下按钮 1 表示主鼠标按钮(常规通常是鼠标左键) 2 表示次鼠标按钮(常规通常是鼠标右键) 3 表示同时按下了主、次鼠标按钮 4 表示按下了中间的鼠标按钮 5 表示同时按下了主鼠标按钮和中间的鼠标按钮 6 表示同时按下了次鼠标按钮和中间的鼠标按钮 7 表示同时按下了三个鼠标按钮 */ /* PS:在绝大部分状况下,咱们最多只使用主次中三个单击键,IE给出的其余组合键一 般没法使用上。因此,咱们只须要作上这三种兼容便可。 */ function getButton(evt){ //跨浏览器左中右键单击相应 var e=evt|| window.event; if(evt){ //Chrome浏览器支持W3C和IE return e.button; //要注意判断顺序 }else if(window.event){ switch(e.button){ case 1: return 0; case 4: return 1; case 2: return 2; } } } document.onmouseup=function(evt){ //调用 if(getButton(evt)==0){ alert('按下了左键!'); }elseif(getButton(evt)==1){ alert('按下了中键!'); }elseif(getButton(evt)==2){ alert('按下了右键!' ); } }; /*2.可视区及屏幕坐标 事件对象提供了两组来获取浏览器坐标的属性,一组是页面可视区左边,另外一组是屏幕 坐标。*/ /* 坐标属性 属性 说明 clientX 可视区X坐标,距离左边框的位置 clientY 可视区Y坐标,距离上边框的位置 screenX 屏幕区X坐标,距离左屏幕的位置 screenY 屏幕区Y坐标,距离上屏幕的位置 */ document.onclick=function(evt){ var e=evt|| window.event; alert(e.clientX+',' +e.clientY); alert(e.screenX+',' +e.screenY); }; /*3.修改键 有时,咱们须要经过键盘上的某些键来配合鼠标来触发一些特殊的事件。这些键为: Shfit、Ctrl、Alt和Meat(Windows中就是Windows键,苹果机中是Cmd键),它们常常被用 来修改鼠标事件和行为,因此叫修改键。*/ /* 修改键属性 属性 说明 shiftKey 判断是否按下了Shfit键 ctrlKey 判断是否按下了ctrlKey键 altKey 判断是否按下了alt键 metaKey 判断是否按下了windows键,IE不支持 */ function getKey(evt){ var e=evt|| window.event; var keys=[]; if(e.shiftKey) keys.push('shift'); //给数组添加元素 if(e.ctrlKey) keys.push('ctrl'); if(e.altKey) keys.push('alt'); return keys; } document.onclick=function(evt){ alert(getKey(evt)); }; /* 三.键盘事件 用户在使用键盘时会触发键盘事件。“DOM2级事件”最初规定了键盘事件,结果又删 除了相应的内容。最终仍是使用最初的键盘事件,不过IE9已经率先支持“DOM3”级键盘 事件。 1.键码 在发生keydown和keyup事件时,event对象的keyCode属性中会包含一个代码,与键 盘上一个特定的键对应。对数字字母字符集,keyCode属性的值与ASCII码中对应小写字母 或数字的编码相同。字母中大小写不影响。 */ document.onkeydown=function(evt){ alert(evt.keyCode); //按任意键,获得相应的keyCode }; /* 不一样的浏览器在keydown和keyup事件中,会有一些特殊的状况: 在Firefox和Opera中,分号键时keyCode值为59,也就是ASCII中分号的编码;而IE 和Safari返回186,即键盘中按键的键码。 PS:其余一些特殊状况因为浏览器版本太老和市场份额过低,这里不作补充。 2.字符编码 Firefox、Chrome和Safari的event对象都支持一个charCode属性,这个属性只有在发 生keypress事件时才包含值,并且这个值是按下的那个键所表明字符的ASCII编码。此时 的keyCode一般等于0或者也可能等于所按键的编码。IE和Opera则是在keyCode中保存 字符的ASCII编码。 */ function getCharCode(evt){ var e=evt|| window.event; if(typeof e.charCode=='number') { return e.charCode; }else{ return e.keyCode; } } //PS:能够使用String.fromCharCode()将ASCII编码转换成实际的字符。 /* keyCode和charCode区别以下:好比当按下“a键(重视是小写的字母)时, 在Firefox中会得到 keydown:keyCodeis65 charCodeis0 keyup: keyCodeis65charCodeis0 keypress:keyCodeis0 charCodeis97 在IE中会得到 keydown:keyCodeis65 charCodeisundefined keyup: keyCodeis65 charCodeisundefined keypress:keyCodeis97 charCodeisundefined 而当按下shift键时,在Firefox中会得到 keydown:keyCodeis16 charCodeis0 keyup:keyCodeis16 charCodeis0 在IE中会得到 keydown:keyCodeis16 charCodeisundefined keyup:keyCodeis16 charCodeisundefined keypress:不会得到任何的charCode值,由于按shift并没输入任何的字符,而且也不 会触发keypress事务 */ /* PS:在keydown事务里面,事务包含了keyCode–用户按下的按键的物理编码。 在keypress里,keyCode包含了字符编码,即默示字符的ASCII码。如许的情势实用于 全部的浏览器–除了火狐,它在keypress事务中的keyCode返回值为0。 */ /* 四.W3C与IE 在标准的DOM事件中,event对象包含与建立它的特定事件有关的属性和方法。触发 的事件类型不同,可用的属性和方法也不同。 */ /* W3C中event对象的属性和方法 属性/方法 类型 读/写 说明 bubbles Boolean 只读 代表事件是否冒泡 cancelable Boolean 只读 代表是否能够取消事件的默认行为 currentTarget Element 只读 其事件处理程序当前正在处理事件的那个元素 detail Integer 只读 与事件相关的细节信息 eventPhase Integer 只读 调用事件处理程序的阶段:1表示捕获阶段,2表示“处理目标”,3表示冒泡阶段 preventDefault() Function 只读 取消事件的默认行为。若是cancelabel是true,则能够使用这个方法 stopPropagation() Function 只读 取消事件的进一步捕获或冒泡。若是bubbles为true,则能够使用这个方法 target Element 只读 事件的目标 type String 只读 被触发的事件的类型 view AbstractView 只读 与事件关联的抽象视图。等同于发生事件的window对象 cancelBubble Boolean 读/写 默认值为false,但将其设置为true就能够取消事件冒泡 returnValue Boolean 读/写 默认值为true,但将其设置为false就能够取消事件的默认行为 srcElement Element 只读 事件的目标 type String 只读 被触发的事件类型 */ /*在这里,咱们只看全部浏览器都兼容的属性或方法。首先第一个咱们了解一下W3C中 的target和IE中的srcElement,都表示事件的目标。*/ function getTarget(evt){ var e=evt|| window.event; return e.target|| e.srcElement; //兼容获得事件目标DOM对象 } document.onclick=function(evt){ var target=getTarget(evt); alert(target); }; /* 事件流 事件流是描述的从页面接受事件的顺序,当几个都具备事件的元素层叠在一块儿的时候, 那么你点击其中一个元素,并非只有当前被点击的元素会触发事件,而层叠在你点击范围 的全部元素都会触发事件。事件流包括两种模式:冒泡和捕获。 事件冒泡,是从里往外逐个触发。事件捕获,是从外往里逐个触发。那么现代的浏览器 默认状况下都是冒泡模型,而捕获模式则是早期的Netscape默认状况。而如今的浏览器要 使用DOM2级模型的事件绑定机制才能手动定义事件流模式。 */ document.onclick=function(){ alert('我是document'); }; document.documentElement.onclick=function(){ alert('我是html'); }; document.body.onclick=function(){ alert('我是body'); }; document.getElementById('box').onclick=function(){ alert('我是div'); }; document.getElementsByTagName('input')[0].onclick=function(){ alert('我是input'); }; //在阻止冒泡的过程当中,W3C和IE采用的不一样的方法,那么咱们必须作一下兼容。 function stopPro(evt){ vare=evt|| window.event; window.event?e.cancelBubble=true:e.stopPropagation(); }
③。事件绑定及深刻
/* 事件绑定分为两种:一种是传统事件绑定(内联模型,脚本模型),一种是现代事件绑定 (DOM2级模型)。现代事件绑定在传统绑定上提供了更强大更方便的功能。 */ /* 一.传统事件绑定的问题 传统事件绑定有内联模型和脚本模型,内联模型咱们不作讨论,基本不多去用。先来看 一下脚本模型,脚本模型将一个函数赋值给一个事件处理函数。 */ var box=document.getElementById('box'); //获取元素 box.onclick=function(){ //元素点击触发事件 alert('Lee'); }; //问题一:一个事件处理函数触发两次事件 window.onload=function(){ //第一组程序项目或第一个JS文件 alert('Lee'); }; window.onload=function(){ //第二组程序项目或第二个JS文件 alert('Mr.Lee'); }; //当两组程序或两个JS文件同时执行的时候,后面一个会把前面一个彻底覆盖掉。致使 //前面的window.onload彻底失效了。 //解决覆盖问题,咱们能够这样去解决: window.onload=function(){ //第一个要执行的事件,会被覆盖 alert('Lee'); }; if(typeof window.onload=='function') { //判断以前是否有window.onload var saved=null; //建立一个保存器 saved=window.onload; //把以前的window.onload保存起来 } window.onload=function(){ //最终一个要执行事件 if(saved)saved(); //执行以前一个事件 alert('Mr.Lee'); //执行本事件的代码 }; //问题二:事件切换器 box.onclick=toBlue; //第一次执行boBlue() function toRed(){ this.className='red'; this.onclick=toBlue; //第三次执行toBlue(),而后来回切换 } function toBlue(){ this.className='blue'; this.onclick=toRed; //第二次执行toRed() } //这个切换器在扩展的时候,会出现一些问题: //1.若是增长一个执行函数,那么会被覆盖 box.onclick=toAlert; //被增长的函数 box.onclick=toBlue; //toAlert被覆盖了 //2.若是解决覆盖问题,就必须包含同时执行,但又出新问题 box.onclick=function(){ //包含进去,但可读性下降 toAlert(); //第一次不会被覆盖,但第二次又被覆盖 toBlue.call(this); //还必须把this传递到切换器里 }; /* 综上的三个问题:覆盖问题、可读性问题、this传递问题。咱们来建立一个自定义的事 件处理函数,来解决以上三个问题。 */ function addEvent(obj,type,fn){ //取代传统事件处理函数 var saved=null; //保存每次触发的事件处理函数 if(typeof obj['on'+type]=='function') { //判断是否是事件 saved=obj['on'+type]; //若是有,保存起来 } obj['on'+type]=function(){ //而后执行 if(saved)saved(); //执行上一个 fn.call(this); //执行函数,把this传递过去 }; } addEvent(window,'load', function(){ //执行到了 alert('Lee'); }); addEvent(window,'load', function(){ //执行到了 alert('Mr.Lee'); }); /* PS:以上编写的自定义事件处理函数,还有一个问题没有处理,就是两个相同函数名 的函数误注册了两次或屡次,那么应该把多余的屏蔽掉。那,咱们就须要把事件处理函数进 行遍历,若是有一样名称的函数名就不添加便可。(这里就不作了) */ addEvent(window,'load', init); //注册第一次 addEvent(window,'load', init); //注册第二次,应该忽略 function init(){ alert('Lee'); } //用自定义事件函数注册到切换器上查看效果: addEvent(window,'load', function(){ var box=document.getElementById('box'); addEvent(box,'click', toBlue); }); function toRed(){ this.className='red'; addEvent(this,'click', toBlue); } functiontoBlue(){ this.className='blue'; addEvent(this,'click', toRed); } /*PS:当你单击不少不少次切换后,浏览器直接卡死,或者弹出一个错误:toomuch recursion(太多的递归)。主要的缘由是,每次切换事件的时候,都保存下来,没有把无用的 移除,致使越积越多,最后卡死。*/ function removeEvent(obj,type){ if(obj['on']+type)obj['on'+type]=null; //删除事件处理函数 } /* 以上的删除事件处理函数只不过是一刀切的删除了,这样虽然解决了卡死和太多递归的 问题。但其余的事件处理函数也一并被删除了,致使最后得不到本身想要的结果。若是想要 只删除指定的函数中的事件处理函数,那就须要遍历,查找。(这里就不作了) */ /* 二.W3C事件处理函数 “DOM2级事件”定义了两个方法,用于添加事件和删除事件处理程序的操做: addEventListener()和removeEventListener()。全部DOM节点中都包含这两个方法,而且它 们都接受3个参数;事件名、函数、冒泡或捕获的布尔值(true表示捕获,false表示冒泡)。 */ window.addEventListener('load',function(){ alert('Lee'); },false); window.addEventListener('load',function(){ alert('Mr.Lee'); },false); /*PS:W3C的现代事件绑定比咱们自定义的好处就是:1.不须要自定义了;2.能够屏蔽相 同的函数;3.能够设置冒泡和捕获。*/ window.addEventListener('load',init,false); //第一次执行了 window.addEventListener('load',init,false); //第二次被屏蔽了 function init(){ alert('Lee'); } //事件切换器 window.addEventListener('load',function(){ var box=document.getElementById('box'); box.addEventListener('click',function(){ //不会被误删 alert('Lee'); },false); box.addEventListener('click',toBlue,false); //引入切换也不会太多递归卡死 },false); function toRed(){ this.className='red'; this.removeEventListener('click',toRed,false); this.addEventListener('click',toBlue,false); } function toBlue(){ this.className='blue'; this.removeEventListener('click',toBlue,false); this.addEventListener('click',toRed,false); } /* 设置冒泡和捕获阶段 以前咱们上一章了解了事件冒泡,即从里到外触发。咱们也能够经过event对象来阻止 某一阶段的冒泡。那么W3C现代事件绑定能够设置冒泡和捕获。 */ document.addEventListener('click',function(){ alert('document'); },true); //把布尔值设置成true,则为捕获 box.addEventListener('click',function(){ alert('Lee'); },true); //把布尔值设置成false,则为冒泡 /*三.IE事件处理函数 IE实现了与DOM中相似的两个方法:attachEvent()和detachEvent()。这两个方法接受 相同的参数:事件名称和函数。 在使用这两组函数的时候,先把区别说一下:1.IE不支持捕获,只支持冒泡;2.IE添加 事件不能屏蔽重复的函数;3.IE中的this指向的是window而不是DOM对象。4.在传统事 件上,IE是没法接受到event对象的,但使用了attchEvent()却能够,但有些区别。*/ window.attachEvent('onload',function(){ var box=document.getElementById('box'); box.attachEvent('onclick',toBlue); }); function toRed(){ var that=window.event.srcElement; that.className='red'; that.detachEvent('onclick',toRed); that.attachEvent('onclick',toBlue); } function toBlue(){ var that=window.event.srcElement; that.className='blue'; that.detachEvent('onclick',toBlue); that.attachEvent('onclick',toRed); } /* PS:IE不支持捕获,无解。IE不能屏蔽,须要单独扩展或者自定义事件处理。IE不能 传递this,能够call过去。 */ window.attachEvent('onload',function(){ var box=document.getElementById('box'); box.attachEvent('onclick',function(){ alert(this===window); //this指向的window }); }); window.attachEvent('onload',function(){ var box=document.getElementById('box'); box.attachEvent('onclick',function(){ toBlue.call(box); //把this直接call过去 }); }); function toThis(){ alert(this.tagName); } /*在传统绑定上,IE是没法像W3C那样经过传参接受event对象,但若是使用了 attachEvent()却能够。*/ box.onclick=function(evt){ alert(evt); //undefined } box.attachEvent('onclick',function(evt){ alert(evt); //object alert(evt.type); //click }); box.attachEvent('onclick',function(evt){ alert(evt.srcElement===box); //true alert(window.event.srcElement===box); //true }); //最后,为了让IE和W3C能够兼容这个事件切换器,咱们能够写成以下方式: function addEvent(obj,type,fn){ //添加事件兼容 if(obj.addEventListener){ obj.addEventListener(type,fn); }else if(obj.attachEvent){ obj.attachEvent('on'+type,fn); } } function removeEvent(obj,type,fn){ //移除事件兼容 if(obj.removeEventListener){ obj.removeEventListener(type,fn); }else if(obj.detachEvent){ obj.detachEvent('on'+type,fn); } } function getTarget(evt){ //获得事件目标 if(evt.target){ return evt.target; }else if(window.event.srcElement){ return window.event.srcElement; } } /*PS:调用忽略,IE兼容的事件,若是要传递this,改为call便可。 PS:IE中的事件绑定函数attachEvent()和detachEvent()可能在实践中不去使用,有几个 缘由:1.IE9就将全面支持W3C中的事件绑定函数;2.IE的事件绑定函数没法传递this;3.IE 的事件绑定函数不支持捕获;4.同一个函数注册绑定后,没有屏蔽掉;5.有内存泄漏的问题。 至于怎么替代,咱们将在之后的项目课程中探讨。*/ /*四.事件对象的其余补充 在W3C提供了一个属性:relatedTarget;这个属性能够在mouseover和mouseout事件 中获取从哪里移入和从哪里移出的DOM对象。*/ box.onmouseover=function(evt){ //鼠标移入box alert(evt.relatedTarget); //获取移入box最近的那个元素对象 } //span box.onmouseout=function(evt){ //鼠标移出box alert(evt.relatedTarget); //获取移出box最近的那个元素对象 } //span //IE提供了两组分别用于移入移出的属性:fromElement和toElement,分别对应mouseover和mouseout。 box.onmouseover=function(evt){ //鼠标移入box alert(window.event.fromElement.tagName);//获取移入box最近的那个元素对象span } box.onmouseout=function(evt){ //鼠标移入box alert(window.event.toElement.tagName); //获取移入box最近的那个元素对象span } //PS:fromElement和toElement若是分别对应相反的鼠标事件,没有任何意义。 //剩下要作的就是跨浏览器兼容操做: function getTarget(evt){ var e=evt|| window.event; //获得事件对象 if(e.srcElement){ //若是支持srcElement,表示IE if(e.type=='mouseover') { //若是是over return e.fromElement; //就使用from }elseif(e.type=='mouseout') { //若是是out return e.toElement; //就使用to } }else if(e.relatedTarget){ //若是支持relatedTarget,表示W3C return e.relatedTarget; } } /*有时咱们须要阻止事件的默认行为,好比:一个超连接的默认行为就点击而后跳转到指 定的页面。那么阻止默认行为就能够屏蔽跳转的这种操做,而实现自定义操做。 取消事件默认行为还有一种不规范的作法,就是返回false。*/ link.onclick=function(){ alert('Lee'); return false; //直接给个假,就不会跳转了。 }; /*PS:虽然returnfalse;能够实现这个功能,但有漏洞;第一:必须写到最后,这样致使 中间的代码执行后,有可能执行不到returnfalse;第二:returnfalse写到最前那么以后的自 定义操做就失效了。因此,最好的方法应该是在最前面就阻止默认行为,而且后面还能执行 代码。*/ link.onclick=function(evt){ evt.preventDefault(); //W3C,阻止默认行为,放哪里均可以 alert('Lee'); }; link.onclick=function(evt){ //IE,阻止默认行为 window.event.returnValue=false; alert('Lee'); }; //跨浏览器兼容 function preDef(evt){ var e=evt|| window.event; if(e.preventDefault){ e.preventDefault(); }else{ e.returnValue=false; } } /*上下文菜单事件:contextmenu,当咱们右击网页的时候,会自动出现windows自带的 菜单。那么咱们能够使用contextmenu事件来修改咱们指定的菜单,但前提是把右击的默认 行为取消掉。*/ addEvent(window,'load', function(){ var text=document.getElementById('text'); addEvent(text,'contextmenu', function(evt){ var e=evt|| window.event; preDef(e); var menu=document.getElementById('menu'); menu.style.left=e.clientX+'px'; menu.style.top=e.clientY+'px'; menu.style.visibility='visible'; addEvent(document,'click', function(){ document.getElementById('myMenu').style.visibility='hidden'; }); }); }); /* PS:contextmenu事件很经常使用,这直接致使浏览器兼容性较为稳定。 卸载前事件:beforeunload,这个事件能够帮助在离开本页的时候给出相应的提示,“离 开”或者“返回”操做。 */ addEvent(window,'beforeunload', function(evt){ preDef(evt); }); //鼠标滚轮(mousewheel)和DOMMouseScroll,用于获取鼠标上下滚轮的距离。 addEvent(document,'mousewheel', function(evt){ //非火狐 alert(getWD(evt)); }); addEvent(document,'DOMMouseScroll', function(evt){ //火狐 alert(getWD(evt)); }); function getWD(evt){ var e=evt|| window.event; if(e.wheelDelta){ return e.wheelDelta; }else if(e.detail){ return-evt.detail*30; //保持计算的统一 } } /* PS:经过浏览器检测能够肯定火狐只执行DOMMouseScroll。 DOMContentLoaded事件和readystatechange事件,有关DOM加载方面的事件,关于这 两个事件的内容很是多且繁杂,咱们先点明在这里,在项目课程中使用的时候详细讨论。 */
第25章 表单处理
/* 为了分担服务器处理表单的压力,JavaScript提供了一些解决方案,从而大大打破了处 处依赖服务器的局面。 */ /*一.表单介绍 在HTML中,表单是由<form>元素来表示的,而在JavaScript中,表单对应的则是 HTMLFormElement类型。HTMLFormElement继承了HTMLElement,所以它拥有HTML元 素具备的默认属性,而且还独有本身的属性和方法:*/ /* HTMLFormElement属性和方法 属性或方法 说明 acceptCharset 服务器可以处理的字符集 action 接受请求的URL elements 表单中全部控件的集合 enctype 请求的编码类型 length 表单中控件的数量 name 表单的名称 target 用于发送请求和接受响应的窗口名称 reset() 将全部表单重置 submit() 提交表单 */ //获取表单<form>对象的方法有不少种,以下: document.getElementById('myForm'); //使用ID获取<form>元素 document.getElementsByTagName('form')[0]; //使用获取第一个元素方式获取 document.forms[0]; //使用forms的数字下标获取元素 document.forms['yourForm']; //使用forms的名称下标获取元素 document.yourForm; //使用name名称直接获取元素 /*PS:最后一种方法使用name名称直接获取元素,已经不推荐使用,这是向下兼容的早 期用法。问题颇多,好比有两个相同名称的,变成数组;并且这种方式之后有可能会不兼容。*/ /*提交表单 经过事件对象,能够阻止submit的默认行为,submit事件的默认行为就是携带数据跳 转到指定页面。*/ addEvent(fm,'submit', function(evt){ preDef(evt); }); /*咱们能够能够使用submit()方法来自定义触发submit事件,也就是说,并不必定非要点 击submit按钮才能提交。*/ if(e.ctrlKey&&e.keyCode==13)fm.submit(); //判断按住了ctrl和enter键触发 //PS:在表单中尽可能避免使用name="submit"或id="submit"等命名,这会和submit()方法 //发生冲突致使没法提交。 /*提交数据最大的问题就是重复提交表单。由于各类缘由,当一条数据提交到服务器的时 候会出现延迟等长时间没反映,致使用户不停的点击提交,从而使得重复提交了不少相同的 请求,或形成错误、或写入数据库多条相同信息。*/ addEvent(fm,'submit', function(evt){ //模拟延迟 preDef(evt); setTimeout(function(){ fm.submit(); },3000); }); /*有两种方法能够解决这种问题:第一种就是提交以后,马上禁用点击按钮;第二种就是 提交以后取消后续的表单提交操做。*/ document.getElementById('sub').disabled=true; //将按钮禁用 var flag=false //设置一个监听变量 if(flag==true)return //若是存在返回退出事件 flag=true; //不然肯定是第一次,设置为true //PS:在某些浏览器,F5只能起到缓存刷新的效果,有可能获取不到真正的源头更新的 //数据。那么使用ctrl+F5就能够把源头给刷出来。 /*重置表单 用户点击重置按钮时,表单会被初始化。虽然这个按钮还得以保留,但目前的Web已 经不多去使用了。由于用户已经填写好各类数据,不当心点了重置就会所有清空,用户体验 极差。 有两种方法调用reset事件,第一个就是直接type="reset"便可;第二个就是使用fm.reset() 方法调用便可。*/ <input type="reset" value="重置"/> //不须要JS代码便可实现 addEvent(document,'click',function(){ fm.reset(); //使用JS方法实现重置 }); addEvent(fm,'reset',function(){ //获取重置按钮 // }); /*表单字段 若是想访问表单元素,能够使用以前章节讲到的DOM方法访问。但使用原生的DOM 访问虽然比较通用,但不是很便利。表单处理中,咱们建议使用HTMLDOM,它有本身的 elements属性,该属性是表单中全部元素的集合。*/ fm.elements[0]; //获取第一个表单字段元素 fm.elements['user']; //获取name是user的表单字段元素 fm.elements.length; //获取全部表单字段的数量 //若是多个表单字段都使用同一个name,那么就会返回该name的NodeList表单列表。 fm.elements['sex']; //获取相同name表单字段列表 /*PS:咱们是经过fm.elements[0]来获取第一个表单字段的,但也能够使用fm[0]直接访问 第一个字段。由于fm[0]访问方式是为了向下兼容的,因此,咱们建议你们使用elements属 性来获取。 共有的表单字段属性 除了<fieldset>元素以外,全部表单字段都拥有相同的一组属性。因为<input>类型能够 表示多种表单字段,所以有些属性只适用于某些字段。如下罗列出共有的属性:*/ /* 属性或方法 说明 disabled 布尔值,表示当前字段是否被禁用 form 指向当前字段所属表单的指针,只读 name 当前字段的名称 readOnly 布尔值,表示当前字段是否只读 tabIndex 表示当前字段的切换 type 当前字段的类型 value 当前字段的值 */ /* 这些属性其实就是HTML表单里的属性,在XHTML课程中已经详细讲解过,这里不 一个个赘述,重点看几个最经常使用的。 */ fm.elements[0].value; //获取和设置value fm.elements[0].form==fm; //查看当前字段所属表单 fm.elements[0].disabled=true; //禁用当前字段 fm.elements[0].type='checkbox'; //修改字段类型,极不推荐 /*除了<fieldset>字段以外,全部表单字段都有type属性。对于<input>元素,这个值等于 HTML属性的type值。对于非<input>元素,这个type的属性值以下:*/ /* 元素说明 HTML标签 type属性的值 单选列表 <select>...</select> select-one 多选列表 <selectmultiple>...</select> select-multiple 自定义按钮 <button>...</button> button 自定义非提交按钮 <buttontype="button">...</button> button 自定义重置按钮 <buttontype="reset">...</button> reset 自定义提交按钮 <buttontype="submit">...</button> submit */ /*PS:<input>和<button>元素的type属性是能够动态修改的,而<select>元素的type属性 则是只读的。(在没必要要的状况下,建议不修改type)。 共有的表单字段方法 每一个表单字段都有两个方法:foucs()和blur()。*/ /*方法 说明 focus() 将焦点定位到表单字段里 blur() 从元素中将焦点移走*/ fm.elements[0].focus(); //将焦点移入 fm.elements[0].blur(); //将焦点移出 /* 共有的表单字段事件 表单共有的字段事件有如下三种: */ /* 事件名 说明 blur 当字段失去焦点时触发 change 对于<input>和<textarea>元素,在改变value并失去焦点时触发;对于<select>元素,在改变选项时触发 focus 当前字段获取焦点时触发 */ addEvent(textField,'focus', function(){ //缓存blur和change再测试一下 alert('Lee'); }); /* PS:关于blur和change事件的关系,并无严格的规定。在某些浏览器中,blur事件 会先于change事件发生;而在其余浏览器中,则刚好相反。 */ /* 二.文本框脚本 在HTML中,有两种方式来表现文本框:一种是单行文本框<inputtype="text">,一种 是多行文本框<textarea>。虽然<input>在字面上有value值,而<textarea>却没有,但经过都 能够经过value获取他们的值。 */ var textField=fm.elements[0]; var areaField=fm.elements[1]; alert(textField.value+',' +areaField.value); //获得value值 /* PS:使用表单的value是最推荐使用的,它是HTMLDOM中的属性,不建议使用标准 DOM的方法。也就是说不要使用getAttribute()获取value值。缘由很简单,对value属性的 修改,不必定会反映在DOM中。 除了value值,还有一个属性对应的是defaultValue,能够获得本来的value值,不会因 为值的改变而变化。 */ alert(textField.defaultValue); //获得最初的value值 //选择文本 //使用select()方法,能够将文本框里的文本选中,而且将焦点设置到文本框中。 textField.select(); //选中文本框中的文本 /*选择部分文本 在使用文本框内容的时候,咱们有时要直接选定部分文本,这个行为尚未标准。Firefox 的解决方案是:setSelectionRange()方法。这个方法接受两个参数:索引和长度。*/ textField.setSelectionRange(0,1); //选择第一个字符 textField.focus(); //焦点移入 textField.setSelectionRange(0,textField.value.length); //选择所有 textField.focus(); //焦点移入 /*除了IE,其余浏览器都支持这种写法(IE9+支持),那么IE想要选择部分文本,能够使 用IE的范围操做。*/ var range=textField.createTextRange(); //建立一个文本范围对象 range.collapse(true); //将指针移到起点 range.moveStart('character',0); //移动起点,character表示逐字移动 range.moveEnd('character',1); //移动终点,同上 range.select(); //焦点选定 /*PS:关于IE范围的详细讲解,咱们将在从此的课程中继续讨论。而且W3C也有本身 的范围。*/ //选择部分文本实现跨浏览器兼容 function selectText(text,start,stop){ if(text.setSelectionRange){ text.setSelectionRange(start,stop); text.focus(); }elseif(text.createTextRange){ var range=text.createTextRange(); range.collapse(true); range.moveStart('character',start); range.moveEnd('character',stop-start); //IE用终点减去起点获得字符数 range.select(); } } //使用select事件,能够选中文本框文本后触发。 addEvent(textField,'select', function(){ alert(this.value); //IE事件须要传递this才能够这么写 }); /*取得选择的文本 若是咱们想要取得选择的那个文本,就必须使用一些手段。目前位置,没有任何规范解 决这个问题。Firefox为文本框提供了两个属性:selectionStart和selectionEnd。*/ addEvent(textField,'select', function(){ alert(this.value.substring(this.selectionStart,this.selectionEnd)); }); /*除了IE,其余浏览器均支持这两个属性(IE9+已支持)。IE不支持,而提供了另外一个方 案:selection对象,属于document。这个对象保存着用户在整个文档范围内选择的文本信息。 致使咱们须要作浏览器兼容。*/ function getSelectText(text){ if(typeof text.selectionStart=='number') { //非IE return text.value.substring(text.selectionStart,text.selectionEnd); }else if(document.selection){ //IE return document.selection.createRange().text; //获取IE选择的文本 } } /* PS:有一个最大的问题,就是IE在触发select事件的时候,在选择一个字符后当即触 发,而其余浏览器是选择想要的字符释放鼠标键后才触发。因此,若是使用alert()的话,导 致跨浏览器的不兼容。咱们没有办法让浏览器行为保持统一,但能够经过不去使用alert()来 解决。 */ addEvent(textField,'select', function(){ //alert(getSelectText(this)); //致使用户行为结果不一致 document.getElementById('box').innerHTML=getSelectText(this); }); /*过滤输入 为了使文本框输入指定的字符,咱们必须对输入进的字符进行验证。有一种作法是判断 字符是否合法,这是提交后操做的。那么咱们还能够在提交前限制某些字符,还过滤输入。*/ addEvent(areaField,'keypress', function(evt){ var e=evt|| window.event; var charCode=getCharCode(evt); //获得字符编码 if(!/\d/.test(String.fromCharCode(charCode))&&charCode>8){ //条件阻止默认 preDef(evt); } }); /* PS:前半段条件判断只有数字才能够输入,致使常规按键,好比光标键、退格键、删 除键等没法使用。部分浏览器好比Firfox,须要解放这些键,而非字符触发的编码均为0; 在Safari3以前的浏览器,也会被阻止,而它对应的字符编码所有为8,因此最后就加上 charCode>8的判断便可。 PS:固然,这种过滤仍是比较脆落的,咱们还但愿可以阻止裁剪、复制、粘贴和中文 字符输入操做才能真正屏蔽掉这些。 若是要阻止裁剪、复制和粘贴,那么咱们能够在剪贴板相关的事件上进行处理, JavaScript提供了六组剪贴板相关的事件: */ /* 事件名 说明 copy 在发生复制操做时触发 cut 在发生裁剪操做时触发 paste 在发生粘贴操做时触发 beforecopy 在发生复制操做前触发 beforecut 在发生裁剪操做前触发 beforepaste 在发生粘贴操做前触发 */ /* 因为剪贴板没有标准,致使不一样的浏览器有不一样的解释。Safari、Chrome和Firefox中, 凡是before前缀的事件,都须要在特定条件下触发。而IE则会在操做时以前触发带before 前缀的事件。 若是咱们想要禁用裁剪、复制、粘贴,那么只要阻止默认行为便可。 */ addEvent(areaField,'cut', function(evt){ //阻止裁剪 preDef(evt); }); addEvent(areaField,'copy', function(evt){ //阻止复制 preDef(evt); }); addEvent(areaField,'paste', function(evt){ //阻止粘贴 preDef(evt); }); /*当咱们裁剪和复制的时候,咱们能够访问剪贴板里的内容,但问题是FireFox,Opera 浏览器不支持访问剪贴板。而且,不一样的浏览器也有本身不一样的理解。因此,这里咱们就不 在赘述。 最后一个问题影响到可能会影响输入的因素就是:输入法。咱们知道,中文输入法,它的 原理是在输入法面板上先存储文本,按下回车就写入英文文本,按下空格就写入中文文本。 有一种解决方案是经过CSS来禁止调出输入法:*/ style="ime-mode:disabled" //CSS直接编写 areaField.style.imeMode='disabled'; //或在JS里设置也能够 /*PS:但咱们也发先,Chrome浏览器却没法禁止输入法调出。因此,为了解决谷歌浏览 器的问题,最好还要使用正则验证已输入的文本。*/ addEvent(areaField,'keyup', function(evt){ //keyup弹起的时候 this.value=this.value.replace(/[^\d]/g,''); //把非数字都替换成空 }); /* 自动切换焦点 为了增长表单字段的易用性,不少字段在知足必定条件时(好比长度),就会自动切换到 下一个字段上继续填写。 */ <input type="text" name="user1" maxlength="1"/> //只能写1个 <input type="text" name="user2" maxlength="2"/> //只能写2个 <input type="text" name="user3" maxlength="3"/> //只能写3个 function tabForward(evt){ var e=evt|| window.event; var target=getTarget(evt); //判断当前长度是否和指定长度一致 if(target.value.length==target.maxLength){ //遍历全部字段 for(vari=0;i<fm.elements.length;i++){ //找到当前字段 if(fm.elements[i]==target){ //就把焦点移入下一个 fm.elements[i+1].focus(); //中途返回 return; } } } } /*三.选择框脚本 选择框是经过<select>和<option>元素建立的,除了通用的一些属性和方法外, HTMLSelectElement类型还提供了以下的属性和方法:*/ /* HTMLSelectElement对象 属性/方法 说明 add(new,rel) 插入新元素,并指定位置 multiple 布尔值,是否容许多项选择 options <option>元素的HTMLColletion集合 remove(index) 移除给定位置的选项 selectedIndex 基于0的选中项的索引,若是没有选中项,则值为-1 size 选择框中可见的行数 */ /*在DOM中,每一个<option>元素都有一个HTMLOptionElement对象,以便访问数据,这 个对象有以下一些属性:*/ /* HTMLOptionElement对象 属性 说明 index 当前选项在options集合中的索引 label 当前选项的标签 selected 布尔值,表示当前选项是否被选中 text 选项的文本 value 选项的值 */ var city=fm.elements['city']; //HTMLSelectElement alert(city.options); //HTMLOptionsCollection alert(city.options[0]); //HTMLOptionElement alert(city.type); //select-one /* PS:选择框里的type属性有多是:select-one,也有多是:select-multiple,这取决 于HTML代码中有没有multiple属性。 */ alert(city.options[0].firstChild.nodeValue); //上海t,获取text值,不推荐的作法 alert(city.options[0].getAttribute('value')); //上海v,获取value值,不推荐的作法 alert(city.options[0].text); //上海t,获取text值,推荐 alert(city.options[0].value); //上海v,获取value值,推荐 /*PS:操做select时,最好使用HTMLDOM,由于全部浏览器兼容的很好。而若是使用 标准DOM,会由于不一样的浏览器致使不一样的结果。 PS:当选项没有value值的时候,IE会返回空字符串,其余浏览器会返回text值。 选择选项 对于只能选择一项的选择框,使用selectedIndex属性最为简单。*/ addEvent(city,'change', function(){ alert(this.selectedIndex); //获得当前选项的索引,从0开始 alert(this.options[this.selectedIndex].text); //获得当前选项的text值 alert(this.options[this.selectedIndex].value); //获得当前选项的value值 }); //PS:若是是多项选择,他始终返回的是第一个项。 city.selectedIndex=1; //设置selectedIndex能够定位某个索引 //经过option的属性(布尔值),也能够设置某个索引,设置为true便可。 city.options[0].selected=true; //设置第一个索引 /*而selected和selectedIndex在用途上最大的区别是,selected是返回的布尔值,因此一 般用于判断上;而selectedIndex是数值,通常用于设置和获取。*/ addEvent(city,'change', function(){ if(this.options[2].selected==true){ //判断第三个选项是否被选定 alert('选择正确!'); } }); //添加选项 //如需动态的添加选项咱们有两种方案:DOM和Option构造函数。 var option=document.createElement('option'); option.appendChild(document.createTextNode('北京t')); option.setAttribute('value','北京v') city.appendChild(option); //使用Option构造函数建立: var option=newOption('北京t','北京v'); city.appendChild(option); //IE出现bug //使用add()方法来添加选项: var option=newOption('北京t','北京v'); city.add(option,0); //0,表示添加到第一位 /*PS:在DOM规定,add()中两个参数是必须的,若是不肯定索引,那么第二个参数设 置null便可,即默认移入最后一个选项。但这是IE中规定第二个参数是可选的,因此设置 null表示放入不存在的位置,致使失踪,为了兼容性,咱们传递undefined便可兼容。*/ city.add(option,null); //IE不显示了 city.add(option,undefined); //兼容了 //移除选项 //有三种方式能够移除某一个选项:DOM移除、remove()方法移除和null移除。 city.removeChild(city.options[0]); //DOM移除 city.remove(0); //remove()移除,推荐 city.options[0]=null; //null移除 //PS:当第一项移除后,下面的项,往上顶,因此不停的移除第一项,便可所有移除。 //移动选项 //若是有两个选择框,把第一个选择框里的第一项移到第二个选择框里,而且第一个选择 //框里的第一项被移除。 var city=fm.elements['city']; //第一个选择框 var info=fm.elements['info']; //第二个选择框 info.appendChild(city.options[0]); //移动,被自我删除 /*排列选项 选择框提供了一个index属性,能够获得当前选项的索引值,和selectedIndex的区别是, 一个是选择框对象的调用,一个是选项对象的调用。*/ var option1=city.options[1]; city.insertBefore(option1,city.options[option1.index-1]); //往下移动移位 //单选按钮 //经过checked属性来获取单选按钮的值。 for(var i=0;i<fm.sex.length;i++){ //循环单选按钮 if(fm.sex[i].checked==true){ //遍历每个找出选中的那个 alert(fm.sex[i].value); //获得值 } } //PS:除了checked属性以外,单选按钮还有一个defaultChecked按钮,它获取的是本来 //的checked按钮对象,而不会由于checked的改变而改变。 if(fm.sex[i].defaultChecked==true){ alert(fm.sex[i].value); } //复选按钮 //经过checked属性来获取复选按钮的值。复选按钮也具备defaultChecked属性。 var love=''; for(vari=0;i<fm.love.length;i++){ if(fm.love[i].checked==true){ love+=fm.love[i].value; } } alert(love);
第26章 错误处理与调试
/*JavaScript在错误处理调试上一直是它的软肋,若是脚本出错,给出的提示常常也让人 摸不着头脑。ECMAScript第3版为了解决这个问题引入了try...catch和throw语句以及一些 错误类型,让开发人员更加适时的处理错误。*/ /*一.浏览器错误报告 随着浏览器的不断升级,JavaScript代码的调试能力也逐渐变强。IE、Firefox、Safari、 Chrome和Opera等浏览器,都具有报告JavaScript错误的机制。只不过,浏览器通常面向 的是普通用户,默认状况下会隐藏此类信息。 IE:在默认状况下,左下角会出现错误报告,双击这个图标,能够看到错误消息对话框。 若是开启禁止脚本调试,那么出错的时候,会弹出错误调试框。设置方法为:工具->Internet Options选项->高级->禁用脚本调试,取消勾选便可。 Firefox:在默认状况下,错误不会经过浏览器给出提示。但在后台的错误控制台能够查 看。查看方法为:工具->[Web开发者]->Web控制台|错误控制台。除了浏览器自带的,开发 人员为Firefox提供了一个强大的插件:Firebug。它不但能够提示错误,还能够调试JavaScript 和CSS、DOM、网络连接错误等。 Safari:在默认状况下,错误不会经过浏览器给出提示。因此,咱们须要开启它。查看 方法为:显示菜单栏->编辑->偏好设置->高级->在菜单栏中显示开发->显示Web检查器|显示 错误控制器。 Opera:在默认状况下,错误会被隐藏起来。打开错误记录的方式为:显示菜单栏->查 看->开发者工具->错误控制台。 Chrome:在默认状况下,错误会被隐藏起来。打开错误记录的方法为:工具->JavaScript 控制台。*/ /*二.错误处理 良好的错误处理机制能够及时的提醒用户,知道发生了什么事,而不会惶恐不安。为此, 做为开发人员,咱们必须理解在处理JavaScript错误的时候,都有哪些手段和工具能够利用。 try-catch语句 ECMA262第3版引入了try-catch语句,做为JavaScript中处理异常的一种标准方式。*/ try{ //尝试着执行try包含的代码 window.abcdefg(); //不存在的方法 }catch(e){ //若是有错误,执行catch,e是异常对象 alert('发生错误啦,错误信息为:' +e); //直接打印调用toString()方法 } //在e对象中,ECMA-262还规定了两个属性:message和name,分别打印出信息和名称。 alert('错误名称:' +e.name); alert('错误名称:' +e.message); /*PS:Opera9以前的版本不支持这个属性。而且IE提供了和message彻底相同的 description属性、还添加了number属性提示内部错误数量。Firefox提供了fileName(文件名)、 lineNumber(错误行号)和stack(栈跟踪信息)。Safari添加了line(行号)、sourceId(内部错误代 码)和sourceURL(内部错误URL)。因此,要跨浏览器使用,那么最好只使用通用的message。 finally子句 finally语句做为try-catch的可选语句,无论是否发生异常处理,都会执行。而且无论try 或是catch里包含return语句,也不会阻止finally执行。*/ try{ window.abcdefg(); }catch(e){ alert('发生错误啦,错误信息为:' +e.stack); }finally{ //老是会被执行 alert('我都会执行!'); } /* PS:finally的做用通常是为了防止出现异常后,没法往下再执行的备用。也就是说,如 果有一些清理操做,那么出现异常后,就执行不到清理操做,那么能够把这些清理操做放到 finally里便可。 错误类型 执行代码时可能会发生的错误有不少种。每种错误都有对应的错误类型,ECMA-262 定义了7种错误类型: 1.Error 2.EvalError 3.RangeError 4.ReferenceError 5.SyntaxError 6.TypeError 7.URIError 其中,Error是基类型(其余六种类型的父类型),其余类型继承自它。Error类型不多见, 通常由浏览器抛出的。这个基类型主要用于开发人员抛出自定义错误。 PS:抛出的意思,就是当前错误没法处理,丢给另一我的,好比丢给一个错误对象。 */ new Array(-5); //抛出RangeError(范围) /*错误信息为:RangeError:invalidarraylength(无效的数组的长度) PS:RangeError错误通常在数值超出相应范围时触发*/ var box=a; //抛出ReferenceError(引用) /*错误信息为:ReferenceError:aisnotdefined(a是没有定义的) PS:ReferenceError一般访问不存在的变量产生这种错误*/ a $ b; //抛出SyntaxError(语法) //错误信息为:SyntaxError:missing;beforestatement(失踪;语句以前) //PS:SyntaxError一般是语法错误致使的 new 10; //抛出TypeError(类型) //错误信息为:TypeError:10isnotaconstructor(10不是一个构造函数) //PS:TypeError一般是类型不匹配致使的 /* PS:EvalError类型表示全局函数eval()的使用方式与定义的不一样时抛出,但实际上并不 能产生这个错误,因此实际上碰到的可能性不大。 PS:在使用encodeURI()和decodeURI()时,若是URI格式不正确时,会致使URIError 错误。但由于URI的兼容性很是强,致使这种错误几乎见不到。 */ alert(encodeURI('李炎恢')); //利用不一样的错误类型,能够更加恰当的给出错误信息或处理。 try{ new 10; }catch(e){ if(e instanceof TypeError){ //若是是类型错误,那就执行这里 alert('发生了类型错误,错误信息为:' +e.message); }else{ alert('发生了未知错误!'); } } /* 善用try-catch 在明明知道某个地方会产生错误,能够经过修改代码来解决的地方,是不适合用 try-catch的。或者是那种不一样浏览器兼容性错误致使错误的也不太适合,由于能够经过判断 浏览器或者判断这款浏览器是否存在此属性和方法来解决。 */ try{ var box=document.getElementbyid('box'); //单词大小写错误,致使类型错误 }catch(e){ //这种状况不必try-catch alert(e); } try{ alert(innerWidth); //W3C支持,IE报错 }catch(e){ alert(document.documentElement.clientWidth); //兼容IE } /* PS:常规错误和这种浏览器兼容错误,咱们都不建议使用try-catch。由于常规错误能够 修改代码便可解决,浏览器兼容错误,能够经过普通if判断便可。而且try-catch比通常语 句消耗资源更多,负担更大。因此,在万不得已,没法修改代码,不能经过普通判断的状况 下才去使用try-catch,好比后面的Ajax技术。 抛出错误 使用catch来处理错误信息,若是处理不了,咱们就把它抛出丢掉。抛出错误,其实就 是在浏览器显示一个错误信息,只不过,错误信息能够自定义,更加精确和具体。 */ try{ new 10; }catch(e){ if(e instanceof TypeError){ throw new TypeError('实例化的类型致使错误!'); //直接中文解释错误信息 }else{ throw new Error('抛出未知错误!'); } } //PS:IE浏览器只支持Error抛出的错误,其余错误类型不支持。 //三.错误事件 //error事件是当某个DOM对象产生错误的时候触发。 addEvent(window,'error', function(){ alert('发生错误啦!') }); new 10; //写在后面 <img src="123.jpg" onerror="alert('图像加载错误!')" /> /* 四.错误处理策略 因为JavaScript错误均可能致使网页没法使用,因此什么时候搞清楚及为何发生错误相当 重要。这样,咱们才能对此采起正确的应对方案。 常见的错误类型 由于JavaScript是松散弱类型语言,不少错误的产生是在运行期间的。通常来讲,须要 关注3种错误: 1.类型转换错误;2.数据类型错误;3.通讯错误,这三种错误通常会在特定的模式下或 者没有对值进行充分检查的状况下发生。 类型转换错误 在一些判断比较的时候,好比数组比较,有相等和全等两种: */ alert(1=='1'); //true alert(1==='1'); //false alert(1==true); //true alert(1===true); //false /*PS:因为这个特性,咱们建议在这种会类型转换的判断,强烈推荐使用全等,以保证 判断的正确性。*/ var box=10; //能够试试0 if(box){ //10自动转换为布尔值为true alert(box); } /*PS:由于0会自动转换为false,其实0也是数值,也是有值的,不该该认为是false, 因此咱们要判断box是否是数值再去打印。*/ var box=0; if(typeof box=='number') { //判断box是number类型便可 alert(box); } /*PS:typeofbox=='number'这里也是用的相等,没有用全等呀?缘由是typeofbox自己 返回的就是类型的字符串,右边也是字符串,那不必验证类型,因此相等就够了。 数据类型错误 因为JavaScript是弱类型语言,在使用变量和传递参数以前,不会对它们进行比较来确 保数据类型的正确。因此,这样开发人员必须须要靠本身去检测。*/ function getQueryString(url){ //传递了非字符串,致使错误 var pos=url.indexOf('?'); return pos; } alert(getQueryString(1)); //PS:为了不这种错误的出现,咱们应该使用类型比较。 function getQueryString(url){ if(typeofurl=='string') { //判断了指定类型,就不会出错了 var pos=url.indexOf('?'); return pos; } } alert(getQueryString(1)); //对于传递参数除了限制数字、字符串以外,咱们对数组也要进行限制。 function sortArray(arr){ if(arr){ //只判断布尔值远远不够 alert(arr.sort()); } } varbox=[3,5,1]; sortArray(box); /* PS:只用if(arr)判断布尔值,那么数值、字符串、对象等都会自动转换为true,而这些 类型调用sort()方法好比会产生错误,这里提一下:空数组会自动转换为true而非false。 */ function sortArray(arr){ if(typeof arr.sort=='function') { //判断传递过来arr是否有sort方法 alert(arr.sort()); //就算这个绕过去了 alert(arr.reverse()); //这个就又绕不过去了 } } var box={ //建立一个自定义对象,添加sort方法 sort:function(){} }; sortArray(box); /*PS:这断代码本意是判断arr是否有sort方法,由于只有数组有sort方法,从而判断arr 是数组。但忘记了,自定义对象添加了sort方法就能够绕过这个判断,且arr还不是数组。*/ function sortArray(arr){ if(arr instanceof Array){ //使用instanceof判断是Array最为合适 alert(arr.sort()); } } var box=[3,5,1]; sortArray(box); /* 通讯错误 在使用url进行参数传递时,常常会传递一些中文名的参数或URL地址,在后台处理 时会发生转换乱码或错误,由于不一样的浏览器对传递的参数解释是不一样的,因此有必要使用 编码进行统一传递。 好比:?user=李炎恢&age=100 */ var url='?user=' +encodeURIComponent('李炎恢') +'&age=100'; //编码 /* PS:在AJAX章节中咱们会继续探讨通讯错误和编码问题。 五.调试技术 在JavaScript初期,浏览器并无针对JavaScript提供调试工具,因此开发人员就想出 了一套本身的调试方法,好比alert()。这个方法能够打印你怀疑的是否获得相应的值,或者 放在程序的某处来看看是否能执行,得知以前的代码无误。 */ var num1=1; var num2=b; //在这段先后加上alert('')调试错误 var result=num1+num2; alert(result); /* PS:使用alert('')来调试错误比较麻烦,重要裁剪和粘贴alert(''),若是遗忘掉没有删掉 用于调试的alert('')将特别头疼。因此,咱们如今须要更好的调试方法。 将消息记录到控制台 IE八、Firefox、Opera、Chrome和Safari都有JavaScript控制台,能够用来查看JavaScript 错误。对于Firefox,须要安装Firebug,其余浏览器直接使用console对象写入消息便可。 */ /* console 对象的方法 方法名 说明 error(message) 将错误消息记录到控制台 info(message) 将信息性消息记录到控制台 log(message) 将通常消息记录到控制台 warn(message) 将警告消息记录到控制台 */ console.error('错误!'); //红色带叉 console.info('信息!'); //白色带信息号 console.log('日志!'); //白色 console.warn('警告!'); //黄色带感叹号 //PS:这里以Firefox为标准,其余浏览器会稍有差别。 var num1=1; console.log(typeofnum1); //获得num1的类型 var num2='b'; console.log(typeofnum2); //获得num2的类型 var result=num1+num2; alert(result); //结果是1b,匪夷所思 /* PS:咱们误把num2赋值成字符串了,其实应该是数值,致使最后的结果是1b。那么 传统调试就必须使用alert(typeonum1)来看看是否是数值类型,比较麻烦,由于alert()会阻 断后面的执行,看过以后还要删,删完估计一下子又忘了,而后又要alert(typeofnum1)来加 深印象。若是用了console.log的话,全部要调试的变量一目了然,也不须要删除,放着也 没事。 将错误抛出 以前已经将结果错误的抛出,这里不在赘述。 */ if(typeof num2 !='number') thrownewError('变量必须是数值!'); /* 六.调试工具 IE八、Firefox、Chrome、Opera、Safari都自带了本身的调试工具,而开发人员只习惯了 Firefox一种,因此不少状况下,在Firefox开发调试,而后去其余浏览器作兼容。其实Firebug 工具提供了一种Web版的调试工具:Firebuglite。 如下是网页版直接调用调试工具的代码:直接复制到浏览器网址便可。 */ javascript:(function(F,i,r,e,b,u,g,L,I,T,E){if(F.getElementById(b))return;E=F[i+'NS']&&F.doc umentElement.namespaceURI;E=E?F[i+'NS'](E,'script'):F[i]('script');E[r]('id',b);E[r]('src',I+g+T); E[r](b,u);(F[e]('head')[0]||F[e]('body')[0]).appendChild(E);E=new%20Image;E[r]('src',I+L);})(doc ument,'createElement','setAttribute','getElementsByTagName','FirebugLite','4','firebug-lite.js','rele ases/lite/latest/skin/xp/sprite.png','https://getfirebug.com/','#startOpened'); /* 还有一种离线版,把firebug-lite下载好,载入工具便可,致使最终工具没法运行,其余 浏览器运行无缺。虽然Web版本的FirebugLite能够跨浏览器使用Firebug,但除了Firefox 原生的以外,都不支持断点、单步调试、监视、控制台等功能。好在,其余浏览器本身的调 试器都有。 PS:Chrome浏览器必须在服务器端方可有效。测试也发现,只能简单调试,若是遇到 错误,系统不能自动抛出错误给firebug-lite。 1.设置断点 咱们能够选择Script(脚本),点击要设置断点的JS脚本处,便可设置断点。当咱们须要 调试的时候,从断点初开始模拟运行,发现代码执行的流程和变化。 2.单步调试 设置完断点后,能够点击单步调试,一步步看代码执行的步骤和流程。上面有五个按钮: 从新运行:从新单步调试 断继:正常执行代码 单步进入:一步一步执行流程 单步跳过:跳到下一个函数块 单步退出:跳出执行到内部的函数 3.监控 单击“监控”选项卡上,能够查看在单步进入是,全部变量值的变化。你也能够新建监 控表达式来重点查看本身所关心的变量。 4.控制台 显示各类信息。以前已了解过。 PS:其余浏览器除IE8以上都可实现以上的调试功能,你们能够本身常识下。而咱们 主要采用Firebug进行调试而后兼容到其余浏览器的作法以提升开发效率。 */
第27章 Cookie与存储
/* 随着Web愈来愈复杂,开发者急切的须要可以本地化存储的脚本功能。这个时候,第 一个出现的方案:cookie诞生了。cookie的意图是:在本地的客户端的磁盘上以很小的文件 形式保存数据。 一.Cookie cookie也叫HTTPCookie,最初是客户端与服务器端进行会话使用的。好比,会员登陆, 下次回访网站时无须登陆了;或者是购物车,购买的商品没有及时付款,过两天发现购物车 里还有以前的商品列表。 HTTPCookie要求服务器对任意HTTP请求发送Set-Cookie,所以,Cookie的处理原则 上须要在服务器环境下进行。固然,如今大部分浏览器在客户端也能实现Cookie的生成和 获取。(目前Chrome不能够在客户端操做,其余浏览器都可) cookie的组成 cookie由名/值对形式的文本组成:name=value。完整格式为: name=value;[expires=date];[path=path];[domain=somewhere.com];[secure] 中括号是可选,name=value是必选。 */ document.cookie='user=' +encodeURIComponent('小明'); //编码写入 alert(decodeURIComponent(document.cookie)); //解码读取 /* expires=date 失效时间,若是没有声明,则为浏览器关闭后即失效。声明了失效时间, 那么时间到期后方能失效。 */ var date=newDate(); //建立一个 date.setDate(date.getDate()+7); document.cookie="user="+encodeURIComponent('小明') +";expires="+date; /* PS:能够经过Firefox浏览器查看和验证失效时间。若是要提早删除cookie也很是简单, 只要从新建立cookie把时间设置当前时间以前便可:date.getDate()-1或newDate(0)。 path=path访问路径,当设置了路径,那么只有设置的那个路径文件才能够访问cookie。 */ var path='/E:/%E5%A4%87%E8%AF%BE%E7%AC%94%E8%AE%B0/JS1/29/demo'; document.cookie="user="+encodeURIComponent('小明') +";path="+path; /* PS:为了操做方便,我直接把路径复制下来,而且增长了一个目录以强调效果。 domain=domain访问域名,用于限制只有设置的域名才能够访问,那么没有设置,会 默认限制为建立cookie的域名。 */ var domain='yc60.com'; document.cookie="user="+encodeURIComponent('小明') +";domain="+domain; /* PS:若是定义了yc60.com,那么在这个域名下的任何网页均可访问,若是定义了 v.yc60.com,那么只能在这个二级域名访问该cookie,而主域名和其余子域名则不能访问。 PS:设置域名,必须在当前域名绑定的服务器上设置,若是在yc60.com服务器上随意 设置其余域名,则会没法建立cookie。 secure 安全设置,指明必须经过安全的通讯通道来传输(HTTPS)才能获取cookie。 */ document.cookie="user="+encodeURIComponent('小明') +";secure"; /*PS:https安全通讯连接须要单独配置。 JavaScript设置、读取和删除并非特别的直观方便,咱们能够封装成函数来方便调用。*/ //建立cookie function setCookie(name,value,expires,path,domain,secure){ var cookieText=encodeURIComponent(name)+'=' +encodeURIComponent(value); if(expiresinstanceofDate) { cookieText+='; expires='+expires; } if(path){ cookieText+='; expires='+expires; } if(domain){ cookieText+='; domain='+domain; } if(secure){ cookieText+='; secure'; } document.cookie=cookieText; } //获取cookie function getCookie(name){ var cookieName=encodeURIComponent(name)+'='; var cookieStart=document.cookie.indexOf(cookieName); var cookieValue=null; if(cookieStart>-1){ var cookieEnd=document.cookie.indexOf(';',cookieStart); if(cookieEnd==-1){ cookieEnd=document.cookie.length; } cookieValue=decodeURIComponent( document.cookie.substring(cookieStart+cookieName.length,cookieEnd)); } return cookieValue; } //删除cookie function unsetCookie(name){ document.cookie=name+"=;expires="+newDate(0); } //失效天数,直接传一个天数便可 function setCookieDate(day){ if(typeof day=='number' &&day>0){ var date = new Date(); date.setDate(date.getDate()+day); }else{ throw new Error('传递的day必须是一个天数,必须比0大'); } return date; } /* 二.cookie局限性 cookie虽然在持久保存客户端用户数据提供了方便,分担了服务器存储的负担。可是还 有不少局限性的。 第一:每一个特定的域名下最多生成20个cookie(根据不一样的浏览器有所区别)。 1.IE6或更低版本最多20个cookie 2.IE7和以后的版本最多能够50个cookie。IE7最初也只能20个,以后因被升级不定后 增长了。 3.Firefox最多50个cookie 4.Opera最多30个cookie 5.Safari和Chrome没有作硬性限制。 PS:为了更好的兼容性,因此按照最低的要求来,也就是最多不得超过20个cookie。 当超过指定的cookie时,浏览器会清理掉早期的cookie。IE和Opera会清理近期最少使用 的cookie,Firefox会随机清理cookie。 第二:cookie的最大大约为4096字节(4k),为了更好的兼容性,通常不能超过4095字 节便可。 第三:cookie存储在客户端的文本文件,因此特别重要和敏感的数据是不建议保存在 cookie的。好比银行卡号,用户密码等。 三.其余存储 IE提供了一种存储能够持久化用户数据,叫作userData,从IE5.0就开始支持。每一个数 据最多128K,每一个域名下最多1M。这个持久化数据存放在缓存中,若是缓存没有清理, 那么会一直存在。 */ <div style="behavior:url(#default#userData)" id="box"></div> addEvent(window,'load', function(){ var box=document.getElementById('box'); box.setAttribute('name',encodeURIComponent('小明')); box.expires=setCookieDate(7); box.save('bookinfo'); //box.removeAttribute('name'); //删除userDate //box.save('bookinfo'); box.load('bookinfo'); alert(decodeURIComponent(box.getAttribute('name'))); }); /* PS:这个数据文件也是保存在cookie目录中,只要清除cookie便可。若是指定过时日 期,则到期后自动删除,若是没有指定就是永久保存。 Web存储 在比较高版本的浏览器,JavaScript提供了sessionStorage和globalStorage。在HTML5 中提供了localStorage来取代globalStorage。而浏览器最低版本为:IE8+、Firefox3.5+、Chrome 4+和Opera10.5+。 PS:因为这三个对浏览器版本要求较高,咱们就只简单的在Firefox了解一下,有兴趣 的能够经过关键字搜索查询。 */ //经过方法存储和获取 sessionStorage.setItem('name','小明'); alert(sessionStorage.getItem('name')); //经过属性存储和获取 sessionStorage.book='小明'; alert(sessionStorage.book); //删除存储 sessionStorage.removeItem('name'); /*PS:因为localStorage代替了globalStorage,因此在Firefox、Opera和Chrome目前的最 新版本已不支持。*/ //经过方法存储和获取 localStorage.setItem('name','小明'); alert(localStorage.getItem('name')); //经过属性存储和获取 localStorage.book='小明'; alert(localStorage.book); //删除存储 localStorage.removeItem('name'); /*PS:这三个对象都是永久保存的,保存在缓存里,只有手工删除或者清理浏览器缓存 方可失效。在容量上也有一些限制,主要看浏览器的差别,Firefox3+、IE8+、Opera为5M, Chrome和Safari为2.5M。*/
第28章 XML
/* 随着互联网的发展,Web应用程序的丰富,开发人员愈来愈但愿可以使用客户端来操 做XML技术。而XML技术一度成为存储和传输结构化数据的标准。因此,本章就详细探 讨一下JavaScript中使用XML的技术。 对于什么是XML,干什么用的,这里就不在赘述了,在以往的XHTML或PHP课程都 有涉及到,能够理解成一个微型的结构化的数据库,保存一些小型数据用的。 一.IE中的XML 在统一的正式规范出来之前,浏览器对于XML的解决方案各不相同。DOM2级提出了 动态建立XMLDOM规范,DOM3进一步加强了XMLDOM。因此,在不一样的浏览器实现 XML的处理是一件比较麻烦的事情。 1.建立XMLDOM对象 IE浏览器是第一个原生支持XML的浏览器,而它是经过ActiveX对象实现的。这个对 象,只有IE有,通常是IE9以前采用。微软当年为了开发人员方便的处理XML,建立了 MSXML库,但却没有让Web开发人员经过浏览器访问相同的对象。 */ var xmlDom = new ActiveXObject('MSXML2.DOMDocument'); /* ActiveXObject类型 XML版本字符串 说明 Microsoft.XmlDom 最初随同IE发布,不建议使用 MSXML2.DOMDocument 脚本处理而更新的版本,仅在特殊状况做为备份用 MSXML2.DOMDocument.3.0 在JavaScript中使用,这是最低的建议版本 MSXML2.DOMDocument.4.0 脚本处理时并不可靠,使用这个版本致使安全警告 MSXML2.DOMDocument.5.0 脚本处理时并不可靠,使用这个版本致使安全警告 MSXML2.DOMDocument.6.0 脚本可以可靠处理的最新版本 */ /* PS:在这六个版本中微软只推荐三种: 1.MSXML2.DOMDocument.6.0最可靠最新的版本 2.MSXML2.DOMDocument.3.0兼容性较好的版本 3.MSXML2.DOMDocument 仅针对IE5.5以前的版本 */ /*PS:这三个版本在不一样的windows平台和浏览器下会有不一样的支持,那么为了实现兼 容,咱们应该考虑这样操做:从6.0->3.0->备用版本这条路线进行实现。*/ function createXMLDOM(){ var version=[ 'MSXML2.DOMDocument.6.0', 'MSXML2.DOMDocument.3.0', 'MSXML2.DOMDocument' ]; for(vari=0;i<version.length;i++){ try{ varxmlDom=newActiveXObject(version[i]); returnxmlDom; }catch(e){ //跳过 } } throw new Error('您的系统或浏览器不支持MSXML!'); //循环后抛出错误 } /*2.载入XML 若是已经获取了XMLDOM对象,那么能够使用loadXML()和load()这两个方法能够分 别载入XML字符串或XML文件。*/ xmlDom.loadXML('<rootversion="1.0"><user>Lee</user></root>'); alert(xmlDom.xml); /*PS:loadXML参数直接就是XML字符串,若是想效果更好,能够添加换行符\n。.xml 属性能够序列化XML,获取整个XML字符串。*/ xmlDom.load('test.xml'); //载入一个XML文件 alert(xmlDom.xml); /*当你已经能够加载了XML,那么你就能够用以前学习的DOM来获取XML数据,好比 标签内的某个文本。*/ var user=xmlDom.getElementsByTagName('user')[0]; //获取<user>节点 alert(user.tagName); //获取<user>元素标签 alert(user.firstChild.nodeValue); //获取<user>里的值Lee //DOM不仅仅能够获取XML节点,也能够建立。 var email=xmlDom.createElement('email'); xmlDom.documentElement.appendChild(email); /*3.同步及异步 load()方法是用于服务器端载入XML的,而且限制在同一台服务器上的XML文件。那 么在载入的时候有两种模式:同步和异步。 所谓同步:就是在加载XML完成以前,代码不会继续执行,直到彻底加载了XML再 返回。好处就是简单方便、坏处就是若是加载的数据中止响应或延迟过久,浏览器会一直堵 塞从而形成假死状态。*/ xmlDom.async=false; //设置同步,false,能够用PHP测试假死 /*所谓异步:就是在加载XML时,JavaScript会把任务丢给浏览器内部后台去处理,不 会形成堵塞,但要配合readystatechange事件使用,因此,一般咱们都使用异步方式。*/ xmlDom.async=true; //设置异步,默认 /*经过异步加载,咱们发现获取不到XML的信息。缘由是,它并无彻底加载XML就 返回了,也就是说,在浏览器内部加载一点,返回一点,加载一点,返回一点。这个时候, 咱们须要判断是否彻底加载,而且能够使用了,再进行获取输出。*/ /* XMLDOM中readystatechange事件 就绪状态 说明 1 DOM正在加载 2 DOM已经加载完数据 3 DOM已经能够使用,但某些部分还没法访问 4 DOM已经彻底能够 PS:readyState能够获取就绪状态值 */ var xmlDom=createXMLDOM(); xmlDom.async=true; //异步,能够不写 xmlDom.onreadystatechange=function(){ if(xmlDom.readyState==4){ //彻底加载了,再去获取XML alert(xmlDom.xml); } } xmlDom.load('test.xml'); //放在后面重点体现异步的做用 /*PS:能够经过readyState来了解事件的执行次数,将load()方法放到最后不会由于代码 的顺序而致使没有加载。而且load()方法必须放在onreadystatechange以后,才能保证就绪状 态变化时调用该事件处理程序,由于要先触发。用PHP来测试,在浏览器内部执行时,是 否能操做,是否会假死。 PS:不可以使用this,不可以用IE的事件处理函数,缘由是ActiveX控件为了预防安 全性问题。 PS:虽然能够经过XMLDOM文档加载XML文件,但公认的仍是XMLHttpRequest 对象比较好。这方面内容,咱们在Ajax章节详细了解。 4.解析错误 在加载XML时,不管使用loadXML()或load()方法,都有可能遇到XML格式不正确的 状况。为了解决这个问题,微软的XMLDOM提供了parseError属性。*/ /* parseError属性对象 属性 说明 errorCode 发生的错误类型的数字代号 filepos 发生错误文件中的位置 line 错误行号 linepos 遇到错误行号那一行上的字符的位置 reason 错误的解释信息 */ if(xmlDom.parseError==0){ alert(xmlDom.xml); }else{ throw new Error('错误行号:' +xmlDom.parseError.line+ '\n 错误代号:' +xmlDom.parseError.errorCode+ '\n 错误解释:' +xmlDom.parseError.reason); } /*二.DOM2中的XML IE能够实现了对XML字符串或XML文件的读取,其余浏览器也各自实现了对XML 处理功能。DOM2级在document.implementaion中引入了createDocument()方法。IE九、Firefox、 Opera、Chrome和Safari都支持这个方法。*/ //1.建立XMLDOM对象 var xmlDom=document.implementation.createDocument('','root',null); //建立xmlDom var user=xmlDom.createElement('user'); //建立user元素 xmlDom.getElementsByTagName('root')[0].appendChild(user); //添加到root下 var value=xmlDom.createTextNode('Lee'); //建立文本 xmlDom.getElementsByTagName('user')[0].appendChild(value); //添加到user下 alert(xmlDom.getElementsByTagName('root')[0].tagName); alert(xmlDom.getElementsByTagName('user')[0].tagName); alert(xmlDom.getElementsByTagName('user')[0].firstChild.nodeValue); /*PS:因为DOM2中不支持loadXML()方法,因此,没法简易的直接建立XML字符串。 因此,只能采用以上的作法。 PS:createDocument()方法须要传递三个参数,命名空间,根标签名和文档声明,因为 JavaScript管理命名空间比较困难,因此留空便可。文档声明通常根本用不到,直接null即 可。命名空间和文档声明留空,表示建立XMLDOM对象不须要命名空间和文档声明。 PS:命名空间的用途是防止太多的重名而进行的分类,文档类型代表此文档符合哪一种 规范,而这里建立XMLDOM不须要使用这两个参数,因此留空便可。 2.载入XML DOM2只支持load()方法,载入一个同一台服务器的外部XML文件。固然,DOM2也 有async属性,来表面同步或异步,默认异步。*/ //同步状况下 var xmlDom=document.implementation.createDocument('','root',null); xmlDom.async=false; xmlDom.load('test.xml'); alert(xmlDom.getElementsByTagName('user')[0].tagName); //异步状况下 var xmlDom=document.implementation.createDocument('','root',null); xmlDom.async=true; addEvent(xmlDom,'load', function(){ //异步直接用onload便可 alert(this.getElementsByTagName('user')[0].tagName); }); xmlDom.load('test.xml'); /*PS:无论在同步或异步来获取load()方法只有Mozilla的Firefox才能支持,只不过新版 的Opera也是支持的,其余浏览器则不支持。 3.DOMParser类型 因为DOM2没有loadXML()方法直接解析XML字符串,因此提供了DOMParser类型 来建立XMLDOM对象。IE九、Safari、Chrome和Opera都支持这个类型。*/ var xmlParser=newDOMParser(); //建立DOMParser对象 var xmlStr='<user>Lee</user></root>'; //XML字符串 var xmlDom=xmlParser.parseFromString(xmlStr,'text/xml'); //建立XMLDOM对象 alert(xmlDom.getElementsByTagName('user')[0].tagName); //获取user元素标签名 /*PS:XMLDOM对象是经过DOMParser对象中的parseFromString方法来建立的,两个 参数:XML字符串和内容类型text/xml。 4.XMLSerializer类型 因为DOM2没有序列化XML的属性,因此提供了XMLSerializer类型来帮助序列化 XML字符串。IE九、Safari、Chrome和Opera都支持这个类型。*/ var serializer=newXMLSerializer(); //建立XMLSerializer对象 var xml=serializer.serializeToString(xmlDom); //序列化XML alert(xml); /*5.解析错误 在DOM2级处理XML发生错误时,并无提供特有的对象来捕获错误,而是直接生 成另外一个错误的XML文档,经过这个文档能够获取错误信息。*/ var errors=xmlDom.getElementsByTagName('parsererror'); if(errors.length>0){ throw new Error('XML格式有误:' +errors[0].textContent); } //PS:errors[0].firstChild.nodeValue也能够使用errors[0].textContent来代替。 /*三.跨浏览器处理XML 若是要实现跨浏览器就要思考几个个问题:1.load()只有IE、Firefox、Opera支持,因此 没法跨浏览器;2.获取XMLDOM对象顺序问题,先判断先进的DOM2的,而后再去判断 落后的IE;3.针对不一样的IE和DOM2级要使用不一样的序列化。4.针对不一样的报错进行不一样 的报错机制。*/ //首先,咱们须要跨浏览器获取XMLDOM function getXMLDOM(xmlStr){ var xmlDom=null; if(typeof window.DOMParser!='undefined') { //W3C xmlDom=(new DOMParser()).parseFromString(xmlStr,'text/xml'); var errors=xmlDom.getElementsByTagName('parsererror'); if(errors.length>0){ throw new Error('XML解析错误:' +errors[0].firstChild.nodeValue); } }elseif(typeof window.ActiveXObject!='undefined') { //IE varversion=[ 'MSXML2.DOMDocument.6.0', 'MSXML2.DOMDocument.3.0', 'MSXML2.DOMDocument' ]; for(var i=0;i<version.length;i++){ try{ xmlDom = new ActiveXObject(version[i]); }catch(e){ //跳过 } } xmlDom.loadXML(xmlStr); if(xmlDom.parseError!=0){ throw new Error('XML解析错误:' +xmlDom.parseError.reason); } }else{ throw new Error('您所使用的系统或浏览器不支持XMLDOM!'); } return xmlDom; } //其次,咱们还必须跨浏览器序列化XML function serializeXML(xmlDom){ var xml=''; if(typeof XMLSerializer!='undefined') { xml=(new XMLSerializer()).serializeToString(xmlDom); }else if(typeof xmlDom.xml!='undefined') { xml=xmlDom.xml; }else{ throw new Error('没法解析XML!'); } return xml; } //PS:因为兼容性序列化过程有必定的差别,可能返回的结果字符串可能会有一些不一样。 //至于load()加载XML文件则由于只有部分浏览器支持而没法跨浏览器。
第29章 XPath
/*XPath是一种节点查找手段,对比以前使用标准DOM去查找XML中的节点方式,大 大下降了查找难度,方便开发者使用。可是,DOM3级之前的标准并无就XPath作出规 范;直到DOM3在首次推荐到标准规范行列。大部分浏览器实现了这个标准,IE则以本身 的方式实现了XPath。*/ /*一.IE中的XPath 在IE8及以前的浏览器,XPath是采用内置基于ActiveX的XMLDOM文档对象实现的。 在每个节点上提供了两个方法:selectSingleNode()和selectNodes()。 selectSingleNode()方法接受一个XPath模式(也就是查找路径),找到匹配的第一个节 点并将它返回,没有则返回null。*/ var user=xmlDom.selectSingleNode('root/user'); //获得第一个user节点 alert(user.xml); //查看xml序列 alert(user.tagName); //节点元素名 alert(user.firstChild.nodeValue); //节点内的值 /*上下文节点:咱们经过xmlDom这个对象实例调用方法,而xmlDom这个对象实例其 实就是一个上下文节点,这个节点指针指向的是根,也就是root元素以前。那么若是咱们 把这个指针指向user元素以前,那么结果就会有所变化。*/ //经过xmlDom,而且使用root/user的路径 var user=xmlDom.selectSingleNode('root/user'); alert(user.tagName); //user //经过xmlDom.documentElement,而且使用user路径,省去了root var user=xmlDom.documentElement.selectSingleNode('user'); alert(user.tagName); //user //经过xmlDom,而且使用user路径,省去了root var user=xmlDom.selectSingleNode('user'); alert(user.tagName); //找不到了,出错 /*PS:xmlDom和xmlDom.documentElement都是上下文节点,主要就是定位当前路径查 找的指针,而xmlDom对象实例的指针就是在最根上。*/ //XPath XPath经常使用语法 //经过user[n]来获取第n+1条节点,PS:XPath实际上是按1为起始值的 var user=xmlDom.selectSingleNode('root/user[1]'); alert(user.xml); //经过text()获取节点内的值 var user=xmlDom.selectSingleNode('root/user/text()'); alert(user.xml); alert(user.nodeValue); //经过//user表示在整个xml获取到user节点,不关心任何层次 var user=xmlDom.selectSingleNode('//user'); alert(user.xml); //经过root//user表示在root包含的层次下获取到user节点,在root内不关心任何层次 var user=xmlDom.selectSingleNode('root//user'); alert(user.tagName); //经过root/user[@id=6]表示获取user中id=6的节点 var user=xmlDom.selectSingleNode('root/user[@id=6]'); alert(user.xml); /*PS:更多的XPath语法,能够参考XPath手册或者XMLDOM手册进行参考,这里只 提供了最经常使用的语法。 selectSingleNode()方法是获取单一节点,而selectNodes()方法则是获取一个节点集合。*/ var users=xmlDom.selectNodes('root/user'); //获取user节点集合 alert(users.length); alert(users[1].xml); /*二.W3C下的XPath 在DOM3级XPath规范定义的类型中,最重要的两个类型是XPathEvaluator和 XPathResult。其中,XPathEvaluator用于在特定上下文对XPath表达式求值。*/ /* XPathEvaluator的方法 方法 说明 createExpression(e,n) 将XPath表达式及命名空间转化成XPathExpression createNSResolver(n) 根据n命名空间建立一个新的XPathNSResolver对象 evaluate(e,c,n,t,r) 结合上下文来获取XPath表达式的值 */ /*W3C实现XPath查询节点比IE来的复杂,首先第一步就是须要获得XPathResult对象 的实例。获得这个对象实例有两种方法,一种是经过建立XPathEvaluator对象执行evaluate() 方法,另外一种是直接经过上下文节点对象(好比xmlDom)来执行evaluate()方法。*/ //使用XPathEvaluator对象建立XPathResult var eva=newXPathEvaluator(); var result=eva.evaluate('root/user',xmlDom,null, XPathResult.ORDERED_NODE_ITERATOR_TYPE,null); alert(result); //使用上下文节点对象(xmlDom)建立XPathResult var result=xmlDom.evaluate('root/user',xmlDom,null, XPathResult.ORDERED_NODE_ITERATOR_TYPE,null); alert(result); /*相对而言,第二种简单方便一点,但evaluate方法有五个属性:1.XPath路径、2.上下 文节点对象、3.命名空间求解器(一般是null)、4.返回结果类型、5保存结果的XPathResult 对象(一般是null)。*/ /*对于返回的结果类型,有10中不一样的类型 常量 说明 XPathResult.ANY_TYPE 返回符合XPath表达式类型的数据 XPathResult.ANY_UNORDERED_NODE_TYPE 返回匹配节点的节点集合,但顺序可能与文档中的节点的顺序不匹配 XPathResult.BOOLEAN_TYPE 返回布尔值 XPathResult.FIRST_ORDERED_NODE_TYPE 返回只包含一个节点的节点集合,且这个节点是在文档中第一个匹配的节点 XPathResult.NUMBER_TYPE 返回数字值 XPathResult.ORDERED_NODE_ITERATOR_TYPE 返回匹配节点的节点集合,顺序为节点在文档中出现的顺序。这是最经常使用到的结果类型 XPathResult.ORDERED_NODE_SNAPSHOT_TYPE 返回节点集合快照,在文档外捕获节点,这样未来对文档的任何修改都不会影响这个节点列表 XPathResult.STRING_TYPE 返回字符串值 XPathResult.UNORDERED_NODE_ITERATOR_TYPE 返回匹配节点的节点集合,不过顺序可能不会按照节点在文档中出现的顺序排列 XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE 返回节点集合快照,在文档外捕获节点,这样未来对文档的任何修改都不会影响这个节点列表*/ /*PS:上面的常量过于繁重,对于咱们只须要学习了解,其实也就须要两个:1.获取一个 单一节、2.获取一个节点集合。*/ //1.获取一个单一节点 var result=xmlDom.evaluate('root/user',xmlDom,null, XPathResult.FIRST_ORDERED_NODE_TYPE,null); if(result!==null){ alert(result.singleNodeValue.tagName); //singleNodeValue属性获得节点对象 } //2.获取节点集合 var result = xmlDom.evaluate('root/user',xmlDom,null, XPathResult.ORDERED_NODE_ITERATOR_TYPE,null); var nodes=[]; if(result!==null){ while((node=result.iterateNext())!==null){ nodes.push(node); } } /*PS:节点集合的获取方式,是经过迭代器遍历而来的,咱们保存到数据中就模拟出IE 类似的风格。*/ /*三.XPath跨浏览器兼容 若是要作W3C和IE的跨浏览器兼容,咱们要思考几个问题:1.若是传递一个节点的下 标,IE是从0开始计算,W3C从1开始计算,能够经过传递获取下标进行增1减1的操做 来进行。2.独有的功能放弃,为了保证跨浏览器。3.只获取单一节点和节点列表便可,基本 能够完成全部的操做。*/ //跨浏览器获取单一节点 function selectSingleNode(xmlDom,xpath){ var node=null; if(typeof xmlDom.evaluate!='undefined') { var patten=/\[(\d+)\]/g; var flag=xpath.match(patten); var num=0; if(flag!==null){ num=parseInt(RegExp.$1)+1; xpath=xpath.replace(patten,'[' +num+']'); } var result=xmlDom.evaluate(xpath,xmlDom,null, XPathResult.FIRST_ORDERED_NODE_TYPE,null); if(result!==null){ node=result.singleNodeValue; } }else if(typeofxmlDom.selectSingleNode!='undefined') { node=xmlDom.selectSingleNode(xpath); } return node; } //跨浏览器获取节点集合 function selectNodes(xmlDom,xpath){ var nodes=[]; if(typeof xmlDom.evaluate!='undefined') { var patten=/\[(\d+)\]/g; var flag=xpath.match(patten); var num=0; if(flag!==null){ num=parseInt(RegExp.$1)+1; xpath=xpath.replace(patten,'[' +num+']'); } var node=null; var result=xmlDom.evaluate('root/user',xmlDom,null, XPathResult.ORDERED_NODE_ITERATOR_TYPE,null); if(result!==null){ while((node=result.iterateNext())!==null){ nodes.push(node); } } }elseif(typeof xmlDom.selectNodes!='undefined') { nodes=xmlDom.selectNodes(xpath); } return nodes; } /*PS:在传递xpath路径时,没有作验证判断是否合法,有兴趣的同窗能够自行完成。在 XML还有一个重要章节是XSLT和EX4,因为在使用频率的缘故,咱们暂且搁置。*/
第30章 JSON
/*前两章咱们探讨了XML的结构化数据,但开发人员仍是以为这种微型的数据结构仍是 过于烦琐、冗长。为了解决这个问题,JSON的结构化数据出现了。JSON是JavaScript的一 个严格的子集,利用JavaScript中的一些模式来表示结构化数据。 一.JSON语法 JSON和XML类型,都是一种结构化的数据表示方式。因此,JSON并非JavaScript 独有的数据格式,其余不少语言均可以对JSON进行解析和序列化。 JSON的语法能够表示三种类型的值: 1.简单值:能够在JSON中表示字符串、数值、布尔值和null。但JSON不支持JavaScript 中的特殊值undefined。 2.对象:顾名思义。 3.数组:顾名思义。 简单值 100、"Lee" 这两个量就是JSON的表示方法,一个是JSON数值,一个是JSON字符串。 布尔值和null也是有效的形式。但实际运用中要结合对象或数组。*/ //对象 //JavaScript对象字面量表示法: var box={ name:'Lee', age:100 }; //而JSON中的对象表示法须要加上双引号,而且不存在赋值运算和分号: { "name":"Lee", //使用双引号,不然转换会出错 "age":100 } //数组 //JavaScript数组字面量表示法: var box=[100,'Lee', true]; //而JSON中的数组表示法一样没有变量赋值和分号: [100,"Lee",true] //通常比较经常使用的一种复杂形式是数组结合对象的形式: [ { "title":"a", "num":1 }, { "title":"b", "num":2 }, { "title":"c", "num":3 } ] /*PS:通常状况下,咱们能够把JSON结构数据保存到一个文本文件里,而后经过 XMLHttpRequest对象去加载它,获得这串结构数据字符串(XMLHttpRequest对象将在Aajx 章节中详细探讨)。因此,咱们能够模拟这种过程。 模拟加载JSON文本文件的数据,而且赋值给变量。*/ var box='[{"name" :"a","age":1},{"name":"b","age":2}]'; /*PS;上面这短代码模拟了varbox=load('demo.json');赋值过程。由于经过load加载的文 本文件,无论内容是什么,都必须是字符串。因此两边要加上双引号。 其实JSON就是比普通数组多了两边的双引号,普通数组以下:*/ var box=[{name:'a', age:1},{name:'b', age:2}]; /*二.解析和序列化 若是是载入的JSON文件,咱们须要对其进行使用,那么就必须对JSON字符串解析成 原生的JavaScript值。固然,若是是原生的JavaScript对象或数组,也能够转换成JSON字 符串。 对于讲JSON字符串解析为JavaScript原生值,早期采用的是eval()函数。但这种方法 既不安全,可能会执行一些恶意代码。*/ var box='[{"name" :"a","age":1},{"name":"b","age":2}]'; alert(box); //JSON字符串 var json=eval(box); //使用eval()函数解析 alert(json); //获得JavaScript原生值 /*ECMAScript5对解析JSON的行为进行规范,定义了全局对象JSON。支持这个对象的 浏览器有IE8+、Firefox3.5+、Safari4+、Chrome和Opera10.5+。不支持的浏览器也能够经过 一个开源库json.js来模拟执行。JSON对象提供了两个方法,一个是将原生JavaScript值转 换为JSON字符串:stringify();另外一个是将JSON字符串转换为JavaScript原生值:parse()。*/ var box='[{"name" :"a","age":1},{"name":"b","age":2}]'; //特别注意,键要用双引号 alert(box); var json = JSON.parse(box); //不是双引号,会报错 alert(json); var box=[{name:'a', age:1},{name:'b', age:2}]; //JavaScript原生值 var json=JSON.stringify(box); //转换成JSON字符串 alert(json); //自动双引号 /*在序列化JSON的过程当中,stringify()方法还提供了第二个参数。第一个参数能够是一个 数组,也能够是一个函数,用于过滤结果。第二个参数则表示是否在JSON字符串中保留缩 进。*/ var box=[{name:'a', age:1,height:177},{name:'b', age:2,height:188}]; var json=JSON.stringify(box,['name','age'], 4); alert(json); /*PS:若是不须要保留缩进,则不填便可;若是不须要过滤结果,但又要保留缩进,则 讲过滤结果的参数设置为null。若是采用函数,能够进行复杂的过滤。*/ var box=[{name:'a', age:1,height:177},{name:'b', age:2,height:188}]; var json=JSON.stringify(box,function(key,value){ switch(key){ case'name' : return'Mr. ' +value; case'age' : returnvalue+'岁'; default: return value; } },4); alert(json); /*PS:保留缩进除了是普通的数字,也能够是字符。 还有一种方法能够自定义过滤一些数据,使用toJSON()方法,能够将某一组对象里指 定返回某个值。*/ var box=[{name:'a', age:1,height:177,toJSON:function(){ return this.name; }},{name:'b',age :2,height:188,toJSON:function(){ return this.name; }}]; var json=JSON.stringify(box); alert(json); /*PS:因而可知序列化也有执行顺序,首先先执行toJSON()方法;若是应用了第二个过 滤参数,则执行这个方法;而后执行序列化过程,好比将键值对组成合法的JSON字符串, 好比加上双引号。若是提供了缩进,再执行缩进操做。 解析JSON字符串方法parse()也能够接受第二个参数,这样能够在还原出JavaScript值 的时候替换成本身想要的值。*/ var box='[{"name" :"a","age":1},{"name":"b","age":2}]'; var json=JSON.parse(box,function(key,value){ if(key=='name') { return 'Mr. ' +value; }else{ return value; } }); alert(json[0].name);
第31章 Ajax
/*2005年JesseJamesGarrett发表了一篇文章,标题为:“Ajax:AnewApproachtoWeb Applications”。他在这篇文章里介绍了一种技术,用他的话说,就叫:Ajax,是Asynchronous JavaScript+XML的简写。这种技术可以想服务器请求额外的数据而无须卸载页面(即刷新), 会带来更好的用户体验。一时间,席卷全球。*/ /*一.XMLHttpRequest Ajax技术核心是XMLHttpRequest对象(简称XHR),这是由微软首先引入的一个特性, 其余浏览器提供商后来都提供了相同的实现。在XHR出现以前,Ajax式的通讯必须借助一 些hack手段来实现,大多数是使用隐藏的框架或内嵌框架。 XHR的出现,提供了向服务器发送请求和解析服务器响应提供了流畅的接口。可以以 异步方式从服务器获取更多的信息,这就意味着,用户只要触发某一事件,在不刷新网页的 状况下,更新服务器最新的数据。 虽然Ajax中的x表明的是XML,但Ajax通讯和数据格式无关,也就是说这种技术不 必定使用XML。 IE7+、Firefox、Opera、Chrome和Safari都支持原生的XHR对象,在这些浏览器中创 建XHR对象能够直接实例化XMLHttpRequest便可。*/ var xhr=newXMLHttpRequest(); alert(xhr); //XMLHttpRequest /*若是是IE6及如下,那么咱们必须还须要使用ActiveX对象经过MSXML库来实现。在 低版本IE浏览器可能会遇到三种不一样版本的XHR对象,即MSXML2.XMLHttp、 MSXML2.XMLHttp.3.0、MSXML2.XMLHttp.6.0。咱们能够编写一个函数。*/ function createXHR(){ if(typeof XMLHttpRequest!='undefined') { return newXMLHttpRequest(); }else if (typeofActiveXObject!='undefined') { var versions=[ 'MSXML2.XMLHttp.6.0', 'MSXML2.XMLHttp.3.0', 'MSXML2.XMLHttp' ]; for(var i=0;i<versions.length;i++){ try{ return new ActiveXObject(version[i]); }catch(e){ //跳过 } } }else{ throw new Error('您的浏览器不支持XHR对象!'); } } var xhr = new createXHR(); /*在使用XHR对象时,先必须调用open()方法,它接受三个参数:要发送的请求类型(get、 post)、请求的URL和表示是否异步。*/ xhr.open('get','demo.php', false); //对于demo.php的get请求,false同步 //PS:demo.php的代码以下: <?php echo Date('Y-m-dH:i:s')?> //一个时间 /*open()方法并不会真正发送请求,而只是启动一个请求以备发送。经过send()方法进行 发送请求,send()方法接受一个参数,做为请求主体发送的数据。若是不须要则,必须填null。 执行send()方法以后,请求就会发送到服务器上。*/ xhr.send(null); //发送请求 /*当请求发送到服务器端,收到响应后,响应的数据会自动填充XHR对象的属性。那么 一共有四个属性:*/ /*属性名 说明 responseText 做为响应主体被返回的文本 responseXML 若是响应主体内容类型是"text/xml"或"application/xml",则返回包含响应数据的XMLDOM文档 status 响应的HTTP状态 statusText HTTP状态的说明 */ /*接受响应以后,第一步检查status属性,以肯定响应已经成功返回。通常而已HTTP状 态代码为200做为成功的标志。除了成功的状态代码,还有一些别的:*/ /* HTTP状态码 状态字符串 说明 200 OK 服务器成功返回了页面 400 BadRequest 语法错误致使服务器不识别 401 Unauthorized 请求须要用户认证 404 Notfound 指定的URL在服务器上找不到 500 InternalServerError 服务器遇到意外错误,没法完成请求 503 ServiceUnavailable 因为服务器过载或维护致使没法完成请求 */ /* 咱们判断HTTP状态值便可,不建议使用HTTP状态说明,由于在跨浏览器的时候,可 能会不太一致。 */ addEvent(document,'click', function(){ var xhr=newcreateXHR(); xhr.open('get','demo.php?rand=' +Math.random(),false); //设置了同步 xhr.send(null); if(xhr.status==200){ //若是返回成功了 alert(xhr.responseText); //调出服务器返回的数据 }else{ alert('数据返回失败!状态代码:' +xhr.status+'状态信息:' +xhr.statusText); } }); /*以上的代码每次点击页面的时候,返回的时间都是时时的,不一样的,说明都是经过服务 器及时加载回的数据。那么咱们也能够测试一下在非Ajax状况下的状况,建立一个 demo2.php文件,使用非Ajax。*/ <script type="text/javascript"src="base.js"></script> <script type="text/javascript">addEvent(document,'click', function(){ alert("<?phpechoDate('Y-m-dH:i:s')?>"); }); </script> /* 同步调用当然简单,但使用异步调用才是咱们真正经常使用的手段。使用异步调用的时候, 须要触发readystatechange事件,而后检测readyState属性便可。这个属性有五个值: */ /* 值 状态 说明 0 未初始化 还没有调用open()方法 1 启动 已经调用open()方法,但还没有调用send()方法 2 发送 已经调用send()方法,但还没有接受响应 3 接受 已经接受到部分响应数据 4 完成 已经接受到所有响应数据,并且能够使用 */ addEvent(document,'click', function(){ var xhr=new createXHR(); xhr.onreadystatechange=function(){ if(xhr.readyState==4){ if(xhr.status==200){ alert(xhr.responseText); }else{ alert('数据返回失败!状态代码:' +xhr.status+'状态信息:' +xhr.statusText); } } }; xhr.open('get','demo.php?rand=' +Math.random(),true); xhr.send(null); }); /*PS:使用abort()方法能够取消异步请求,放在send()方法以前会报错。放在responseText 以前会获得一个空值。 二.GET与POST 在提供服务器请求的过程当中,有两种方式,分别是:GET和POST。在Ajax使用的过 程中,GET的使用频率要比POST高。 在了解这两种请求方式前,咱们先了解一下HTTP头部信息,包含服务器返回的响应头 信息和客户端发送出去的请求头信息。咱们能够获取响应头信息或者设置请求头信息。咱们 能够在Firefox浏览器的firebug查看这些信息。*/ //使用getResponseHeader()获取单个响应头信息 alert(xhr.getResponseHeader('Content-Type')); //使用getAllResponseHeaders()获取整个响应头信息 alert(xhr.getAllResponseHeaders()); //使用setRequestHeader()设置单个请求头信息 xhr.setRequestHeader('MyHeader','Lee'); //放在open方法以后,send方法以前 /*PS:咱们只能够获取服务器返回回来响应头信息,没法获取向服务器提交的请求头信 息,天然自定义的请求头,在JavaScript端是没法获取到的。 GET请求 GET请求是最多见的请求类型,最经常使用于向服务器查询某些信息。必要时,能够将查 询字符串参数追加到URL的末尾,以便提交给服务器。*/ xhr.open('get','demo.php?rand=' +Math.random()+'&name=Koo', true); /*经过URL后的问号给服务器传递键值对数据,服务器接收到返回响应数据。特殊字符 传参产生的问题能够使用encodeURIComponent()进行编码处理,中文字符的返回及传参, 能够讲页面保存和设置为utf-8格式便可。 */ //一个通用的URL提交函数 function addURLParam(url,name,value){ url+=(url.indexOf('?')==-1?'?' :'&'); //判断的url是否有已有参数 url+=encodeURIComponent(name)+'=' +encodeURIComponent(value); alert(url); return url; } /*PS:当没有encodeURIComponent()方法时,在一些特殊字符好比“&”,会出现错误导 致没法获取。 POST请求 POST请求能够包含很是多的数据,咱们在使用表单提交的时候,不少就是使用的POST 传输方式。*/ xhr.open('post','demo.php', true); //而发送POST请求的数据,不会跟在URL的尾巴上,而是经过send()方法向服务器提交数据。 xhr.send('name=Lee&age=100'); /*通常来讲,向服务器发送POST请求因为解析机制的缘由,须要进行特别的处理。由于 POST请求和Web表单提交是不一样的,须要使用XHR来模仿表单提交。*/ xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded'); /*PS:从性能上来说POST请求比GET请求消耗更多一些,用相同数据比较,GET最多 比POST快两倍。 上一节课的JSON也能够使用Ajax来回调访问。*/ var url='demo.json?rand=' +Math.random(); var box=JSON.parse(xhr.responseText); /*三.封装Ajax 由于Ajax使用起来比较麻烦,主要就是参数问题,好比到底使用GET仍是POST;到 底是使用同步仍是异步等等,咱们须要封装一个Ajax函数,来方便咱们调用。*/ function ajax(obj){ var xhr=new createXHR(); obj.url=obj.url+'?rand=' +Math.random(); obj.data=params(obj.data); if(obj.method==='get') obj.url=obj.url.indexOf('?')==-1? obj.url+'?' +obj.data:obj.url+'&' +obj.data; if(obj.async===true){ xhr.onreadystatechange=function(){ if(xhr.readyState==4)callback(); }; } xhr.open(obj.method,obj.url,obj.async); if(obj.method==='post') { xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded'); xhr.send(obj.data); }else{ xhr.send(null); } if(obj.async===false){ callback(); } function callback(){ if(xhr.status==200){ obj.success(xhr.responseText); //回调 }else{ alert('数据返回失败!状态代码:' +xhr.status+', 状态信息:' +xhr.statusText); } } } //调用ajax addEvent(document,'click', function(){ //IE6须要重写addEvent ajax({ method:'get', url:'demo.php', data:{ 'name' :'Lee', 'age' :100 }, success:function(text){ alert(text); }, async:true }); }); //名值对编码 function params(data){ var arr=[]; for(variindata){ arr.push(encodeURIComponent(i)+'=' +encodeURIComponent(data[i])); } return arr.join('&'); } //PS:封装Ajax并非一开始就造成以上的形态,须要通过屡次变化而成。