分类存档: javascript

(转)javascript编程容易出现的11个错误

javascript-11-mistake
javascript是比较容易学的。但是,对于这门语言需要有一些值得注意的地方。本文将指出javascript编程中可能犯过的10个错误

错误1-使用全局变量

如果你刚开始javascript编程,可能会觉得全局变量很好用。事实上,刚开始javascript编程,你可能不知道使用全局变量会带来什么 麻烦。在同一个页面中,全局变量可以在任何内嵌的javascript代码段中或是该页面加载的不同的js文件中,都能访问到。这听起来很强大,是吗?这 就使得全局变量可以随时随地的被修改赋值。
事实上这样很糟!
这样做会导致变量在意料之外被修改重写。假设有一个网店,需要用javascript计算并显示购物车所有商品的价格总和(当然,服务器端还会进行重新计算,这里只是为了增强用户的体验)。可能会编写代码如下:

  1. var total = 0, // total price
  2. tax = 0.05; // 5%

现在,还需要用javascript代码在网站上展示一些信息,或则是做一个商品仓库。代码如下:

  1. var total = 15; // number of tweets pulled from twitter

或则是如下代码:

  1. var tax = function () { /* … */ }; // Trigger Animation eXperience function

现在,出现问题了:两个重要的变量被重写,但可能还没被意识到。这样代码运行会出错,会花费很多时间来跟踪和修复该错误。
那该如何解决呢?简言之—“封装”:当然封装有很多方法可以实现。第一种做法是将代码放入一个匿名的自调函数中。代码如下:

  1. (function () {
  2. var total = 0, tax = 0.05;
  3. // other code
  4. }());

这样做,在函数外部是绝对不能访问到函数内部定义的变量。这就形成了个人的代码空间,但是这样就不能公开部分方法或属性。例如,想要创建一个购物车,定义一个总价的变量,作为公共属性,这种情形下可以采用模块式编程。

  1. var cartTotaler = (function () {
  2. var total = 0; tax = 0.05;
  3. // other code
  4. return {
  5. addItem : function (item) { },
  6. removeItem : function (item) { },
  7. calculateTitle : function () { }
  8. };
  9. }());

关于全局变量有一点值得注意,如果不用关键词var来声明创建变量,那么javascript引擎会默认将该变量定义为全局变量。

  1. (function () {
  2. tax = 0.05;
  3. }());
  4. var totalPrice = 100 + (100 * tax); // 105

这里的变量tax在函数外部也是可以被访问的,因为在定义tax的时候没有使用var关键词。

错误2-不加分号

每句javascript语句必须以分号结尾。在编程时如果忘了加分号,这时javascript编解析器会自动加上。那我们在编程的时候是不是就可以完全不用浪费时间去加分号呢?
但是在一些语句中,分号是必不可少的。如for循环语句中的分号是必不可少的,否则会报语法错误。那么语句末尾的分号呢?
Javascript社区已经讨论过该问题。下面是本文的看法:你的代码,只要是被javascript解析器修改过(即便是很小的修改,如添加分号),就可能会出现一些你意料之外的结果。看看下面这段javascript代码:

  1. function returnPerson (name) {
  2. return
  3. {
  4. name : name
  5. };
  6. }

该方法看起来会返回一个对象,但事实上,javascript解析器会在return后面紧接着添加一个分号,这就导致该函数返回undefined。Return后的对象会被忽略。解决方法很简单,代码如下:

  1. return {
  2. name : name
  3. };

在javascript编程中应严格要求自己添加分号,这个习惯并不难。当然,作为一名web开发人员,你可能也会用到其他的语言(如php),这些语言都是严格要求以分号结尾的。你可以把这些编程语言的习惯带到javascript编程中来。
作者注解:除了你完全肯定可以被忽略的地方,你都不可以忽略添加分号。

错误3-使用==

如果你问一个javascript编程者,在javascript编程中通常会犯什么错误。他/她很可能会说,使用= = =来代替= =。这是什么意思呢?
试试下面的代码:

  1. if (1 == 1) {
  2. console.log(“it’s true!”);
  3. }

代码如你所愿的输出了“it’s true!”那再试试下面的代码:

  1. if (1 == ’1′) {
  2. console.log(“it’s true!”);
  3. }

这段代码在控制台中尽然也输出了“it’s true!”,但其实这并不是你所期望的输出。这里的==运算符转换了运算数的类型,从而使得两个运算数相等了。这里if语句中的==运算符使得右边string类型的“1”变成了number型的1。
想要得到你想要的输出,这里应该用= = =运算符来代替= =。===不会强制转换运算数的类型,这样才能如你所期望的。同样地,用!= =运算符来替换!=。下面是用==来做比较,得出的结果令人意外。

  1. ” == ’0′ // false
  2. ’0′ == ” // true
  3. false == ’0′ // true
  4. ‘ \t\r\n ‘ == 0 // true

错误4-使用数据类型的包装对象

Javascript提供了各个数据类型的包装对象。

  1. new Number(10);
  2. new String(“hello”);
  3. new Boolean(true);
  4. new Object();
  5. new Array(“one”, “two”, “three”);

