1、关于引用
//创建数组 var items = new Array("one","two","three"); //创建数组的一个引用 var itemsRef = items; //将一个元素添加到数组中 items.puth("four"); //这两个数组的长度应该是一致的,因为他们指向同一个变量 alert(items.length == itemsRef.length); //将items设置为一个新对象 items = new Array("new","array"); //items和itemsRef现在指向不同的对象了 alert(items != itemsRef);
//将items设置为一个新的字符串对象 var items = "test"; //itemsRef和items现在指向同一个对象 var itemsRef = items; //将一些新的文本附加在字符串的后面。注意:这会创建一个新对象,而非修改原对象 items += "ing"; //items和itemsRef的值不相等了,因为新的字符串已经被创建 alert(items != itemsRef);
2、关于变量的类型
(1)、变量类型检查
变量----------------------------------typeof 变量-----------------------------------变量.constructor
{an : "object"}----------------------object------------------------------------------Object
["an", "array"]-----------------------object------------------------------------------Array
function (){}-------------------------function----------------------------------------Function
"a string"----------------------------string-------------------------------------------String
55-------------------------------------number----------------------------------------Number
true-----------------------------------boolean----------------------------------------Boolean
new User()-------------------------object-------------------------------------------User
//用一个变量类型列表严格检查一个参数列表 function strict(types, args) { if (types.length != args.length) { throw "types.length != args.length"; } for (var i=0;i<args.length;i++) { if (args[i].constructor != types[i]) { throw "args["+i+"].constructor != types["+i+"]"; } } }
当函数无明确返回值时,返回的也是值undefined。
Null类型只有一个专用值null。alert(null == undefined);输出true,尽管他们相等,但含义不同,undefined是声明了变量但未对其初始化时赋予该变量的值,null则用于表示尚未存在的对象。如果函数或方法要返回的是对象,那么找不到该对象时,返回的通常是null。
NaN是一个特殊值,表示非数(Not a Number),通常出现在类型转换失败时,NaN不能用于算术计算,而且它与自身不相等,alert(NaN == NaN);将返回FALSE,可以用isNaN()函数来判断。
(2)、类型强制转换
可用如下3个函数来进行类型强制转换:
Boolean(value)------------把给定的值转换成Boolean型
当要转换的值是至少有一个字符的字符串、非0数字或对象时,Boolean()函数将返回TRUE;如果该值是空字符串、数字0、undefined、null时,将返回FALSE。
var b1 = Boolean("");//false - empty string var b2 = Boolean("hi");//true var b3 = Boolean(100);//true var b4 = Boolean(null);//false var b5 = Boolean(0);//false var b6 = Boolean(new Object());//true
String(value)---------------把给定的值转换成字符串
String()方法可以把任何值转换成字符串。强制转换成字符串和调用toString()方法的唯一不同之处在于,对null和undefined值强制类型转换可以生成字符串而不引发错误。
var s1 = String(null);//return "null" var onull = null; var s2 = onull.toString();//won't work,causes an error
Number(value)------------把给定的值转换成数字(可以是整数或浮点数)
var n1 = Number(false);//0 var n2 = Number(true);//1 var n3 = Number(undefined);//NaN var n4 = Number(null);//0 var n5 = Number("5.5");//5.5 var n6 = Number("56");//56 var n7 = Number("5.6.7");//NaN var n8 = Number(new Object());//NaN var n9 = Number(100);//100
(3)、instanceof运算符
instanceof运算符与typeof运算符类似,用于识别正在处理的对象的类型。与typeof方法不同的是,instanceof方法要求开发者明确的确认对象为某特定的类型。
var oString = new String("Hello world"); alert(oString instanceof String);//output "true"
3、关于作用域
javascript中,所有属于全局作用域的变量其实都是window对象的属性
var test = "test"; //你可以发现全局变量test和window的属性test是一致的 alert(window.test == test);
如果变量没有显式定义,那么它就是全局定义的。
function test() { foo = "test"; } test(); //foo现在是在全局作用域下 alert(window.foo == "test");
4、关于闭包
var obj = document.getElementById("testid"); var items = ["click","keypress"]; for (var i=0;i<items.length;i++) { //使用一个自执行的匿名函数来激发出作用域 (function (){ var item = items[i]; obj["on"+item] = function () { alert("Thank you for your "+item); } })(); }
5、关于上下文对象
上下文对象是通过this变量体现的,这个变量永远指向当前代码所处的对象中。
function changeColor(color) { this.style.backgroundColor = color; } //在window对象中调用此函数会失败,因为window对象没有style属性 changeColor("white"); var main = document.getElementById("testid"); //call方法将上下文对象设置为第一个参数,并将其他参数作为原函数的参数 changeColor.call(main, "blue"); function setBodyColor() { //第二个参数是传给函数的所有参数的数组 changeColor.apply(document.body, arguments); } setBodyColor("black");
6、关于javascript面向对象
在javascript中,任何函数都可以被实例化成一个对象。
//接受名称并将其存入当前上下文中 function User(name) { this.name = name; } var me = new User("my name"); //这个对象的名称被设置为自身的name属性了 alert(me.name == "my name"); //而且这是User对象的一个实例 alert(me.constructor == User); User("test"); //因为它的this上下文对象未曾指定,所以模认为全局的window对象。 //也就是说window.name等于提供的这个名字 alert(window.name == "test");
7、关于公有方法
通过对象的prototype属性,这个属性包含了一个对象,该对象可以作为所有新副本的基引用。
function User(name,age) { this.name = name; } //公有方法getName User.prototype.getName = function () { return this.name; } //user1与user2对象都有getName方法 var user1 = new User("Bob"); var user2 = new User("Tom"); alert(user1.getName() == "Bob"); alert(user2.getName() == "Tom");
8、关于私有方法
function Classroom(students, teacher) { function disp() { alert(this.names.join(", ")); } this.students = students; this.teacher= teacher; //调用私有方法来显示错误 disp(); } var class = new Classroom(["tom","bob"], "smith"); //调用disp方法会失败,因为它不是该对象的公有方法。 class.disp();
9、关于特权方法
function User(name,age) { var year = (new Date()).getFullYear() - age; //创建一个新的特权方法,能够访问year变量,同时自身属于公共可以访问的 this.getYearBorn = function () { return year; } } var user = new User("bob", 44); alert(user.getYearBorn()); //注意我们无法访问该对象私有的year属性 alert(user.year);
动态生成的特权方法:
//参数为有许多属性的对象 function User(properties) { //遍历对象的所有属性,并保证其作用域正确 for (var i in properties) { (function (which) { var p = i; //创建此属性的getter which["get"+p] = function () { return properties[p]; }; //创建此属性的setter which["set"+p] = function (val) { properties[p] = val; }; })(this); } } var user = new User({ name:"bob", age:44 }); //注意name属性并不存在,因为它是properties的私有变量 alert(user.name); //getname()方法是动态生成的,且是公有方法 alert(user.getname()); alert(user.getage()); user.setage(22); alert(user.getage());
10、关于原型式继承
function Person(name) { this.name = name; } Person.prototype.getName = function () { return this.name; } function User(name,password) { //注意,这里并没有支持方便的重载/继承,也就是说,不能调用父类的构造函数 this.name = name; this.password = password; } //user对象继承所有person对象的方法 User.prototype = new Person(); User.prototype.getPassword = function () { return this.password; }
User是对User对象构造函数的引用。new Person()使用Person构造函数创建了一个新的Person对象,然后把User构造函数的原型置为这个操作的结果。也就是说,每当你new User()时,得到新User对象都会带有Person对象所有的方法,如同通过操作new Person()得到的一样。
11、关于类式继承
//简单的辅助函数,让你可以将新函数绑定到对象的prototype上 Function.prototype.method = function (name,func) { this.prototype[name] = func; return this; }
它提供了一个简单的方法,把函数与构造函数的原型关联起来。之所以有效,是因为所有的构造函数本身都是函数,所以能获得method这个新方法。
//一个复杂的函数,允许你方便的从其他对象继承函数,同时仍然可以调用属于父对象的那些函数 Function.method("inherit", function (parent) { //记录目前我们所在父层次的级数 var depth = 0; //继承父对象的方法 var proto = this.prototype = new parent(); //创建一个新的名为uber的特权函数,调用它时会执行所有在继承时被重写的函数 this.method("uber",function uber(name) { var func; //要执行的函数 var ret; //函数的返回值 var v = parent.prototype; //父对象的prototype //如果我们已经在某个uber函数之内 if (depth) { //上溯必要的depth,以找到原始的prototype for (var i=d;i>0;i+=1) { v = v.constructor.prototype; } //从该prototype中获取函数 func = v[name]; } else { //否则就是第一次调用 //从prototype获得要执行的函数 func = proto[name]; //如果此函数属于当前的prototype if (func = this[name]) { //则改为调用父对象的prototype func = v[name]; } } //记录我们在继承堆栈中所在位置的级数 depth += 1; //使用除第一个以外所有的arguments调用此函数(因为第一个参数是执行的函数明) ret = func.apply(this, Array.prototype.slice.apply(arguments,[1])); //恢复继承堆栈 depth -= 1; return ret; }); return this; });
这一函数可以用于提供简单的单对象继承,它的代码主要围绕在任意对象方法中调用this.uber('methodName')为中心,并在让这个uber方法去执行它要覆盖的父对象的方法。
//之继承父对象特定函数的函数。而非使用new parent()继承所有的函数 Function.method("swiss", function (parent) { //遍历所有要继承的方法 for (var i=0;i<arguments.length;i+=1) { //需要导出的方法名 var name = arguments[i]; //将此方法导入this对象的prototype中 this.prototype[name] = parent.prototype[name]; } return this; });
这是.method()函数的增强版,可以用于从单一父对象获取多个函数。如果用在多个父对象上就能获得可用的多对象继承。
示例:
function Person(name) { this.name = name } Person.method("getName",function () { return this.name; }); function User(name,password) { this.name = name; this.password = password; } //从Person对象继承所有的方法 User.inherit(Person); User.method("getPassword", function () { return this.password; }); //覆盖Person对象的getName方法,但通过uber函数来调用原有方法 User.method("getName", function () { return "my name is "+this.uber("getName"); }); var per = new Person("person"); var usr = new User("user","pw"); alert(usr.getName());
12、关于命名空间
实际上,javascript里并不存在命名空间的概念,但考虑到javascript的所有对象都有自己的属性,属性又可以包含对象,这样就能创造一些和其他语言里的命名空间神似的东西了。
//创建一个默认的、全局的命名空间 var YOHOO = {}; //使用对象设置一些子命名空间 YAHOO.util = {}; //创建最终命名空间,它包含一个值为函数的属性 YAHOO.util.Event = { addEventListener: function(){} }; //调用某个具体命名空间中的函数 YAHOO.util.Event.addEventListener();
13、关于几点编码习惯
(1)、变量声明
程序里所有的变量都必须在使用前声明。虽然javascript并没有明确要求这么做,但不声明会导致变量的实际作用域不好理解。
//不当的变量使用 foo = "test"; //恰当的变量声明 var foo; foo = "test";
(2)、!=和==对!==和===
在javascript中,null、undefined是相等的(==),而0、""、false之间也是两两相等的(==)""
//下面两个表达式皆为真 "" == false; null == undefined; //下面应该使用 !== 和 === null !== false; false === false;
(3)、代码块与大括号
不应该使用单行的代码块。比如在判断语句后面即使只跟着一条语句,也应该保留大括号,while和for也是如此。虽然对单行语句不使用大括号是javascript提供的巨大便利,但这可能导致其他人的误解。
(4)、分号
javascript里如果每个语句单独占一行,那语句后面的分号不是必需的。在未压缩的代码里不用分号当然没什么问题,但在去除换行符以压缩代码后就可能导致问题了。要避免这样的问题,你应该时时记住,在所有语句后面加上分号。
var foo = "test"; var foo = function () { alert("hello"); }; foo();
14、关于DOM文档里的nodeType
nodeType=1:匹配HTML或XML中的大部分元素。比如,<li>、<a>、<p>、<body>等元素都有一个值为1的nodeType
nodeType=3:匹配文档内的的所有文本块。当使用previousSibling和nextSibling来遍历DOM结构时,你会经常碰到元素内和元素间的文本块。
nodeType=9:匹配文档的根元素。比如在HTML文档内,它是<html>元素。
15、关于两个DOM方法
getElementById("every"):该方法只能运行在document对象下
getElementsByTagName("li"):该方法能运行在任何对象下,找出该元素下的所有标签名为li的后代元素,并返回一个 NodeList。该结构跟普通的javascript数组非常相似,但一个重要的不同之处在于:它并不支 持.push()、.pop()、.shift()等javascript数组的常用方法。
var liList = document.getElementById("every").getElementsByTagName("li");
16、关于判断DOM何时加载完毕
以下是检查HTML DOM是否可用的几个要点:
(1)、document:你需要知道DOM文档是否已经加载。若能足够快的检查,运气好的话你能看到undefined。
(2)、document.getElementsByTagName和document.getElementById:频繁使用这两个函数检查文档,当存在这些函数则表明已完成加载。
(3)、document.body:作为额外补充,检查<body>元素是否已经完全加载。
function domReady(func) { //如果DOM加载,立刻执行函数 if (domReady.done) { return func(); } //加入我们已经增加了一个函数 if (domReady.timer) { //把它加入待执行的函数清单中 domReady.ready.push(func); } else { //为页面加载完毕绑定一个事件 window.onload = isDOMReady; //初始化待执行的函数数组 domReady.ready = [func]; //尽可能快的检查DOM是否可用 domReady.timer = setInterval(isDOMReady, 13); } } //检查DOM是否可以操作 function isDOMReady() { if (domReady.done) { return false; } if (document && document.getElementById && document.getElementsByTagName && document.body) { //如果可用就停止检查 clearInterval(domReady.timer); domReady.timer = null; //执行所有等待的函数 for (var i=0;i<domReady.ready.length;i++) { domReady.ready[i](); } domReady.ready = null; domReady.done = true; } } function tag(name, elem) { return (elem || document).getElementsByTagName(name); } domReady(function () { alert("dom load ok"); tag("li")[0].style.border = "4px" });
17、关于通过类(class)的值查找元素
function hasClass(name,type) { var r = []; //定位到类值上 var re = new RegExp("(^|//s)" + name + "(//s|$)"); //限制类型的查找,或者遍历所有元素 var e = document.getElementsByTagName(type || "*"); for (var j=0;j<e.length;j++) { //如果元素拥有指定类,把它添加到函数的返回值中 if (re.test(e[j])) { r.push(e[j]); } } return r; } hasClass("test"); hasClass("test","li");
18、关于XPath
css3选择器和XPath表达式比较
目标----------------------------------------------css3-----------------------------------------XPath
所有元素-----------------------------------------*----------------------------------------------//*
所有<p>元素------------------------------------p---------------------------------------------//p
所有子元素--------------------------------------p > *-----------------------------------------//p/*
由ID获取元素-----------------------------------#foo-------------------------------------------//*[@id='foo']
由类获取元素-----------------------------------.foo-------------------------------------------//*[contains(@class,'foo')]
由属性获取元素--------------------------------*[title]----------------------------------------//*[@tilte]
<p>的第一个子元素----------------------------p>*:first-child------------------------------//p/*[0]
所有拥有子元素的<p>-------------------------不支持---------------------------------------//p[a]
下一个元素--------------------------------------p + *------------------------------------------//p/下一兄弟元素::*[0]
19、关于获取元素文本内容
function text(e) { var t = ""; //如果传入的是元素,则继续遍历其子元素,否则假定它是一个数组 e = e.childNodes || e; for (var i=0;i<e.length;i++) { //如果不是元素,追加其文本值。否则,递归遍历所有元素的字节点 t += e[i].nodeType != 1 ? e[i].nodeValue : text(e[i].childNodes); } return t; } var contents = text(document.body);
20、关于innerHTML的几点说明
(1)、IE返回的元素字符都是大写的,如果你想保持一致性可能会感到失望
(2)、innerHTML作为一个只能用在HTMLDOM文档的元素中的属性,若在XMLDOM文档中使用的话只会返回null值
21、关于创建DOM元素
function create(elem) { return document.createElementNS ? document.createElementNS("http://www.w3.org/1999/xhtml", elem) : document.createElement(elem); }
createElementNS() 方法与 createElement() 方法 相似,只是它创建的 Element 节点除了具有指定的名称外,还具有指定的命名空间。只有使用命名空间的 XML 文档才会使用该方法。
22、关于删除DOM元素
//删除一个独立的DOM节点 function remove(elem) { if (elem) { elem.parentNode.removeChild(elem); } } //删除所有子节点 function empty(elem) { while (elem.firstChild) { remove(elem.firstChild); } }
23、关于事件对象(event object)
IE使用一个独立的全局事件对象(它可以在全局变量属性window.event中找到),而其他浏览器则使用独立的包含事件对象的参数传递。
事件对象拥有许多你可以在事件监听函数中使用的属性:
target: 触发事件的元素(在IE中,target被srcElement取代了)。
type: 被触发的事件(例如,click)。
button: 被按下的鼠标按钮。0代表左键,1代表中键,2代表右键(详见关于35)。
keyCode/data/charCode: 被按下的键的字符编码。W3C规范使用data,可是它并没有获得广泛的支持。相反大多数浏览器都是用charCode,而keyCode是IE的实现。幸好所有的浏览器都能理解keyCode,这意味着你现在可以使用它(详见关于36)。
shiftKey/ctrlKey/altKey: 一个布尔值-----如果shift、ctrl、alt键分别的被按下则返回true。
24、关于取消事件冒泡
希望事件发生在它的目标而非它的父元素上
function stopBubble(e) { //如果传入了事件对象,那么就是非IE浏览器 if (e && e.stopPropagation) { e.stopPropagation(); } else { //否则使用IE方式来取消事件冒泡 window.event.cancelBubble = true; } }
25、关于浏览器的默认行为
浏览器的默认行为可以归纳为没有明确指令而浏览器自动执行的行为。例如点击<a>元素将重定向到它的href属性上的URL
//防止发生默认浏览器行为的函数 function stopDefault(e) { //W3C的防止默认浏览器行为 if (e && e.preventDefault) { e.preventDefault(); } else { window.event.returnValue = false; } return false; }
var iframe = document.getElementById("iframe"); var a = document.getElementsByTagName("a"); for (var i=0;i<a.length;i++) { a[i].onclick = function() { iframe.src = this.href; return stopDefault(e); }; }
26、关于绑定事件监听函数
(1)、传统方式
document.getElementsByTagName("fomr")[0].onsubmit = function(e) { //停止表单提交的默认行为 return stopDefault(e); } document.body.onkeypress = myKeyPressHandler;
优点:非常简单和稳定,可以确保在不同的浏览器中运作一致;处理事件时,this关键字引用的事当前元素
缺点:只会在事件冒泡中运行,而非捕获和冒泡;一个元素一次只能绑定一个事件处理函数;事件对象参数仅非IE浏览器可用
(2)、W3C方式
document.getElementsByTagName("form")[0].addEventListener("submit", function(){ return stopDefault(e); }, false); document.body.addEventListener("keypress", myKeyPressHandler, false);
addEventListener有3个参数:事件的名称(比如click)、处理事件的函数、一个启用或禁用事件捕获的布尔标记。
优点:该方法同时支持事件处理的捕获和冒泡阶段。取决于addEventListener最后的参数设置:false(冒泡)或true(捕获); 在事件处理函数内部,this关键字引用当前元素;事件对象总是可以通过处理函数的第一个参数获取;可以绑定多个事件而不会覆盖先前绑定的事件。
缺点:不支持IE,必须使用IE的attachEvent函数代替
(3)、IE方式
document.getElementsByTagName("form")[0].attachEvent("onsubmit", function(){ return stopDefault(); }); document.body.attachEvent("onkeypress", myKeyPressHandler);
优点:可以绑定多个事件
缺点:IE仅支持事件捕获的冒泡阶段;监听函数内的this关键字指向了window对象而不是当前对象;事件对象仅存在于window.event参数中;事件必须以ontype的形式命名;仅有IE可用
27、关于确保链接不依赖于javascript
当点击链接是发生GET请求,提交表单时则发生POST请求。规范指出GET请求不应有破坏性的副作用(比如删除一条消息),如果通过链接可以删除、编辑或者修改任何用户的数据,你应该使用表单来进行。
28、关于访问式样信息
javascript要求你在设置任何几何属性时必须明确尺寸单位(例如设置高度,必须使用px单位)。同时,任何几何属性都会返回表示元素式样的字符串而非数值(例如是100px而非100)
//获取元素的真实、最终的CSS样式属性值得函数 function getStyle(elem, name) { //如果属性存在于style[]中,那么它已被设置了 if (elem.style[name]) { return elem.style[name]; //否则尝试使用IE的方法 } else if (elem.currentStyle) { return elem.currentStyle[name]; //或者W3C的方法,如果存在的话 } else if (document.defaultView && document.defaultView.getComputedStyle) { //它使用的是通用的'text-align'的样式规则而非'textAlign' name = name.replace(/([A-Z])/g, "-$1"); name = name.toLowerCase(); //获取样式对象并获取属性值 var s = document.defaultView.getComputedStyle(elem,""); return s && s.getPropertyValue(name); } else { //否则用户使用的是其他浏览器 return null; } } window.onload = function() { var div = document.getElementById("test"); alert(div.style.height); alert(getStyle(div,"height")); };
29、关于元素定位
(1)、静态定位:这是元素定位的默认方式。当元素是静态定位时,top和left属性无效。
position:static; top:0px; left:0px;
(2)、相对定位:与静态定位相似,但是设置top或left属性会引起元素相对于它的原始(静态)位置进行偏移。
position:relative; top:-50px; left:50px;
(3)、绝对定位:绝对定位的元素会相对于它的第一个非静态定位的祖先元素而展示。如果没有这样的祖先元素,则相对于整个文档。
position:absolute; top:20px; left:20px;
(4)、固定定位:固定定位把元素相对于浏览器窗口而定位。例如设置元素的top和left为0会使它显示在浏览器左上角,它完全忽略浏览器滚动条的拖动,一直会出现在用户的视野。
position:fixed; top:20px; left:20px;
30、关于获取元素位置
offsetLeft和offsetTop这两个属性分别是元素在offsetParent上下文中的水平和垂直偏移量。
//获取元素的X(水平、左端)位置 function pageX(elem) { return elem.offsetParent ? elem.offsetLeft + pageX(elem.offsetParent) : elem.offsetLeft; } //获取元素的Y(垂直、顶端)位置 function pageY(elem) { return elem.offsetParent ? elem.offsetTop + pageY(elem.offsetParent) : elem.offsetTop; } //获取元素相对于父元素的水平位置 function parentX(elem) { return elem.parentNode == elem.offsetParent ? elem.offsetLeft : pageX(elem) - pageX(elem.parentNode) } //获取元素相对于父元素的垂直位置 function parentY(elem) { return elem.parentNode == elem.offsetParent ? elem.offsetTop : pageY(elem) - pageY(elem.parentNode) }
31、关于元素的可见性
CSS有两种不同的方式可以有效的隐藏元素。
visibility属性在切换元素可见性的同时会保持元素普通流的属性的相关影响。它 有两个值:visible(默认的)和hidden(不可见的)。假设一小段文本包含在<b>标签内,同时<b>的 visibility设置为hidden,那么结果就是文本内有一块空白留白,它的尺寸刚好等于被包裹文本的原有尺寸。即虽然不显示,但是其在页面的位置 还是被其占据。
display属性可以是inline(比如<b>和<span>的标签是inline的,它们都 遵循文本的普通流动)、block(比如<p>和<div>的标签是block的,它们都打破文本的普通流动)或none(它完 全从文档中隐藏了元素)。设置元素display属性的结果跟从文档中删除了该元素的情形看起来一样,只是该元素还可以迅速的切换回文档的视觉中来。
//设置元素的透明度(级别从0-100,0表示完全透明,100相反) function setOpacity(elem, level) { if (elem.filters) { elem.style.filters = 'alpha(opacity='+level+')';//IE } else { elem.style.opacity = level/100;//W3C } }
32、关于表单验证
(1)、验证必填
//检查输入元素是否键入了信息的通用函数 function checkRequired(elem) { if (elem.type == "checkbox" || elem.type == "radio") { return getInputsByName(elem.name).numChecked; } else { return elem.value.length > 0 && elem.value != elem.defaultValue } } //找出指定name的所有input元素(对查找以及处理单选框或复选框十分有用) function getInputsByName(name) { //匹配的input元素数组 var results = []; //追踪被选中的元素的数量 result.numChecked = 0; var inputs = document.getElementsByTagName("input"); for (var i=0;i<inputs.length;i++) { if (inputs[i].name == name) { results.push(inputs[i]); if (inputs[i].checked) { results.numChecked++ } } } return results; } var age = document.getElementById("age"); if (!checkRequired(age)) { alert("input"); }
(2)、验证匹配
function checkEmail(elem) { return elem.value == "" || /^[a-z0-9_+.-]+/@([a-z0-9-]+/.)+[a-z0-9]{2,4}$/i.test(elem.value); } function checkURL(elem) { return elem.value == "" || !elem.value == "http://" || /^https?:([a-z0-9-]+/.)+[a-z0-9]{2,4}.*$/.test(elem.value); } function checkDate(elem) { //确保输入了内容并检查是否符合MM/DD/YYYY的时间格式 return !elem.value || /^/d{2}///d{2}///d{2,4}$/.test(elem.value); }
33、关于AJAX
//执行AJAX请求的通用函数,参数是包含一些列选项的对象。但请求与本页面在不同域下的数据是不可能的。 function ajax(options) { options = { //HTTP请求的类型 type: options.type || "POST", url: options.url || "", timeout: options.timeout || 5000, //请求失败、成功或完成(不管成功还是失败都会调用的)时执行的函数 onComplete: options.onComplete || function () {}, onError: options.onError || function () {}, onSuccess: options.onSuccess || function () {}, //服务器将会返回的数据类型,这一默认值用于判断服务器返回的数据并作相应的动作 data: options.data || "" }; if (typeof XMLHttpRequest == "undefined") { XMLHttpRequest = function () { return new ActiveXObject(navigator.userAgent.indexOf("MSIE 5") >= 0 ? "Microsoft.XMLHTTP" : "Msxml2.XMLHTTP"); } } var xml = new XMLHttpRequest(); xml.open(options.type, options.url, true); //在请求等待5秒,超时则放弃 var timeoutLength = options.timeout; //记录请求是否成功完成 var requestDone = false; //初始化一个5秒后执行的回调函数,用于取消请求(如果尚未完成的话) setTimeout(function () { requestDone = true; }, timeoutLength); xml.onreadystatechange = function () { //保持等待,直到数据完全加载,并保证请求并未超时 if (xml.readyState == 4 && !requestDone) { //检查是否请求成功 if (httpSuccess(xml)) { //以服务器返回的数据作为参数调用成功回调函数 options.onSuccess(httpData(xml, options.type)); } else { options.onError(); } options.onComplete(); xml = null; } }; xml.send(); //判断HTTP响应是否成功 function httpSuccess(r) { try { //如果得不到服务器状态,且我们正在请求本地文件,认为成功; 所有200到300之间的状态码表示成功; 文档未修改也算成功; return !r.status && location.protocol == "file:" || (r.status >= 200 && r.status < 300) || r.status == 304 || navigator.userAgent.indexOf("safari") >= 0 && typeof r.status == "undefined"; } catch(e) {} return false; } //从HTTP响应中解析正确数据 function httpData(r, type) { var ct = r.getResponseHeader("content-type"); //若没有提供默认的类型,判断服务器返回的是否是XML格式 var data = !type && ct && ct.indexOf("xml") >= 0; //若是,获得XML文档对象,否则返回文本内容 data = type == "xml" || data ? r.responseXML : r.responseText; //若指定类型是"script",则以JavaScript形式执行返回文本 if (type == "script") { eval.call(window, data); } return data; } }
34、关于DOM函数的说明
(1)、appendChild(nodeToAppend): 这是一个可用来为元素追加子节点的函数。如果需要追加节点已存在于文档中,那么它会从原有位置移动到当前元素上。appendChild函数必须在你所期望追加的元素上调用。
var ul = document.createElement("ul"); var li = document.getElementsByTagName("li"); for (var i=0;i<li.length;i++) { ul.appendChild(li[i]); } document.body.appendChild(ul);
(2)、cloneNode(true|false): 该函数是一种通过克隆已有的节点为开发者简化代码的方法,而克隆出来的节点也能够插入到DOM中去。因为一个普通的insertBefore或者appendChild会把文档中的DOM节点移开,但cloneNode函数能够克隆新的节点而保持原有节点的位置。该函数有一个值是true或者false的参数。如果参数是true,节点及其内部的所有东西都会克隆,如果是false,则只有节点本身被克隆。
//查找第一个ul元素 var ul = document.getElementsByTagName("ul")[0]; //克隆节点并追加到旧元素之后 ul.parentNode.appendChild(ul.cloneNode(true));
(3)、insertBefore(nodeToInsert, nodeToInsertBefore): 这是一个用来在文档的任意地方插入DOM节点的函数。该函数必须在你希望插入到前面的元素的父元素上调用。该函数带有两个参数,一个是你希望插入到DOM中的节点,另一个是需要插入前面位置的DOM节点。
var a = document.getElementsByTagName("a"); for (var i=0;i<a.length;i++) { var img = document.createElement("img"); img.src = "xxxxxx.jpg"; //把图片插入到链接之前 a[i].parentNode.insertBefore(img, a[i]); }
(4)、removeChild(nodeToRemove): 这个函数用来删除文档的节点。该函数必须在你希望删除节点的父元素上调用。
var op = document.body.getElementsByTagName("p")[0]; op.parentNode.removeChild(op);
(5)、replaceChild(nodeToInsert, nodeToReplace): 这个函数是删除一个节点并在原地插入另外一个节点的替代方法。该函数必须在你需要替换的节点的父元素上调用。该函数带两个参数:你希望插入到DOM的节点和希望替换的节点。
var a = document.getElementsByTagName("a"); while (a.length) { var s = document.createElement("strong"); //使内容等于<a>链接的URL s.appendChild(document.createTextNode(a[i].href)); //使用<b>元素替换原有的<a>元素 a[i].replaceNode(s, a[i]); }
35、关于鼠标属性
鼠标属性仅在鼠标相关的事件(比如click、mousedown、mouseup、mouseover、mousemove和mouseout)发生时才存在于事件对象中。
(1)、clientX/clientY: 这两个属性包含相对于窗口的鼠标光标的x和y坐标。
(2)、pageX/pageY: 这两个属性包含相对于呈现文档的鼠标光标的x和y坐标(例如,如果你向下滚动文档,得到的数字将不会等于clientX/clientY属性所包含的值)。它们在IE中无效,要在IE中获取光标的位置,你必须使用clientX/clientY属性并加上当前的滚动偏移值。
(3)、layerX/layerY和offsetX/offsetY: 这些属性包含相对于事件目标元素的鼠标光标的x和y坐标。layerX/layerY属性可用在基于Mozilla的浏览器和safari,而offsetX/offsetY则可用在opear和IE中。
(4)、button: 这个属性是表示当前点击(仅可用于click、mousedown和mouseup事件)的鼠标键的数字。
点击--------------------------------IE---------------------------------W3C
左键---------------------------------1-----------------------------------0
右键---------------------------------2-----------------------------------2
中键---------------------------------4-----------------------------------1
在IE中,返回3代表左键和右键同时按下,返回7代表左键、中键和右键3个键同时被按下。
document.onclick = function (e) { e = e || window.event; //如果执行的是右键点击 if (e.button == 2) { //阻止发生默认行为 e.preventDefault(); return false; } }
(5)、relatedTarget: 这个事件对象包含一个鼠标刚离开的元素的引用。除此之外,还可用在这样的场合:需要使用mouseover/mouseout,而且必须知道鼠标所处,或者即将进入的元素。
36、关于键盘属性
键盘属性一般只在键盘相关的事件(比如keydown、keyup和keypress)发生时才存在于事件对象中。除了ctrlKey和shiftKey属性,它们可以在鼠标事件中存在(这就允许ctrl+click一个元素)。而其余场合,你可以假定这些返回值并不存在或不确定。
(1)、ctrlKey: 该属性返回一个布尔值,表示键盘的Ctrl键是否被按住。该属性可存在于键盘和鼠标事件中。
(2)、keyCode: 这个属性包含一个键盘响应键位的数字。某些键位(比如pageup和home键)的可用性并不能确定,但一般来说,其他的键位均工作稳定。
键位------------------------------------------------------------代码
退格键(Backspace)---------------------------------------------8
制表键(Tab)-----------------------------------------------------9
回车键(Enter)---------------------------------------------------13
空格键(Space)--------------------------------------------------32
左箭头键(Left arrow)-------------------------------------------37
上箭头键(Up arrow)----------------------------------------------38
右箭头键(Rigth arrow)-------------------------------------------39
下箭头键(Down arrow)-------------------------------------------40
0-9-----------------------------------------------------------48~57
A-Z-----------------------------------------------------------65~90
(3)、shiftKey: 该属性返回一个布尔值,表示键盘的Shift键是否被按住。该属性可存在于键盘和鼠标事件中。
37、关于页面事件
(1)、beforeunload: 在事件处理函数内,如果返回的是字符串,那么字符串就会显示在一个确认窗口中,询问用户是否希望离开当前页面。
window.onbeforeunload = function () { return "Your data not save."; }
(2)、error: error事件在javascript代码发生错误时触发,它无需传递事件对象参数,而包含一条已发生错误的解释信息。
(3)、resize: 在用户重置浏览器窗口时触发。当用户调整浏览器窗口的尺寸时,resize事件仅在完成重置的事件触发一次,而不是每一步都会触发。
(4)、scroll: 于用户在浏览器窗口内移动文档的位置时触发。这会在键盘敲击(比如箭头、翻页或者空格键)或使用滚动条时触发。
(5)、unload: 这个事件在用户离开当前页(可以是点击链接、后退键甚至是关闭浏览器窗口)时触发。阻止默认行为并不会在此事件中生效。
38、关于键盘事件
keydown/keypress
keydown事件是键盘敲击时触发的第一个键盘事件。如果用户继续按住键位,keydown事件会持续进行。keypress是keydown事件的同义事件,它们的表现完全一致,只有一个例外。如果需要阻止按键的默认行为,你必须使用keypress事件。
39、关于表单事件
表单事件主要处理<form><input><select><button><textarea>元素
(1)、select: select事件在用户使用鼠标于输入框内选择不同区块的文本时触发。
//阻止用户在<textarea>内选择文本 var textarea = document.getElementsByTagName('textarea')[0]; textarea.onselect = function () { return fasle; }
(2)、change: change事件在用户改变了输入元素(包括<select>和<textarea>元素)的值时触发。该事件仅在用户已经离开了元素,使其失去焦点时触发。
40、关于CSS-DOM
css-dom技术简单来说就是读取和设置style对象的各种属性。style属性最大的不足是无法通过它来提取到外部CSS设置的样式信息。可以使用jquery等框架,例如jquery中的css()方法无论是外部css导入,还是直接拼接在HTML元素里(内联),css()方法都可以获取到属性style里的其他属性值。
41、关于变量命名规则
(1)可以是字母(大小写均可)、美元符$以及下划线_,但是首字符不能是数字。
(2)后续的字符由字母、数字、下划线、美元符组成。
(3)最好不要用保留字做变量名称。
(4)变量名称区分大小写。
注意:在函数中定义变量时,没写var则是全局变量。
42、关于String对象
可以显式或者隐式的创建String对象,隐式创建如下:
var mystring = "abc"; alert(typeof(mystring));//"string";
显式创建如下:
var mystring = new String("abc"); alert(typeof(mystring));//"object"
显式与隐式创建String对象的唯一真正区别是,如果你要重复的使用同样的字符串,显式的创建字符串有更高的效率。显式的创建String对象还有助于防止JavaScript解释器混淆数字和字符串。
43、关于Array对象
JavaScript的数组索引不仅可以用数字,也可以用字符串来充当。例如:
var my = new Array(); var bbb = 'aaa' my[bbb] = 'ddd'; alert(my[bbb]); my['ccc'] = 'fff'; alert(my['ccc']); my[0] = 'eee'; alert(my[0]);
但是要注意,如果数组中有字符串的索引,那么在使用数组的length属性时可能会得不到正确的结果。例如上例中最后如果alert(my.length)的话,返回的结果是1,并不是我们期望的3,这是因为数组的length属性返回比数组中最后一个数字项索引大1的索引,而上例中最后一个数字项索引是0,所以返回的结果是1。也就是说数组的length属性并不一定等于数组中元素的个数。
44、关于父节点和子节点
(1)、从父节点到子节点
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>test</title> </head> <body> <p id="my">this is p tag.</p> </body> </html>
如果想更改P段落的文本值,你可能认为应该像下面那样设置:
document.getElementById("my").nodeValue = "change text value";
可是,这并不起作用,因为P段落是一个element节点。如果你想更改段落内部的文本,就需要访问它内部的文本节点:
document.getElementById("my").firstChild.nodeValue = "change text value";
firstChild属性是个简便用法,每个元素都可以有任意数量的子节点,可以通过childNodes属性列出来:
[1]、childNodes是该元素所有第一层子节点的列表----并不包括向下更深的层次。
[2]、可以通过数组计数器或item()方法访问当前元素的子元素。
[3]、简便用法yourElement.firstChild和yourElement.lastChild是yourElement.childNodes[0]和yourElement.childNodes[yourElement.childNodes.length-1]的简化版本,可以使访问更快捷一些。
[4]、可以通过方法hasChildNodes()检查一个元素是否有子节点,它会返回一个布尔值。
(2)、从子节点到父节点
通过parentNode属性,可以从子节点回到父节点。
<ul> <li>11111111</li> <li>222222222222222</li> <li> <a id="mylink">333333333333333</a> </li> <li>4444444444444444</li> </ul> <mce:script type="text/javascript"><!-- var myLinkItem = document.getElementById("mylink"); var parentElem = myLinkItem.parentNode; while (parentElem != document.body) { parentElem = parentElem.parentNode; } alert(parentElem); // --></mce:script>
45、关于兄弟节点之间
可以通过一个节点的previousSibling和nextSibling属性访问同一级别上的不同子节点。
<ul> <li>11111111</li> <li>222222222222222</li> <li> <a id="mylink">333333333333333</a> </li> <li>4444444444444444</li> </ul> <mce:script type="text/javascript"><!-- var myLink = document.getElementById("mylink"); var parentElem = myLink.parentNode; var nextList = parentElem.nextSibling; var previousList = parentElem.previousSibling; // --></mce:script>
可以分别获得li item2和li item4。
注意,这个是IE ONLY。在其余浏览器中下一个和上一个兄弟并不是LI元素,而是将换行符作为内容的文本节点。
如果当前对象是父节点的最后一个子节点,那么nextSibling就会是undefined.
46、关于获取和修改元素属性
有2种方式来获取和修改元素属性:
<img src="aaa.jpg" mce_src="aaa.jpg" border="0" alt="aaa" id="myimg"> <p id="isp" isp="1">aaaaaaaaaaaaa</p>
(1)、以对象属性的方式来获取和设置元素的属性:
var myimg = document.getElementById("myimg"); myimg.src = "bbb.jpg"; myimg.alt = "bbb"; var ptag = document.getElementById("isp"); alert(ptag.id); alert(ptag.isp);//返回undefined
这种方法不支持用户自定义属性,如P元素的isp属性。但是class属性是个例外,因为class在ECMAScript中是一个保留字,在JavaScript中,它不能被作为变量名、属性名或者函数名,所以相应的属性名就变成了className。
(2)、通过getAttribute和setAttribute方法:
var myimg = document.getElementById("myimg"); myimg.setAttribute("src","bbb,jpg"); var ptag = document.getElementById("isp"); alert(ptag.getAttribute("isp"));//返回1
这种方法更贴近高级编程语言,而且支持用户自定义属性。但IE在setAttribute上有个很大的问题:当你使用它时,变更并不会总是正确的反映出来。所以如果打算支持IE,最好尽可能使用属性方式,即第一种方法。
47、关于一元运算符
(1)、delete: delete运算符删除对以前定义的对象属性或方法的引用,但不能删除开发者未定义的属性或方法。
var o = new Object(); o.name = "jim"; alert(o.name);//output "jim" delete o.name; alert(o.name);//output "undefined";
(2)、void: void运算符对任何值都返回undefined,该运算符通常用于避免输出不应该输出的值。
<a href="javascript:window.open('about:blank');">click</a>这种写法可能和你想要的结果不同,应该采用下句的写法:
<a href="javascript:void(window.open('about:blank'));">click</a>
(3)、一元加法和一元减法
一元加法本质上对数字无任何影响,但却会把字符串转换成数字。
var inum = 25; inum = +inum; alert(inum);//output "25" var snum = "25"; alert(typeof snum);//output "string" var inum = +snum; alert(typeof inum);//output "number"
一元减法就是对数值求负,也会把字符串转换成近似的数字,此外还会对该值求负。
48、关于关系运算符
var bResult = "23" < "3"; alert(bResult);//output true
上例因为两个运算数都是字符串,所以比较的是它们的字符代码("2"的字符代码是50,"3"的字符代码是51)。
var bResult = "23" < 3; alert(bResult);//output false
上例字符串"23"将被转换成数字23,然后与数字3进行比较。无论何时比较一个数字和字符串,ECMAScript都会把字符串转换成数字,然后按照数字顺序比较它们。
var bResult = "a" < 3; alert(bResult);//output false
上例因为字母"a"不能转换成有效的数字。不过,如果对它调用parseInt()方法,返回的是NaN。根据规则,任何包含NaN的关系运算都要返回false。
49、关于函数重载
JavaScript中的函数不能重载,虽然可以在同一个作用域中定义两个相同名字的函数,而不会引发错误,但真正使用的是后一个函数。但是JavaScript可以通过arguments对象模拟重载。如下:
function sayHi() { if (arguments[0] == "bye") { return; } alert(arguments[0]); } function howManyArgs() { alert(arguments.length); } howManyArgs("string", 45);//output 2 howManyArgs();//output 0 howManyArgs(12);//output 1
上例中的sayHi()函数arguments[0]表示第一个参数,以此类推,因此,无需明确命名参数。还可用arguments.length检测参数个数。
PS: JavaScript不会验证传递给函数的参数个数是否等于函数定义的参数个数。开发者定义的函数都可以接受任意个数的参数,而不会引发任何错误。任何遗漏的参数都会以undefined传递给函数,多余的参数将忽略。
50、关于call()和apply()方法以及 caller和callee
call()和apply()它们的作用都是将函数绑定到另外一个对象上去运行,两者仅在定义参数方式有所区别:
apply(thisArg, argArray);call(thisArg[,arg1,arg2…]);
即所有函数内部的this指针都会被赋值为thisArg,这可实现将函数作为另外一个对象的方法运行的目的。
(1)、call()方法是实现继承的好方法。call()方法第一个参数用作this对象,其他参数都直接传递给函数自身。如果call的第一个参数是null或undefined,被调用的function将被传入全局对像作为this。全局对象就是window对象。
function sayColor(prefix, suffix) { alert(prefix+this.color+suffix); } var obj = new Object(); obj.color = "red"; sayColor.call(obj, "this color is ", " a very nice color.");
在这个例子中,函数sayColor()在对象外定义,即使它不属于任何对象,也可以引用关键字this。对象obj的color属性等于red。调用call()方法时,第一个参数是obj,说明应该赋予sayColor()函数中的this关键字值是obj。第二和第三个参数是字符串,它们与sayColor()函数中的参数prefix和suffix匹配。
function classA(scolor) { this.color = scolor; this.sayColor = function () { alert(this.color); } } function classB(scolor, sname) { classA.call(this, scolor); this.name = sname; this.sayName = function () { alert(this.name); } } var obj = new classB("red", "tom"); obj.sayColor();
(2)、apply()方法有2个参数,用作this的对象和要传递给函数的参数的数组。
function sayColor(prefix, suffix) { alert(prefix+this.color+suffix); } var obj = new Object(); obj.color = "red"; sayColor.apply(obj, new Array("this color is ", " a very nice color."));
第一个参数与call()方法相同。第二个参数是由两个字符串组成的数组,与sayColor()的参数prefix和suffix匹配。
function classA(scolor) { this.color = scolor; this.sayColor = function () { alert(this.color); } } function classB(scolor, sname) { //classA.apply(this, new Array(scolor)); classA.apply(this, arguments); this.name = sname; this.sayName = function () { alert(this.name); } } var obj = new classB("blue"); obj.sayColor();
只有超类中的参数顺序与子类中的参数顺序完全一致时才可以传递参数对象。如果不是,就必须创建一个单独的数组,按照正确的顺序放置参数。此外,还可以使用call()方法。
(3)、caller返回一个对函数的引用,该函数调用了当前函数。
对于函数来说,caller 属性只有在函数执行时才有定义。如果函数是由顶层调用的,那么 caller 包含的就是 null 。如果在字符串上下文中使用 caller 属性,那么结果和 functionName.toString 一样,也就是说,显示的是函数的反编译文本。
function callerDemo() { if (callerDemo.caller) { var a = callerDemo.caller.toString(); alert(a); } else { alert("this is a top function"); } } callerDemo(); function handleCaller() { callerDemo(); } handleCaller();
(4)、callee返回正被执行的 Function 对象,也就是所指定的 Function 对象的正文。
callee 属性的初始值就是正被执行的 Function 对象。callee 属性是 arguments 对象的一个成员,它表示对函数对象本身的引用,这有利于匿名
函数的递归或者保证函数的封装性,例如下边示例的递归计算1到n的自然数之和。而该属性仅当相关函数正在执行时才可用。还有需要注意的是callee拥有length属性,这个属性有时候用于验证还是比较好的。arguments.length是实参长度,arguments.callee.length是形参长度,由此可以判断调用时形参长度是否和实参长度一致。
function calleeDemo() { alert(arguments.callee); } calleeDemo(); function calleeLengthDemo(arg1, arg2) { if (arguments.length == arguments.callee.length) { alert("ok"); return; } else { alert(arguments.length); alert(arguments.callee.length); } } calleeLengthDemo(1); calleeLengthDemo(1,2); var sum = function(n){ if (n <= 0) { return 1; } else { return n + arguments.callee(n - 1) } } alert(sum(100));