首先,它们并不好用。上面的代码可以用更少的代码来实现,如下:

  1. 10;
  2. “hello”;
  3. true;
  4. {};
  5. ["one", "two", "three"];

但是这两种方式还是有所不同的。下面是douglas crockford的观点:
例如用new Boolean(false)创建一个对象,该对象有一个方法valueOf,调用该方法会返回构造器的值。
这意味着,如果运行typeof new Number(10)或者是typeof new String(‘hello’),将返回‘object’,而不是’number’或’string’.另外,用数据类型的包装还会引发一些意料之外的结果。
那么为什么javascript要提供数据类型的包装对象呢?这是因为javascript解析器内部会调用。简单的数据类型是没有方法的(因为它们不是 对象),所以当调用简单类型的数据的方法时(如’hello’.replace(‘ello’, ‘i’)),javascript会调用String包装对象来创建一个临时的string对象,然后调用该对象的方法,完成调用后会删除这个临时对象。
所以不要用数据类型的包装对象来创建简单类型的数据。
注意:本来不用这么说明的,但本文还是想明确的告诉初学者:不是说不使用它们和new(尽管有些是推荐使用的),这里需要特别指出的是,这个建议特别针对这些数据类型,如:number、string、Boolean、array和空对象。

错误5-在使用for-in时不对属性检查

我们都很熟悉遍历数组,但是你可能还希望能遍历对象的属性。(题外话:array事实上是属性名为数字的对象)。这是可以用for-in循环语句,代码如下:

  1. var prop, obj = { name: “Joe”, job: “Coder”, age: 25 };
  2. for (var prop in obj) {
  3. console.log(prop + “: ” + obj[prop]);
  4. }

运行上面的代码,输出如下:

  1. name: Joe
  2. job: Coder
  3. age: 25

但是,浏览器中for-in遍历对象属性和方法时会包括对象原型链上的所有属性和方法。但绝大多数属性是不希望被枚举出来的。可以用hasOwnProperties方法来检测属性是否属于对象。代码如下:

  1. Function Dog (name) {
  2. this.name = name;
  3. }
  4. Dog.prototype.legs = 4;
  5. Dog.prototype.speak = function () {
  6. return “woof!”;
  7. };
  8. var d = new Dog(“Bowser”);
  9. for (var prop in d) {
  10. console.log( prop + “: ” + d[prop] );
  11. }
  12. console.log(“=====”);
  13. for (var prop in d) {
  14. if (d.hasOwnProperty(prop)) {
  15. console.log( prop + “: ” + d[prop] );
  16. }
  17. }
  18. // Output
  19. // name: Bowser
  20. // legs: 4
  21. // speak: function () {
  22. return “woof!”;
  23. // }
  24. // =====
  25. // name: Bowser

有时,只希望枚举列出对象的的属性,不包括方法。可以用typeof方法,代码如下:

  1. for (var prop in d) {
  2. if (typeof d[prop] !== ‘function’) {
  3. console.log( prop + “: ” + d[prop] );
  4. }
  5. }

不管怎么样,在用for-in循环时要确保对属性进行检测,以避免得到你意料之外的结果。

错误6-使用with或eval

幸运的是,现在大部分javascript教程都不会教你使用with或eval。但是一些老教程或名气不大的资料时(因为有时候好的资料在网上很难找到),可能会发现有使用with或eval。
下面是两个不用with的主要原因:
1、 它会降低代码性能
2、 不易于代码阅读

第一点是它与生俱来的。第二点,看看下面的代码,这里用with访问person对象的name、age属性。

  1. var person = { name: “Joe”, age : 10 };
  2. with (person) {
  3. console.log(name); // Joe
  4. console.log(age); // 10
  5. }

但是,若果有一个变量和对象其中一个属性同名,那用with会发生什么呢?事实上,这种情况下,访问变量会引用那个变量而不是对象的属性。另一个值 得注意的是,在with语句中,如果访问的属性不存在或对象不存在,就不能给对象添加属性,同时会使得作用域链上with作用域后的那个作用域中创建一个 变量。

  1. var person = { name: “Joe”, age : 10 },
  2. name = “Billy”;
  3. with (person) {
  4. console.log(name); // Billy
  5. job = “Designer”;
  6. }
  7. console.log(person.job); // undefined;
  8. console.log(job); // Designer

那eval呢?它可以接受一个字符串参数,并且解析执行改字符串。

这听起来没有什么不好,甚至觉得很棒,对吗?但问题就是这太棒了!与其将一连串字符串将给它来解析执行,为什么不直接编写在程序中呢?不该这么做的原因如下:

  1. 完全可以直接编写在代码中。
  2. eval解析很慢的,性能跟with差不多。

eval的用法是在非运行时运行环境。可以从服务器端或客户端获取代码。难道真的想你的网站用户来底控制你的代码?这样不就意味着你的网站向无数的黑客敞开了大门。用eval就好比,离开了家,并告诉大家钥匙就是门口垫子下面。如果你爱自己或你的用户,就不要用eval。

错误7-在用parseInt时不用基数

Javascript提供了一个非常有用的方法parseInt,它可以将字符串转换为数值。

  1. parseInt(“200″); // 200
  2. parseInt(“043″); // 35

结果是不是令人觉得意外?第二句为什么不是43?事实上,parseInt方法不仅仅是只能把字符串当做十进制数来转换。当parseInt的第一 个参数是以0开头,它就会把字符串当作是八进制数来转换。这就是不使用基数出现的意料之外结果。第二个参数–基数,会指定parseInt方法把字符串当 做什么进制的数来转换。(当然,它的返回值永远是十进制数)

  1. parseInt(“020″, 10); // 20
  2. parseInt(“100″, 2); // 4

错误8 if和while语句不使用{}

Javascript最明显的特点是语法要求不那么严格。但正是这样的特点,有时会带来麻烦。If和while语句的{}就会引起一些麻烦。{}是根据if条件成立时执行代码语句的条数来用的。

  1. if (true)
  2. console.log(“inside the if statement”);

这里看起来没什么问题,因为这里的执行语句只有一句

  1. var arr = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"],
  2. i = arr.length – i;
  3. while (i) console.log( arr[i--] );

但是这样做不易于阅读:首先,不用{}代码结构看起来不是那么清晰。

  1. if (true)
  2. console.log(“inside the if-statement.”);
  3. console.log(“outside the if-statement.”);

看看上面的代码,第二行console语句是不属于if执行语句的,但是这里它看起来像是if的执行语句。使用{}会使结构更清晰。同时,如果你想在if的执行代码中添加一句,也需要使用{}。习惯使用{}并不是一件难事。

错误9-单个单个地插入dom元素

这并不是javascript自身的问题。99%100的javascript编程都会涉及DOM操作,在对DOM操作上会犯很多错误,但这是最明显的一个。
DOM操作会使浏览器重绘页面,所以如果有一连串的元素一个接一个的插入页面中,这会急剧增加浏览器渲染页面的负担。

  1. var list = document.getElementById(“list”),
  2. items = ["one", "two", "three", "four"],
  3. el;
  4. for (var i = 0; items[i]; i++) {
  5. el = document.createElement(“li”);
  6. el.appendChild( document.createTextNode(items[i]) );
  7. list.appendChild(el); // slow, bad idea
  8. }

Document fragments 是一个DOM元素容器,可以使用它同时添加这些元素到页面中。Document fragment自身不是一个DOM节点,它不会在页面DOM树中显示,并且在把它插入DOM之前它是不可见的。下面是它的用法:

  1. var list = document.getElementById(“list”),
  2. frag = document.createDocumentFragment(),
  3. items = ["one", "two", "three", "four"],
  4. el;
  5. for (var i = 0; items[i]; i++) {
  6. el = document.createElement(“li”);
  7. el.appendChild( document.createTextNode(items[i]) );
  8. frag.appendChild(el); // better!
  9. }
  10. list.appendChild(frag);

非常快速、简洁!

错误10-不懂javascript

许多人不花时间来认真地学习javascript。
Javascript并不等于jquery。这是否吓到你了?如果你会犯以上列出的错误,那么你需要认真地学习javascript。 Javascript是一门语言,一门基本上不用学习就可以使用的语言,这就导致许多人不花时间来认真学习。千万不要这么做,已经有太多太多的教程指出这 样做的弊端,你没有借口不认真学习javascript。如果你只是了解jquery(或mootools,或别的),那么你学习了解 javascript的出发点就已经错了。

错误11-严格遵循以上的规则

“Rules are made to be broken.”(规则是用来被打破的。)

虽然本文列举了以上规则,但像任何事一样,规则是用来被打破的。如果是刚开始学习javascript,你会严于律己,严格遵循以上规则。但是到了 真正理解了为什么要遵循以上规则的原因后,你才会知道灵活运用以上规则。例如,eval被反复的说到不能用,但是它却是唯一能解析服务器端返回json字 符串的方法。当然这里在运用它时会做很多安全的检测(你可能会用到一个javascript库)。这里想要指明的是,在需要的地方,不应该害怕犯错,大胆 的运用它。当然,永远不要犯错误10所指出的问题。

结论:

如果你是javascript新手,希望以上的内容对你javascript编程有所帮助。如果你是一个资深javascript工程师,如过这里有遗漏的,请在留言板中留言告知大家。

本文翻译自《The 11 JavaScript Mistakes you’re Making》

原文地址:http://www.36ria.com/4479

转:javascript contains方法

IE有许多好用的方法,后来都被其他浏览器抄袭了,比如这个contains方法。如果A元素包含B元素,则返回true,否则false。唯一不支持这个方法的是IE的死对头firefox。

提示:你可以先修改部分代码再运行。

不过火狐支持compareDocumentPosition() 方法,这是W3C制定的方法,标准浏览器都支持,不过实用性性很差,因此没有什么人用,推广不开来。它的使用形式与contains差不多,但返回的不是一个布尔值,而是一个很奇怪的数值,它是通过如下方式累加计算出来的:

Bits Number Meaning
000000 0 元素一致
000001 1 节点在不同的文档(或者一个在文档之外)
000010 2 节点 B 在节点 A 之前
000100 4 节点 A 在节点 B 之前
001000 8 节点 B 包含节点 A
010000 16 节点 A 包含节点 B
100000 32 浏览器的私有使用

提示:你可以先修改部分代码再运行。

PPK给出如下解决方法。

if (window.Node && Node.prototype && !Node.prototype.contains){  

   Node.prototype.contains = function (arg) {  

     return !!(this.compareDocumentPosition(arg) & 16)  

   }  

 } 

我搞出个更短的:

if(!!window.find){  

  HTMLElement.prototype.contains = function(B){  

    return this.compareDocumentPosition(B) - 19 > 0  

  }  

} 

提示:你可以先修改部分代码再运行。

descendantOf: function(element, ancestor) {
    if (element.compareDocumentPosition)
    return (element.compareDocumentPosition(ancestor) & 8) === 8;  

  if (ancestor.contains)
    return ancestor.contains(element) && ancestor !== element;  

  while (element = element.parentNode)
    if (element == ancestor) return true;  

  return false;  

},

原文地址:http://www.cnblogs.com/rubylouvre/archive/2011/05/30/1583523.html

转:js判断一个元素是否为另一个元素的子元素

用js判断一个元素是否为另一个元素的子元素,再做一些效果的时候经常用到,特别是和鼠标事件相关的应用中,比如一个浮层,在鼠标操作浮层内元素的时候浮层显示,当点击浮层外的元素的时候隐藏浮层。当然方法有很多,不过个人认为通过判断一个元素是否为另一个元素的子元素是最简单的实现方式之一。

function isParent (obj,parentObj){
    while (obj != undefined && obj != null && obj.tagName.toUpperCase() != 'BODY'){
        if (obj == parentObj){
            return true;
        }
        obj = obj.parentNode;
    }
    return false;
}

查看demo:http://www.css88.com/demo/isParent/
还有另外一种方法(contains)http://www.uedspace.com/blog/411.html

本文转自:http://www.css88.com/archives/3700

js小时倒计时

小时倒计时,可指定具体时间


HTML代码:
<SCRIPT LANGUAGE="JavaScript">
<!--
var maxtime = 60*60 //一个小时,按秒计算,自己调整!
function CountDown(){
if(maxtime>=0){
minutes = Math.floor(maxtime/60);
seconds = Math.floor(maxtime%60);
msg = "距离结束还有"+minutes+"分"+seconds+"秒";
document.all["timer"].innerHTML=msg;
if(maxtime == 5*60) alert('注意,还有5分钟!');
--maxtime;
}
else{
clearInterval(timer);
alert("时间到,结束!");
}
}
timer = setInterval("CountDown()",1000);
//-->
</SCRIPT>
<div id="timer" style="color:red"></div>

js拖拽效果


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "<a href="http://www.w3.org/TR/html4/strict.dtd">http://www.w3.org/TR/html4/strict.dtd</a>">
<html>
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <title>js拖拽效果</title>
  <script type="text/javascript">
   var dragState = 0 ; //初始化dragState为0
   var drag_ele; //声明拖拽元素
   var mouse_x,mouse_y,obj_x,obj_y; //声明全局变量mouse_x、mouse_y、obj_x、obj_y
   
   //页面加载后执行函数
   window.onload = function(){
    drag_ele = document.getElementById('drag_ele');
    drag_ele.style.left = '50px';
    drag_ele.style.top = '50px';
    drag_ele.onmousedown = mouseDown;
    document.onmousemove = mouseMove;
    drag_ele.onmouseup = mouseUp;
   }
   
   //创建mouseDown函数来获取鼠标按下时当前鼠标的x、y坐标和当前对象的x、y坐标
   //给函数设置一个参数来解决火狐下的兼容window.event问题,火狐下没有window.event事件对象
   function mouseDown(ev){
    ev = ev || window.event;
    dragState = 1;
    mouse_x = parseInt(ev.clientX)+(document.documentElement.scrollLeft || document.body.scrollLeft);
    mouse_y = parseInt(ev.clientY)+(document.documentElement.scrollTop || document.body.scrollTop);
    obj_x = parseInt(drag_ele.style.left);
    obj_y = parseInt(drag_ele.style.top);
   }
   
   //创建mouseMove函数来获取鼠标移动时当前元素的x和y坐标
   //在这里有一个判断如果dragState==1 再继续执行以下代码
   //声明变量 x(当前元素的x坐标) y (当前元素的y坐标)
   //元素x = 现在鼠标x - 原来鼠标x + 原来元素x
   //元素y = 现在鼠标y - 原来鼠标y + 原来元素y
   //设置drag_ele的x和y坐标
   function mouseMove(ev){
    ev = ev || window.event;
    if(dragState ==1){
     var x,y;
     x = parseInt(ev.clientX)+(document.documentElement.scrollLeft || document.body.scrollLeft) - mouse_x + obj_x;;
     y = parseInt(ev.clientY)+(document.documentElement.scrollTop || document.body.scrollTop) - mouse_y + obj_y;
     drag_ele.style.left = x+'px';
     drag_ele.style.top = y+'px';
    }
   }
   
   //鼠标松开dragState=0;
   function mouseUp(){
    dragState = 0;
   }   
  </script>
 </head>
 <body style="height:2000px;">
  <div style="width:100px;height:100px;background:#666; position:absolute; cursor:move;" id="drag_ele">拖拽</div>
 </body>
</html>

window.event对象详细介绍

1、event代表事件的状态,例如触发event对象的元素、鼠标的位置及状态、按下的键等等。event对象只在事件发生的过程中才有效。event的某些属性只对特定的事件有意义。比如,fromElement 和 toElement 属性只对 onmouseover 和 onmouseout 事件有意义。

2、属性:
altKey, button, cancelBubble, clientX, clientY, ctrlKey, fromElement, keyCode, offsetX, offsetY, propertyName, returnValue, screenX, screenY, shiftKey, srcElement, srcFilter, toElement, type, x, y
3、属性详细说明:
属性名 描述 值 说明
altKey 检查alt键的状态 当alt键按下时,值为True否则为False 只读
shiftKey 检查shift键的状态 当shift键按下时,值为True否则为False 只读
ctrlKey 检查ctrl键的状态 当ctrl键按下时,值为True否则为False 只读
例:(点击按钮时显示那几个特殊键按下的状态)
<input type=”button” value=”点击” onClick=”showState()”/>
<script>
function show(){
 alert(“altKey:”+window.event.altKey
  +”\nshiftKey:”+window.event.shiftKey
  +”\nctrlKey:”+window.event.ctrlKey);
}</script>
 keyCode  检测键盘事件相对应的内码    可读写,可以是任何一个Unicode键盘内码。如果没有引发键盘事件,则该值为0
例:(按回车键让下一组件得到焦点,相当按Tab键)
<input type=”text” onKeyDown=”nextBlur()”/>
<input type=”text”/>
<script>
function nextBlur(){
 if(window.event.keyCode==13)//回车键的 code
  window.event.keyCode=9;//Tab键的code
}
</script>
 srcElement  返回触发事件的元素  Object  只读
例:(点击按钮时显示按钮的name值)
<input type=”button” value=”闽” name=”福建” onClick=”show()”/>
<input type=”button” value=”赣” name=”江西” onClick=”show()”/>
<script>
function show(){
 alert(window.event.srcElement.name);
}
</script>
 x,y  鼠标相对于当前浏览器的位置  px  只读
 clientX,clientY  鼠标当前相对于网页的位置  px  只读
 offsetX,offsetY  鼠标当前相对于网页中的某一区域的位置  px  只读
 screenX,screenY  相对于用户显示器的位置  px  只读
说明:当你点击一个按钮时得到(x,clientX,offsetX,screenX)很容易明白offsetX;当你把IE窗口还原后得到(x,clientX,screenX),你就会明白screenX;当你把div的属性position在absolute和relative之间切换时,你就会明白x和clientX的区别。
 returnValue  设置或检查从事件中返回的值  true 事件中的值被返回
false 源对象上事件的默认操作被取消  可读写
例如:屏蔽鼠标右键、Ctrl+n、shift+F10、F5刷新、退格键
function KeyDown(){
 //屏蔽鼠标右键、Ctrl+N、Shift+F10、F5刷新、退格键
  if ((window.event.altKey)&&
      ((window.event.keyCode==37)||   //屏蔽 Alt+ 方向键 ←
       (window.event.keyCode==39))){  //屏蔽 Alt+ 方向键 →
     event.returnValue=false;//防止使用ALT+方向键前进或后退网页
  }
  if ((event.keyCode==8) ||      //屏蔽退格删除键
      (event.keyCode==116)||   //屏蔽 F5 刷新键
      (event.keyCode==112)||   //屏蔽 F1 刷新键 bitsCN.com中国网管联盟
      (event.ctrlKey && event.keyCode==82)){ //Ctrl + R
     event.keyCode=0;
     event.returnValue=false;
  }
  if ((event.ctrlKey)&&(event.keyCode==78))   //屏蔽Ctrl+N
     event.returnValue=false;
  if ((event.shiftKey)&&(event.keyCode==121)) //屏蔽Shift+F10
     event.returnValue=false;
  if (window.event.srcElement.tagName == “A” && window.event.shiftKey)
      window.event.returnValue = false;  //屏蔽 shift 加鼠标左键新开一网页
  if ((window.event.altKey)&&(window.event.keyCode==115)){ //屏蔽Alt+F4
      window.showModelessDialog(“about:blank”,”",”dialogWidth:1px;dialogHeight:1px”);
      return false;}
}
 button  检查按下的鼠标键  0 没按键
1 按左键
2 按右键
3 按左右键
4 按中间键
5 按左键和中间键
6 按右键和中间键
7 按所有的键  仅用于onmousedown,onmouseup和onmousemove事件。对其他事件,不管鼠标状态如何,都返回0(比如onclick)
 srcElement  检测onmouseover和onmouseout事件发生时,鼠标所离开的元素  Object  只读
 toElement  检测onmouseover和onmouseout事件发生时,鼠标所进入的元素  Object  只读
 type  返回事件名    返回没有“on”作为前缀的事件名,比如,onclick事件返回的type是click

在IE与火狐下window.event对象的区别

window.event
IE:有window.event对象
FF:没有window.event对象。可以通过给函数的参数传递event对象。如onmousemove=doMouseMove(event)
鼠标当前坐标
IE:event.x和event.y。
FF:event.pageX和event.pageY。
通用:两者都有event.clientX和event.clientY属性。

鼠标当前坐标(加上滚动条滚过的距离)
IE:event.offsetX和event.offsetY。
FF:event.layerX和event.layerY。
标签的x和y的坐标位置:style.posLeft 和 style.posTop
IE:有。
FF:没有。
通用:object.offsetLeft 和 object.offsetTop。

窗体的高度和宽度
IE:document.body.offsetWidth和document.body.offsetHeight。注意:此时页面一定要有body标签。
FF:window.innerWidth和window.innerHegiht,以及document.documentElement.clientWidth和document.documentElement.clientHeight。
通用:document.body.clientWidth和document.body.clientHeight。

添加事件
IE:element.attachEvent(”onclick”, func);。
FF:element.addEventListener(”click”, func, true)。
通用:element.onclick=func。虽然都可以使用onclick事件,但是onclick和上面两种方法的效果是不一样的,onclick只有执行一个过程,而attachEvent和addEventListener执行的是一个过程列表,也就是多个过程。例如:element.attachEvent(”onclick”, func1);element.attachEvent(”onclick”, func2)这样func1和func2都会被执行。

标签的自定义属性
IE:如果给标签div1定义了一个属性value,可以div1.value和div1[”value”]取得该值。
FF:不能用div1.value和div1[”value”]取。
通用:div1.getAttribute(”value”)。

父节点、子节点和删除节点
IE:parentElement、parement.children,element.romoveNode(true)。
FF:parentNode、parentNode.childNodes,node.parentNode.removeChild(node)。

记录几道前端面试题

收集的一些前端面试题在这里记录一下,以后也要常练习。

一、


//输出变量的数据类型、变量的作用域;
(function(){
 var a=b=2;
 alert(typeof a); //输出number
 alert(typeof b); //输出number
})()
window.onload=function(){
alert(b); //输出数字2,b在这里属于全局变量可以直接输出2
alert(a); //未定义,a在这里属于局部变量是无法访问的(会报错)
}

二、


   //考while的使用,在这里表达式不成立,所以是空
   var a = 8;
   while(a<=5){
    document.write(a);
    a++;
   }
   
   //考 do while的使用,do while 循环至少要执行一次所以这里输出的是8
   var a = 8;
   do{
    document.write(a);
    a++; 
   }
   while(a<=5);

jQuery1.5的改进细节

jQuery 1.5 beta1出来了,从学习跟进上来说,这一次已经比较晚了(我竟然不知道1.5什么时候出的alpha,就这么beta了)。

这个1.5版本最大的更新是AJAX的完全重写,提供了更强的可扩展性。但是受制于精力和篇幅,对新的AJAX的分析还是放到下回,本篇先简单介绍一下细节方面的改进。

jQuery._Deferred和jQuery.Deferred

首先不得不说这两个新生事物,因为他们是作为基础设施存在,不把这两个东西讲明白了,有些问题根本没办法解释。

首先,jQuery.Deferred是jQuery._Deferred的增强版,因此对于这个问题,从jQuery._Deferred入手,就能说明一大半的问题。

什么是Deferred?从字面上看,我的第一反应是“延迟加载”,首字母大写的应该是“类型”的定义,所以这大概是一个“透明提供延迟加载功能”的类型吧。然而实际上,虽然确实带有那么一点点“延迟”的意思,这个东西却不是用来实现延迟加载的。

简单来说,jQuery._Deferred是一个函数队列,他的作用有以下几点:

  • 保存若干个函数。
  • 在特定的时刻把保存着的函数全部执行掉。
  • 执行过后,新进来的函数会立刻执行。

感觉是不是和啥东西很像?对,jQuery的ready函数就是这样的逻辑,实际中jQuery 1.5中的ready函数也确实被嫁接到这上面去了。

jQuery._Deferred提供下面的接口:

  • done:function(fn1, fn2, …)的形式,用于把函数添加到队列中。
  • fire:function(context, args)的形式,使用context指定this对象,args指定参数,调用队列中所有函数。fire被调用后,_Deferred会进入isResolved状态,未来对done的调用不会再保存函数,而是直接调用函数。
  • resolve:相当于调用fire(this, arguments),一个简化的方法。
  • isResolved:用来判断_Deferred是否在isResolved状态,具体参考前面的fire函数的解释。
  • cancel:取消掉整个队列,这样不管未来是不是fire,队列中的函数都不会再被调用。

说明白了jQuery._Deferred,再来看看jQuery.Deferred。这个东西其实就是2个_Deferred组成的,第一个称为deferred,用于保管“正常”状态下的函数;第二个称为failDeferred,用于保管“出错”状态下的函数。同时jQuery.Deferred提供了一些新的接口:

  • then:function(done, fail)的形式,把done添加进deferred,把fail添加进failedDeferred。
  • fail:相当于failDeferred的done函数。
  • fireReject:相当于failDeferred的fire函数。
  • reject:相当于failDeferred的resolve函数。
  • isRejected:相当于failDeferred的isResolved函数。

同时jQuery.Deferred取消了cancel函数。

那么这个是啥用的呢?有“正常”和“出错”2个状态,同时又是异步的,很容易就能想到……对,给AJAX用的,在下一篇分析中再详细说明。

jQuery.ready的变化

因为有了jQuery._Deferred这个东西,jQuery.ready函数变成依赖于函数队列,具体的变化有:

原来的readyList变量已经不再是一个数组,而变成了jQuery._Deferred对象。

原本在DOMContentLoaded时,调用readList中所有函数的逻辑,现在也使用了jQuery._Deferred中,原来的代码:

while ( (fn = ready[ i++ ]) ) {
    fn.call( document, jQuery );
}

变成了:

readyList.fire( document , [ jQuery ] );

jQuery.parseXML函数

新增了静态函数jQuery.parseXML,用于提供浏览器兼容的从字符串转为XML文档的功能。

该函数的逻辑网上有很多,jQuery也没有特别的地方,大致分为以下2种:

  • 对于标准浏览器,使用DOMParser对象:
    var parser = new DOMParser();
    var xml = parser.parseFromString(text, 'text/html');
  • 对于IE,使用Microsoft.XMLDOM对象:
    var parser = new ActiveXObject('Microsoft.XMLDOM');
    parser.async = 'false';
    parser.loadXML(text);
    var xml = parser.documentElement;

data部分

添加了jQuery.hasData函数,用于判断一个元素是否有jQuery附加上去的数据。

修改了jQuery.expando的实现,在原来单纯地取当前时间的基础上,添加了一个随机数:

expando = "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" );

这样保证在同一时间,引入多个jQuery副本,这几个副本之间的expando不会相互冲突,导致元素上的data变得错乱。一般来说,是不会引入多个jQuery副本的,但是使用SealJS等的时候,配置不当的话,也是很容易出现此类问题的。

DOM操作部分

原本的hasClass、addClass、removeClass函数都需要将元素的class属性分隔为数组,在1.4.4版本中,通过\n或\t进行分隔,在1.5中增加了一个\r,用于对应Windows平台下的换行符(\r\n)。

jQuery.fn.attr函数,1.4.4版本中拒绝从TextNode和CommentNode上获取属性,在1.5版本中添加了一个AttributeNode(noteType == 2)。

在1.4.4版本中,jQuery会在页面unload的时候清理掉由jQuery维护的所有DOM事件,这是为了避免IE的内存泄露问题。但是在1.5中这一段代码不见了,不知是出于什么考虑。

对于IE下使用cloneNode复制节点,会将事件也一起复制过来的问题,1.4.4中是采取复制innerHTML的方式给予解决,而在1.5中则采纳了mootools团队提供的方法,使用cloneFixAttribute函数修正该问题。

cloneFixAttribute函数们于jQuery 1.5 beta1源码文件的5388-5438行,处理IE的BUG的原理很简单,当然前端里一些看似简单的东西,都是很难发现的:

  1. IE中有个叫clearAttributes的函数,会清除到节点上的所有属性,顺便把和事件相关的onclick之类的属性也去掉了。在复制出来的节点上调用这个函数,就会把属性清得干干净净。
  2. IE中还有一个叫mergeAttributes的函数,把一个节点的属性复制到另一个节点上,但他不会把和事件相关的属性复制过去。所以再把原始节点调用mergeAttributes,把属性重新放回复制出来的节点上,这就相当于起到了去除事件相关属性的作用。

另外cloneFixAttribute函数还处理了非常多IE6-8在cloneNode上的兼容性问题,非常值得详细研究。

AJAX部分

AJAX已经完全重写了,只留下一点边边角角保留着1.4.4版本的风采,这里只抽取一部分进行简单的说明。

原来版本中$.get和$.post的实现非常相似,具体来说仅有一个method配置项不同,因此在1.5版本中被合并起来了:

$.each(['get', 'post'], function(i, method) {
    $[method] = function() { ... };
});

ajaxSetup函数现在加了一行return this;,可以链式调用了。

serializeArray函数现在统一将value中的换行符替换成Windows的风格(\r\n)。

AJAX的回调函数中,作为参数的对象不再是原生的XMLHTTPRequest,而是jQuery自己封装的称为jXHR的对象,这个对象提供了XMLHTTPRequest的常用接口。

原本对于“请求成功”的浏览器状态码,除200-299以及304外,还有一个1223,来自于IE的一个BUG,会将204的状态码变成1223。现在因为有了jXHR对象,相当于中间多了一层,因此从jXHR对象获取statusCode不会出现1223的情况,已经被变回204了。

jQuery.ajax函数的配置项中多了一个statusCode项,其结构为map,用于指定返回特定状态码时的回调函数,大致形式如下:

jQuery.ajax({
    url: 'xxx',
    statusCode: {
        200: function() { 处理请求成功 },
        404: function() { 处理页面未找到 },
        503: function() { 处理Service Unavailable }
    }
});

再添加了这个回调后,jQuery.ajax函数已经有非常多的回调函数,其触发过程如下:

  1. 根据返回的状态码,触发success或者error回调。
  2. 根据状态码,触发对应的statusCode回调。
  3. 触发complete回调。
  4. 触发全局ajaxComplete回调。
  5. 如果此时没有正在执行的AJAX,触发全局ajaxStop回调。

其他细节

入口函数jQuery.fn.init现在多了一个参数,值始终为rootjQuery,用于加速init函数中对rootjQuery变量的查找速度(减少了一层作用域):

//jQuery 1.5 beta1 源码23行
jQuery = function( selector, context ) {
    // The jQuery object is actually just the init constructor 'enhanced'
    return new jQuery.fn.init( selector, context, rootjQuery );
}

jQuery对象支持继承了,具体的修改是将几处直接调用jQuery的代码改为了对this.constructor的调用:

202行:return this.constructor( context ).find( selector );
253行:var ret = this.constructor();
334行:return this.prevObject || this.constructor(null);

同时还提供了jQuery.subclass函数用于创建一个继承自jQuery的类型,由于不是很常用jQuery,更是从来没有用到过需要继承jQuery的情况,因此也不方便说这个功能的作用有多大。

原文地址:http://www.otakustay.com/jquery-1-5-enhanced-detail/

jQuery 1.5发布 Ajax模块重写

美国时间1月31日John Resig在jQuery官方博客发表文章,宣布jQuery 1.5正式版已经如期开发完成,可以下载使用。压缩版本jQuery Minified 29KB,不压缩版本jQuery Regular(用于阅读和调试)207KB。由于jQuery已经成为目前最流行的JavaScript库,得到广泛的支持,新版本的发布当然非常引人注目。

jQuery创始人John Resig在CSDN TUP活动上演讲(相关报道

另外,微软和Google也为jQuery 1.5提供了CDN支持,可以直接导入:

微软:http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.5.min.js

Google:https://ajax.googleapis.com/ajax/libs/jquery/1.5.0/jquery.min.js (发稿时为止尚未上线)

新版本的主要改进可以参见变更日志:http://api.jquery.com/category/version/1.5/

John Resig总结了几点比较引人注目的变化。

1. Ajax重写

Ajax模块完全进行了重写。新增一个jXHR对象,为不同浏览器内置的XMLHttpRequest提供了一致的超集。对于XMLHttpRequest之外的传输机制,比如JSONP请求,jXHR对象也可以进行处理。(详情可以参见:jQuery.ajax文档

此外,系统的可扩展性大大增强,可以附加各种数据处理器、过滤器和传输机制,为开发新的Ajax插件提供了方便。(详情参见:Ajax扩展文档

2. 延迟对象

延迟对象(Deferred Object,jQuery.Deferred对象)是一个可链接的(chainable)实用工具对象,实现了Promise接口,可以在回调队列中注册多个回调、调用回调队列并转发任何同步/异步函数的成败状态。正如Using Deferreds in jQuery 1.5一文中说明的,其结果是在jQuery中能够将依赖于某个任务(事件)结果的逻辑与任务本身解耦了。这一点在JavaScript中其实并不新鲜,Mochikit和Dojo等已经实现有些日子了。由于jQuery 1.5的Ajax模块内置使用了延迟对象,因此现在通过jQuery编写Ajax程序将自动获得这一功能。

开发人员借此可以使用无法立即获得的返回值(如异步Ajax请求的返回结果),而且第一次能够附加多个事件处理器。

例如,使用了新的jQuery内部Ajax API就可以实现下面的代码了:

// Assign handlers immediately after making the request,
// and remember the jxhr object for this request
var jxhr = $.ajax({ url: "example.php" })
    .success(function() { alert("success"); })
    .error(function() { alert("error"); })
    .complete(function() { alert("complete"); });

// perform other work here ...

// Set another completion function for the request above
jxhr.complete(function(){ alert("second complete"); });

此外,使用jQuery.Deferred还可以开发自己的延迟对象。更多详情参见:延迟对象文档。 

3. jQuery.sub()

jQuery 1.5提供了一种创建和修改jQuery副本的方式。可以用来添加不向外部公开的方法,或者对jQuery的某些方法进行重新定义以提供新功能,或者提供更好的封装、避免名称空间冲突。当然,也可以用来开发插件,但Resig强烈建议在开发插件之前,先考虑jQuery UI widget工厂。

值得注意的是,sub函数并不提供真正的隔离,所有方法、数据、调用仍然依靠jQuery本身来支持。

4. 遍历性能提高

在新版本中.children(), .pre(), .next()几个常用的遍历函数性能有了显著提高。比如.children()的数据如下所示:

详细的测试用例和数据可以参考这里

5. 内部开发系统

John Resig还特别提到了jQuery团队内部开发系统的两点改变:一是服务器端用Node.js替换了老的Java/Rhino系统,使得团队可以专注于JavaScript环境的新变化;二是所用的代码优化程序从Google Closure切换到UglifyJS,新工具的压缩效果非常令人满意。

有意思的是,此前UglifyJS开发者曾经公布过自己的测试结果,表明对jQuery的压缩结果UglifyJS要比Closure略大(都在72KB左右),但运行速度快得多,而且Closure不太安全。看来,这段时间UglifyJS的进展也很快啊。

对了,如果你在使用中发现问题,可以直接到这里报告bug:http://bugs.jquery.com/。另外,也可以到Reddit上参与讨论,别忘了,John Resig同学是那里的版主,随时盯着呢。

【延伸阅读】

jQuery 1.5的改进细节

学习jQuery必须知道常用的几种方法

Web开发者必知的12款jQuery插件

免费电子书:jQuery Fundamentals(英文)