Javascript中的常见问题
1. 集合类对象问题
现有代码中许多集合类对象取用时使用 (),IE 能接受,Firefox 不能。
解决方法:改用 [] 作为下标运算。如:document.forms("formName") 改为
1. 集合类对象问题
现有代码中许多集合类对象取用时使用 (),IE 能接受,Firefox 不能。
解决方法:改用 [] 作为下标运算。如:document.forms("formName") 改为
- document.forms["formName"];
- //又如:
- document.getElementsByName("inputName")(1);
- //改为
- document.getElementsByName("inputName")[1];
2. DIV对象
在 IE 中,DIV对象可以使用ID作为对象变量名直接使用。在 Firefox 中不能。
DivId.style.display = "none";
解决方法:document.getElementById("DivId").style.display = "none";
ps:得对象的方法不管是不是DIV对象,都应该使用getElementById方法。
3. 关于frame
现有问题:在 IE中 可以用window.testFrame取得该frame,mf中不行
解决方法:在frame的使用方面Firefox和IE的最主要的区别是:
如果在frame标签中书写了以下属性:
那么IE可以通过id或者name访问这个frame对应的window对象
而mf只可以通过name来访问这个frame对应的window对象
例如如果上述frame标签写在最上层的window里面的htm里面,那么可以这样访问
IE: window.top.frameId或者window.top.frameName来访问这个window对象
Firefox:只能这样window.top.frameName来访问这个window对象
另外,在mf和ie中都可以使用window.top.document.getElementById("frameId")来访问frame标签
并且可以通过window.top.document.getElementById("testFrame").src = 'xx.htm'来切换frame的内容
也都可以通过window.top.frameName.location = 'xx.htm'来切换frame的内容
4. 窗口
现有问题:IE中可以通过showModalDialog和showModelessDialog打开模态和非模态窗口,但是Firefox不支持。
解决办法:直接使用window.open(pageURL,name,parameters)方式打开新窗口。
如果需要传递参数,可以使用frame或者iframe。
5. 在JS中定义各种对象变量名时,尽量使用id,避免使用name.
在 IE 中,HTML 对象的 ID 可以作为 document 的下属对象变量名直接使用。在 Firefox 中不能,所以在平常使用时请尽量使用id,避免只使用name,而不使用id。
6. document.all
Firefox可以兼容document.all, 但会生成一条警告。可以用getElementById("*") 或者 getElementByTagName("*)来代替
不过对于document.all.length等属性,则完全不兼容。大家尽量不要使用document.all属性.
7. parentElement
IE中支持使用parentElement和parentNode获取父节点.
而Firefox只可以使用parentNode.
8. event
W3C不支持windows.event
比方说,在IE里面:
- function onMenuClick(){
- collapseMenu(event.srcElement);
- }
工作正常。不过在Firefox中,则改成:
- function onMenuClick(evt){
- if(evt == null)
- evt = window.event; // For IE
- var srcElement = evt.srcElement? evt.srcElement : evt.target;
- // IE使用srcElement, 而Firefox使用target
- collapseMenu(srcElement);
9. event.x 与 event.y 问题
在IE 中,event 对象有 x, y 属性,Firefox中没有。
解决方法:
在Firefox中,与event.x 等效的是 event.pageX。但event.pageX IE中没有。
故采用 event.clientX 代替 event.x。在IE 中也有这个变量。
event.clientX 与 event.pageX 有微妙的差别(当整个页面有滚动条的时候),
不过大多数时候是等效的。
如果要完全一样,可以稍麻烦些:
- mX = event.x ? event.x : event.pageX;
然后用 mX 代替 event.x
10. 用idName字符串取得对象的问题
在IE中,利用 eval(idName) 可以取得 id 为 idName 的 HTML 对象,在Firefox中不能。
解决办法:用 getElementById(idName) 代替 eval(idName).
14. nodeName 和 tagName 问题
在Firefox中,所有节点均有 nodeName 值,但 textNode 没有 tagName 值。
在IE中nodeName 的使用有时会有问题。
解决方法:
使用 tagName,但应检测其是否为空。
15. input的type属性
IE下 input.type属性为只读,但是Firefox下可以修改.
16. 自定义属性
在mf中,自己定义的属性必须getAttribute()取得
而IE可以直接通过"."运算符获取.
17.const 问题
在 IE 中不能使用 const 关键字。如
const constVar = 32;
在IE中这是语法错误.
解决方法:
不使用 const ,以 var 代替.
18. body 对象
Firefox的body在body标签没有被浏览器完全读入之前就存在,而IE则必须在body完全被读入之后才存在.
19. img对象alt和title的解析
alt:当照片不存在或者load错误时的提示,
title:照片的tip说明,
在IE中如果没有定义title,alt也可以作为img的tip使用,但是在Firefox中,两者完全按照标准中的定义使用
在定义img对象时,最好将alt和title对象都写全,保证在各种浏览器中都能正常使用
20.childNodes获取的节点
childNodes的下标的含义在IE和Firefox中不同,Firefox使用DOM规范,childNodes中会插入空白文本节点。
获取子节点时,一般可以通过node.getElementsByTagName()来回避这个问题。
21.removeNode()
Firefox中节点没有removeNode方法,必须使用如下方法
10. 用idName字符串取得对象的问题
在IE中,利用 eval(idName) 可以取得 id 为 idName 的 HTML 对象,在Firefox中不能。
解决办法:用 getElementById(idName) 代替 eval(idName).
14. nodeName 和 tagName 问题
在Firefox中,所有节点均有 nodeName 值,但 textNode 没有 tagName 值。
在IE中nodeName 的使用有时会有问题。
解决方法:
使用 tagName,但应检测其是否为空。
15. input的type属性
IE下 input.type属性为只读,但是Firefox下可以修改.
16. 自定义属性
在mf中,自己定义的属性必须getAttribute()取得
而IE可以直接通过"."运算符获取.
17.const 问题
在 IE 中不能使用 const 关键字。如
const constVar = 32;
在IE中这是语法错误.
解决方法:
不使用 const ,以 var 代替.
18. body 对象
Firefox的body在body标签没有被浏览器完全读入之前就存在,而IE则必须在body完全被读入之后才存在.
19. img对象alt和title的解析
alt:当照片不存在或者load错误时的提示,
title:照片的tip说明,
在IE中如果没有定义title,alt也可以作为img的tip使用,但是在Firefox中,两者完全按照标准中的定义使用
在定义img对象时,最好将alt和title对象都写全,保证在各种浏览器中都能正常使用
20.childNodes获取的节点
childNodes的下标的含义在IE和Firefox中不同,Firefox使用DOM规范,childNodes中会插入空白文本节点。
获取子节点时,一般可以通过node.getElementsByTagName()来回避这个问题。
21.removeNode()
Firefox中节点没有removeNode方法,必须使用如下方法
- node.parentNode.removeChild(node);
22.innerText
IE支持,FIREFOX不支持
FF中设置内容文本是用textConent属性.
23. XMLHTTP的区别
FireFox中的创建方法为:
- xmlhttp=new XMLHttpRequest()
而在IE中为:
- xmlhttp=new ActiveXObject(”Microsoft.XMLHTTP”)
24. img的src刷新问题
在IE 下可以用 可以刷新图片,但在FireFox下不行。主要是缓存问题,在地址后面加个随机数就解决了:
- myImg.src=this.src+’?'+Math.random();
25. setAttribute()设置属性问题
IE中很多属性都不能用setAttribute进行设置,但Firefox中却可以,如:
- theDiv.setAttribute('style','color:red');
- //改为:
- object.style.cssText = 'color:red;';
- setAttribute('class','styleClass')
- //改为:
- setAttribute('className','styleClass');
- obj.setAttribute('onclick','funcitonname();');
- //改为:
- obj.οnclick=function(){fucntionname();};
...等等
-----------------------------------------------------------------------------------------
IE和FIREFOX在解析CSS方面的区别
1. 对高度的解析
IE:将根据内容的高度变化,包括未定义高度的图片内容,即使定义了高度,当内容超过高度时,将使用实际高度
Firefox:没有定义高度时,如果内容中包括了图片内容,Firefox的高度解析是根据印刷标准,这样就会造成和实际内容高度不符合的情况;当定义了高度,但是内容超过高度时,内容会超出定义的高度,但是区域使用的样式不会变化,造成样式错位。
结论:大家在可以确定内容高度的情况下最好定义高度,如果真的没有办法定义高度,最好不用使用边框样式,否则样式肯定会出现混乱!
3.布局问题
当你在写css的时候,特别是用float: left(或right)排列一窜图片时,会发现在firefox里面正常而IE里面有问题。无论你用margin:0,还是border: 0来约束,都无济于事。
其实这里还有另外一个问题,就是IE对于空格的处理,firefox是忽略的而IE对于块与块之间的空格是处理的。也就是说一个div结束后要紧接着一个div写,中间不要有回车或者空格。不然也许会有问题,比如3px的偏差,而且这个原因很难发现。
非常不走运的是我又碰到了这样的问题,多个img标签连着,然后定义的float: left,希望这些图片可以连起来。但是结果在firefox里面正常而IE里面显示的每个img都相隔了3px。我把标签之间的空格都删除都没有作用。
后来的解决方法是在img外面套li,并且对li定义margin: 0,这样就解决了IE和firefox的显示偏差。IE对于一些模型的解释会产生很多错误问题,只有多多尝试才能发现原因。
这只是一些简单的区别,在做布局和CSS设计时候可以综合考虑,但最为有效与简单的解决兼容问题还是用TABLE表格,表格在兼容性方面有着不错的表现.
4.鼠标样式
firefox不支持hand,但ie支持pointer
解决方法: 统一使用pointer
5. padding 问题
padding 5px 4px 3px 1px FireFox无法解释简写,
必须改成 padding-top:5px; padding-right:4px; padding-bottom:3px; padding-left:1px;
6. 消除ul、ol等列表的缩进
消除ul、ol等列表的缩进样式应写成:list-style:none;margin:0px;padding:0px;
其中margin属性对IE有效,padding属性对FireFox有效
7. CSS透明
IE:filter:progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=60)。
FF:opacity:0.6。
8. CSS圆角
IE:不支持圆角。
FF: -moz-border-radius:4px,或者
-moz-border-radius-topleft:4px;
-moz-border-radius-topright:4px;
-moz-border-radius-bottomleft:4px;
-moz-border-radius- bottomright:4px;。
9. CSS双线凹凸边框
IE:border:2px outset;。
FF:
-moz-border-top-colors: #d4d0c8 white;
-moz-border-left-colors: #d4d0c8 white;
-moz-border-right-colors:#404040 #808080;
-moz-border-bottom-colors:#404040 #808080;
10. 滤镜
IE中支持使用滤镜,而Firefox中不支持.
11. 禁止选取网页内容:
在IE中一般用js:obj.onselectstart=function(){return false;};
而firefox用CSS:-moz-user-select:none;
JavaScript高级程序设计(第3版)
* 如果在函数中使用var 定义变量,那么变量在函数退出后就会被销毁。
* 5种基本数据类型:Undefined、Null、Boolean、Number、String。
* 1种复杂数据类型:Object [Object本质上是由一组无序的名值对组成的]
* ECMAScript不支持任何创建自定义类型的机制,而所有值最终都将是上述6种数据类型之一。
* typeof操作符(而不是函数,所以可以不用圆括号)的 返回值:
"undefined" ------ 如果这个值 未定义
"boolean" ------ 如果这个值是 布尔值
"string" ------ 如果这个值是 字符串
"number" ------ 如果这个值是 数值
"object" ------ 如果这个值是 对象或null
"function" ------ 如果这个值是 函数
* 对未经声明的变量调用delete不会导致错误,但没什么实际意义,且在严格模式下确实会导致错误。
注:即便未初始化的变量会自动被赋予undefined值,但显式地初始化变量依然是明智的选择。如果能够做到这一点,那么当typeof操作符返回"undefined"值时,我们就知道被检测的变量还没有被声明,而不是尚未初始化。
* Null 类型
Null类型 的值是 null。 从逻辑角度来看,null值表示一个空对象指针,而这也正是使用typeof操作符检测null值时会返回"object"的原因。如:var car = null; alert(typeof car); => "object"
如果定义的变量准备在将来用于保存对象,那么最好将该变量初始化为null而不是其他值。这样一来,只要直接检查nll值就可以知道相应的变量是否已经保存了一个对象的引用,如: i
f(car !=null ){ //对car对象执行操作 }
实际上,undefined值是派生自null值的, 因此ECMA-262规定对它们的相等性测试要返回true:
alert( null == undefined ); // true
* Boolean类型的字面值 true 和 false 是区分大小写的。也就是说,True 和 False 都不是Boolean值,只是标识符。
将一个值转换为其对应的Boolean值,可调用转型函数Boolean(),如下所示:
var message = "Hello world!";
var messageAsBoolean = Boolean(message);
在上例中,字符串message被转换成一个Boolean值,该值被保存在messageAsBoolean变量中。可以对任何数据类型的值调用Boolean()函数,而且总会返回一个Boolean值。至于返回的这个值是true还是false,取决于要转换值的数据类型及其实际值。
数据类型 转换为true的值 转换为false的值
Boolean true false
String 任何非空字符串 "" (空字符值)
Number 任何非零数字值(包括无穷大) 0 和 NaN
Object 任何对象 null
Undefined n/a(not applicable - 不适用) undefined
var floatNum1 = 1. ; //小数点后面没有数字 --- 解析为 1 [如果小数点后面没有跟任何数字-->整数保存,如本身表示为一个整数-->整数
var floatNum2 = 10.0; // 整数 --- 解析为10
var floatNum = 3.125e7; //等于31250000 这只是一种表示变量floatNum的形式--简洁,但它的实际值则是31250000。3.125*10的7次幂
注:浮点数值的最高精度是17位小数,但在进行算术计算时其精确度远远不如整数。例如,0.1 + 0.2 != 0.3,而是0.30000000000000004。
如,if( a+b == 0.3 ) alert("You got 0.3."); //不要做这样的测试!
* 数值范围
由于内存的限制,能够表示的最小数值保存在 Number.MIN_VALUE (是5e-324);
最大数值保存在Number.MAX_VALUE中(是1.7976931348623157e+308)。
如果超出JS数值范围那么将被自动转换成特殊的Infinity值。原为负转换成 -Infinity(负无穷), 原为正数转换成 Infinity(正无穷)。
如果想判断一个数值是不是有穷的,即是不是位于最小 和 最大的数值之间,可用 isFinite() 函数判断。
* NaN (Not a Number -- 非数值), 任何数值除以0都会导致错误,从而停止代码执行。在JS中,任何数值除以0会返回NaN,因此不会影响其他代码的执行。 a. 任何涉及NaN的操作(如NaN/10)都会返回NaN;b. NaN与任何值都不相等,包括NaN本身。如 NaN==NaN --> false
isNaN( 任何类型 ) 如:
isNaN(NaN) --> true
isNaN(10) --> false
isNaN("10") --> false (可以被转换成数值10)
isNaN(true) --> false (可以被转换成数值1)
3个函数可把非数值 转换为 数值: Number()、parseInt()、parseFloat()。
Number()函数可用于任何数据类型,而另两个函数则专门用于把字符串转换成数值。这3个函数对于同样的输入会有返回不同的结果。
Number()函数的转换规则如下:
如果是Boolean值,true --> 1 / false ---> 0
如果是数字值,只是简单的传入和返回。
如果是null值,返回 0.
如果是undefined, 返回NaN
如果是 字符串,遵循下列规则:
如字符串中包含数字,则转换为 十进制数值。即"1"-->1, "123"-->123, "011" --> 11;
如字符串中包含 有效的浮点格式,如"1.1"--> 1.1 , "01.01" --> 1.01;
如字符串中包含 有效的十六进制格式,例如"0xf", 则将其转换为相同大小的十进制整数值;
如 "" , 则调用对象的valueOf()方法,然后依照前面的规则转换返回的值。如果转换的结果是NaN,则调用对象的toString()方法,然后再次依照前面的规则转换返回的字符串值。
e.g. Number("Hello word!") --> NaN
Number("") --> 0
Number("000011") --> 11
Number(true) --> 1
e.g. parseInt("1234blue") --> 1234
parseInt("") --> NaN
parseInt("0xA") --> 10 (十六进制数)
parseInt(22.5) --> 22
parseInt("070") --> 56 (八进制数)
parseInt("70") --> 70
parseInt("0xf") --> 15 (十六进制数)
注:ECMAScript 3 JavaScript引擎中,"070" 被当成八进制字面量,因此转换后的值是 十进制的56。而在ECMAScript 5 JavaScript引擎中,parseInt()会认为无效,从而将这个值当成"0",结果就得到十进制的0。所以用到了 parseInt()的第二个参数 parseInt("070",8), parseInt("0xAF",16) --> 175, parseInt("AF",16) --> 175, parseInt("AF") --> NaN
如果忽略parseInt的第二个参数,那么数字的基数将由下面的规则所决定:
◆ 默认基数为10,即按10进制解析
◆ 如果数字以0x开头,那么基数为16,即按16进制解析
◆ 如果数字以0开头,那么基数为8,即按8进制解析
◆ 默认基数为10,即按10进制解析
◆ 如果数字以0x开头,那么基数为16,即按16进制解析
◆ 如果数字以0开头,那么基数为8,即按8进制解析
parseFloat() 从第一个字符开始解析每个字符。而且也是一直解析到字符串末尾,或者解析到遇见一个无效的浮点数字字符为止。也就是说,字符串中的第一个小数点是有效的,而第二个小数点是无效的,它后面的字符串将被忽略。如: "22.34.5"--> 22.34
parseFloat()始终都会忽略前导的零。 注:十六进制格式的字符串则始终会被转换成0。由于它只解析十进制值,因此它没有用第二个参数指定基数的用法,如果字符串包含的是一个可解析为整数的数,parseFloat()会返回整数。
var num1 = parseFloat("1234blue"); //1234
var num2 = parseFloat("0xA"); //0
var num3 = parseFloat("22.34.5"); //22.34
var num4 = parseFloat("0908.5"); //908.5
var num5 = parseFloat("3.125e7"); //
31250000
* String类型用于表示 由0 或 多个16位 Unicode字符组成的字符序列,即字符串。
数值、布尔值、对象、字符串值都有toString()方法,但null 和 undefined 值没有此方法。一般调用toStrng()方法不必传递参数,但在调用数值的toString()方法时,可以传递一个参数:输出数值的基数。默认情况下,toString()方法以十进制格式返回数值的字符串表示。而通过传递基数,toString()可输出以二、八、十六进制,乃至其他任意有效进制格式表示的字符串值。如下:
var num = 10;
alert( num.toString() ); //"10"
alert( num.toString(2)); //"1010";
alert( num.toString(8)); //"12"
alert( num.toString(10)); //"10"
alert( num.toString(16)); //"a"
//在不知道要转换的值是不是null或undefined的情况下,还可以使用转型函数String(),此函数能够将任何类型的值转换为字符串。
var value3 = null;
var value4;
alert( String(value3) ); //"null"
alert( String(value4) ); //"undefined"
* Object类型 是一组数据和功能的集合。
var o = new Object; //有效,但不推荐省略圆括号
在ECMAScript中Object类型是所有他的实例的基础。换句话说,Object类型所具有的任何属性和方法也同样存在于更具体的对象中。
Object的每个实例都具有下列属性和方法:
◆ Constructor: 保存着用于创建当前对象的函数。对于前面的例子而言,构造函数(contructor)就是Object().
◆ hasOwnProperty(propertyName): 用于检查给定的属性在当前对象实例中(而不是在实例的原型中)是否存在。其中,作为参数的属性名(propertyName)必须以字符串形式指定。如:o.hasOwnProperty("name"). hasOwnProperty是用来判断一个对象是否有你给出名称的属性或对象。不过需要注意的是,此方法无法检查该对象的原型链中是否具有该属性,该属性必须是对象本身的一个成员。
e.g. 1
e.g. 1
function Test( name1, name2 ){
this.name1 = name1;
this.name2 = name2;
}
Test.prototype.showName1 = function(){ ... };
Test.prototype.showName2 = function(siteUrl){ this.siteUrl = siteUrl; };
var n1 = new Test("peng","tao");
var n2 = new Test("henry","pt");
n1.age = "30";
alert( n1.hasOwnProperty("name1")); //true
alert( n1.hasOwnProperty("age")); //true
alert( n1.hasOwnProperty("showName1")); //false
alert( n1.hasOwnProperty("siteUrl")); //false
alert( Test.prototype.hasOwnProperty("showName1"));//true
alert( Test.prototype.hasOwnProperty("siteUrl")); //false
alert( Test.prototype.isPrototypeOf(n1)); //true
alert( Test.prototype.isPrototypeOf(n2)); //true
e.g. 2
Object.prototype.bar = 1; //修改Object.prototype
var foo = {goo : undefined };
foo.bar; // 1
'bar' in foo; //true
foo.hasOwnProperty('bar'); //false
foo.hasOwnProperty('goo'); //true
◆ isPrototypeOf(object): 用于检查传入的对象是否是另一个对象的原型。
◆ propertyIsEnumerable( propertyName ): 用于检查给定的属性是否能够使用for-in语句来枚举。与hasOwnProperty()方法一样,作为参数的属性名必须以字符串形式指定。
◆ toLocaleString(): 返回对象的字符串表示,该字符串与执行环境的地区对应。
◆ toString(): 返回对象的字符串表示。
◆ valueOf(): 返回对象的字符串、数值、布尔值 表示。通常与toString()方法的返回值相同。
* 函数(Functions)
函数是JS中的一等对象,这意味着可把函数像其它值一样传递。一个常见的用法是把匿名函数作为回调函数传递对异步函数中。
foo(); //正常运行,因为foo在代码运行前已经被创建
funcion foo(){}
foo; //'undefined'
foo(); //出错:TypeError
var foo = function(){}; //赋值语句只在运行时执行,如果把这句放在 foo();之前就OK
var foo = function bar(){
bar(); //正常运行
};
bar(); //出错: ReferenceError
说明:bar函数声明外是不可见的,这是因为我们已经把函数赋值给了foo; 然而在bar内部依然可见。这是由于JS的命名处理所致,函数名在函数内部是可见的。
* this 的工作原理
在五种不同的情况下,this指向的各不相同:
◆ 全局范围内 --- this; //指向 全局对象。
◆ 函数调用 --- foo(); //指向 全局对象。
◆ 方法调用 --- test.foo();//this指向test对象。
◆ 调用构造函数---new foo();//如函数倾向于和new关键词一块使用,则我们称此函数是 构造函数。在函数内部this指向新创建的对象。
◆ 显式的设置this--- function foo(a,b,c){};
var bar = {};
foo.apply(bar ,[1,2,3]); //数组将会被扩展,如下所示
foo.call( bar, 1, 2, 3); //传递到foo的参数是: a = 1, b = 2, c = 3
当使用Function.prototype上的call 或 apply 方法时,函数内的this将会被
显式设置 为函数调用的第一个参数。
因此函数调用的规则在上例中已经不适用了,在foo函数内 this 被设置成了bar。
注意:在对象的字面声明语法中,this不能用来指向对象本身。因此var obj = {me: this}中的me不会指向obj,因为this只可能出现在上述的五种情况中。(这个例子中,如果是在浏览器中运行,obj.me等于window对象)
注:第二个规则被认为是JS语言另一个错误设计的地方,因为它从来没有实际的用途。如下例:
Foo.method = function(){
function test(){
// this 将会被设置为 全局对象 (浏览器环境中也就是window对象)
}
test();
}
一个常见的误解是test中的this将会指向Foo对象,实际上不是这样子的。
为了在 test 中获取对Foo对象的引用,我们需要在method函数内部创建一个局部变量指向 Foo对象。
Foo.method = function(){
var that = this;
function test(){
// 使用 that 来指向 Foo对象
}
test();
}
that只是我们随意起的名字,不过这个名字被广泛的用来指向外部的this对象。
另一个看起来奇怪的地方是函数别名,也就是将一个方法赋值给一个变量。
var test = someObject.methodTest;
test();
上例中,test就像一个普通的函数被调用;因此,函数内的this将不再被指向到someObject对象。
虽然this的晚绑定特性似乎并不友好,但是这确实 基于原形继承 赖以生存的土壤。
function Foo(){}
Foo.prototype.method = function(){};
function Bar(){}
Bar.prototype = Foo.prototype;
new Bar().method();
当method 被调用时,this 将会指向 Bar 的实例对象。
* 一元操作符
1、递增 和 递减 操作符
这种操作符不仅适用于整数,还可用于字符串、布尔值、浮点数值、对象。见如下规则:
◆ 在应用于一个包含有效数字字符的字符串时,先将其转换为数字值,再执行加减的操作。
字符器变量 变成 数值变量。
◆ 在应用于一个不包含有效数字字符的字符串时,将变量的值设置为NaN. 字符串变量变成数值变量.
◆ 在应用于布尔值false时,先将其转换为0再执行加减的操作。布尔值变量变成数值变量。
◆ 在应用于布尔值true时,先将其转换为1再执行加减的操作。布尔值变量变成数值变量。
◆ 在应用于浮点数值时,执行加减的操作。
◆ 在应用于对象时,先调用对象的valueOf()方法以取得一个可供操作的值。然后对该值应用前述规则。如果结果是NaN,则在调用toString()方法后再应用前述规则。对象变量变成数值变量。
e.g. var s1 = "2";
var s2 = "z";
var b = false;
var f = 1.1;
var o = {
valueOf : function(){ return -1; }
};
s1++; // 3
s2++; // NaN
b++; // 1
f--; // 0.
100000000009 (由于浮点舍入错误所致)
o--; // -2
2、一元加 和 减 操作符
var num = 25;
num = +25; //仍是25
对非数值应用时,该操作符会像Number()转型函数一样对这个值执行转换。即布尔值false和true->0和1, 字符串值会被按照一组特殊的规则进行解析,而对象是先调用它们的valueOf() 和(或)toString()方法,再转换得到的值。如下例:
var s1 = "01";
var s2 = "1.1";
var s3 = "z";
var b = false;
var f = 1.1;
var o = { valueOf : function(){ return -1; } };
s1 = +s1; //1
s2 = +s2; // 1.1
s3 = +s3; // NaN
b = +b; // 0
f = +f; // 1.1
o = +o; // -1
//当用于 一元减操作符时
s1 = -s1; //-1
s2 = -s2; //-1.1
s3 = -s3; //NaN
b = -b; //0
f = -f; // -1.1
o = -o; // 1
3、乘法操作符
规则:
◆ 如果乘积超过ECMAScript数值的表示范围,则返回Infinity 或 -Infinity;
◆ 如果有一个操作数是NaN,则结果是NaN;
◆ 如果是 Infinity 与 0 相乘,则结果是NaN;
◆ 如果是 Infinity 与 非0数值相乘, 则结果是 Infinity或 -Infinity,取决于有符号操作数的符号;
◆ 如果是 Infinity 与 Infinity 相乘,则结果是 Infinity;
◆ 如果有一个操作数不是数值,则在后台调用Number()转换,然后再应用上面的规则。
4、除法操作符
规则:
◆ 如果商超过了ECMAScript数值的表示范围,则返回 Infinity 或 -Infinity;
◆ 如果有一个操作数是NaN, 则结果是NaN;
◆ 如果是Infinity / Infinity, 则结果是NaN;
◆ 如果 是 0/0,则结果是 NaN;
◆ 如果 分子不为0,分母为0,则结果是Infinity;
◆ 如果是 非零的有限数被零除,则结果是 Infinity 或 -Infinity,取决于有符号操作数的符号;
◆ 如果是Infinity被任何非零数值除,则结果是Infinity或-Infinity,取决于有符号操作数的符号;
◆ 如果有一个操作数不是数值,则在后台调用Number()转换,然后再应用上面的规则。
5、求模(%)
◆ 如果被除数是无穷大,除数是有限大的数值,则结果是NaN;
◆ 如果被除数是有限大,除数是0,则结果是NaN;
◆ 如果是 Infinity / Infinity,则结果是NaN;
◆ 如果被除数是有限大的数值,除数是无穷大的数值,则结果是被除数;
◆ 如果被除数是0,则结果是0;
◆ 如果有一个操作数不是数值,则在后台调用Number()转换后再应用上面的规则。
6、加性质操作符
1> 加法操作符规则:
◆ 如果有一个是NaN,则结果是NaN;
◆ 如果是Infinity + Infinity = Infinity;
◆ 如果是 -Infinity + -Infinity = -Infinity;
◆ 如果是 Infinity + -Infinity = NaN;
◆ 如果是 +0 加上 +0 = +0;
◆ 如果是 -0 加上 -0 = -0;
◆ 如果是 +0 加上 -0 = +0;
◆ 如果两个操作数都是字符串,则将第二个操作数 与 第一个操作数 拼接起来;
◆ 如果只有一个操作数是字符串,则将另一个操作数转换为字符串,然后再将两个字符串拼接起来.
◆ 如果有一个操作数是 对象、数值、布尔值,则调用它们的toString()方法取得相应的字符串值,然后再应用上两条关于字符串的规则。对于 undefined 和 null,则分别调用String()函数并取得字符串"undefined" 和 "null".
e.g. var r = 5+"5"; //"55"
var k = "Result: " + 5 + 10; //Result: 510 "Result: "+5 --> "Result: 5" + 10
2> 减法操作符(-)
◆ 如果两个操作符都是数值,则执行常规的算术减法操作并返回结果;
◆ 如果有一个操作数是NaN,则结果是NaN;
◆ 如果是Infinity - Infinity = NaN;
◆ 如果是 -Infinity - -Infinity = NaN;
◆ 如果是 Infinity - -Infinity = Infinity;
◆ 如果是 -Infinity - Infinity = -Infinity;
◆ 如果是 +0 - +0 = +0;
◆ 如果是 +0 - -0 = -0;
◆ 如果是 -0 - -0 = +0;
◆ 如果有一个操作数是 字符串、布尔值、null、undefined,则先在后台调用Number()函数将其转换为数值,然后再根据前面的规则执行减法计算。如果转换的结果是NaN,则减法的结果就是NaN;
◆ 如果有一个操作数是 对象,则调用 对象的valueOf()方法以取得表示该对象的数值。如果得到的值是NaN,则减法的结果就是NaN。如果对象没有valueOf()方法,则调用其toString()方法并将得到的字符串转换为数值。
e.g. var r = 5 - true; // 4, true->1
var r = NaN - 1; // NaN
var r = 5 - ""; // 5, "" -> 0
var r = 5 - "2"; // 3, "2"->2
var r = 5 - null; // 5, null -> 0
7、关系操作符(>、<、>=、<=)
规则:
◆ 如果两个操作数都是数值,则执行数值比较;
◆ 如果两个操作数都是字符串,则比较两个字符串对应的字符编码值;
◆ 如果一个操作数是数值,则将另一个操作数转换为一个数值,然后执行数值比较;
◆ 如果一个操作数是对象,则调用这个对象的valueOf()方法,并用得到解决的结果根据前面的规则执行比较;
◆ 如果一个操作数是布尔值,则先将其转换为数值,然后再执行比较;
e.g. var r = "Brick" < "alphabet"; //true
var r = "Brick".toLowerCase() < "alphabet".toLowerCase(); //false
var r = "23" < "3"; //true "2"的编码是50, "3"的编码是51
var r = "23" < 3; //false "23"->23 < 3 -> false "23"会被转换成数值23
var r = "a" < 3; //false 因为"a"被转换成NaN
var r = NaN < 3; //false
var r = NaN >=3; //false
1> 相等 和 不相等
◆ 如果有一个是布尔值,则在比较前先转换为数值---false->0,true->1;
◆ 如果有一个是字符串,另一个是数值,在比较前先将字符串->数值;
◆ 如果有一个是对象,别一个不是,则调用对象的valueOf()方法,用得到的基本类型值按照前面的规则进行比较;
◆ null 和 undefined 是相等的;
◆ 要比较相等性之前,不能将null 和 undefined 转换成其他任何值;
◆ 如果有一个操作数是NaN, 则相等操作符返回false,而不相等操作符返回true。注:即使两个操作数都是NaN,相等操作符也返回false;因为按照规则,NaN不等于NaN。
◆ 如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回true;否则,返回false;
e.g. null == undefined //true
"NaN" == NaN //false
5 == NaN //false
NaN == NaN //false
NaN != NaN //true
false == 0 //true
true == 1 //true
true == 2 //false
undefined == 0 //false
null == 0 //false
"5" == 5 //true
null == undefined //true
null === undefined //false 因为它们是不同类型的值
2> 逗号操作符
用于声明多个变量,但除此之外,逗号操作符还可用于赋值。在用于赋值时,逗号操作符总会返回表达式中的最后一项,如下面的例子所示:
var num = (5, 1, 4, 8, 0); //num的值为0
由于0是表达式中的最后一项,因此num的值就是0。虽然逗号的这种方式并不常见,但此例可帮我们理解逗号的这种行为。
8、break 和 continue语句都可以与label语句联合使用,从而返回代码中特定的位置。这种联合使用的情况多发生在循环嵌套的情况下,如下面的例子所示:
var num = 0;
outermost:
for(var i=0; i<10; i++){
for(var j=0; j<10; j++){
if( i==5 && j==5 ){
break outermost; //continue outermost;
}
}
}
alert(num); //55
9、switch语句在比较值时使用的是全等操作符,因此不会发生类型转换(例如,字符串"10"不等于数值10).
10、函数function
function sum(num1, num2){
return num1 + num2;
alert("Hello world"); //永远不会执行
}
function sayHi(name, message){
return; //return语句也可不带有任何返回值,这时函数在停止执行后将返回undefined值。
}
推荐的做法是要么让函数始终都返回一个值,要么永远都不要返回值。否则,如果函数有时候返回值,有时候有不返回值,会给调试代码带来不便。
严格模式对函数有一些限制:
◆ 不能把函数命名为 eval 或 arguments;
◆ 不能把参数命名为 eval 或 arguments;
◆ 不能出现两个命名参数同名的情况。
如果发生以上情况,就会导致语法错误,代码无法执行。
1> 理解参数
ECMAScript中的函数参数在内部是用一个类似数组的arguments对象表示的。
e.g. function Args(){ alert( arguments.length ); }
Args("string",45); //2
Args(); //0
Args(12); //1
function add(num1, num2){
arguments[1] = 10;
alert( arguments[0] + ", " + num2 );//num1被改变了,用num1=10也同样改变
}
add(2,3); //2, 10
function add(num1, num2){
arguments[1] = 10;
alert( arguments[0] + ", " + num2 );//第二个参数没给,函数里怎么改都不行
}
add(2); //2, undefined
注:ECMAScript中的所有参数传递的都是值,不可能通过引用传递参数。
2、没有重载
function add(num){ return num+100; }
function add(num){ return num+200; }
add(100); //300 如果定义两个同名函数,后一个覆盖前一个
小结:
◆ ECMAScript中的基本数据类型包括Undefined、Null、Boolean、Number、String;
◆ Number类型可用于表示所有数值;
◆ Object类型是JS中所有对象的基础类型;
◆ 严格模式为这门语言中容易出错的地方施加了限制;
◆ 未指定返回值的函数返回的是一个特殊的undefined值;
◆ JS中也没有 函数签名的概念,因为其函数参数是以一个包含 零个/多个值的数组的形式传递的;
◆ 函数可传递任意数量的参数,并可通过arguments对象来访问这些参数;
◆ 由于不存在函数签名的特性,ECMAScript函数不能重载。
变量、作用域、内存问题
按照ECMA-262的定义,JS的变量与其他语言的变量有很大区别。JS变量松散类型的本质,决定了它只是在特定时间用于保存特定值的一个名字而已。由于不存在定义某个变量必须要保存何种数据类型值的规则,变量的值及其数据通信类型可以在脚本生命周期内改变。尽管从某种角度看,这可能是一个既有趣又强大,同时又容易出问题的特性,但JS变量实际的复杂程度还远不止如此。
1、基本类型 和 引用类型 的值
ECMAScript变量可能包含两种不同数据类型的值:基本类型 和 引用类型值。
在将一个值赋给变量时,解析器必须确定这个值是基本类型值 还是 引用类型值。
◆ 基本类型值 指的是简单的数据段(Undefined、Null、Boolean、Number、String)
◆ 引用类型值 指的是那些可能由多个值构成的对象。引用类型的值是保存在内存中的对象。与其他语言不同,JS不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。为此,引用类型的值是按引用访问的。
注: 在很多语言中字符串以对象的形式来表示,因此被认为是引用类型. ECMAScript放弃了这一传统.
2、动态属性
对于 引用类型的值,可为其添加属性和方法,也可改变和删除其属性和方法,如下:
var person = new Object();
person.name = "Nicholas"; //为对象person添加属性name
alert( person.name );
但不能给基本类型的值添加属性,尽管这么做不会导致任何错误,比如:
var name = "Nicholas";
name.age = 27;
alert(name.age);
3、复制变量值
从一个变量 向 另一个变量 复制基本类型 和 引用类型值时,也存在不同。
如从一个变量 向 另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上。如下例:
var num1 = 5;
var num2 = num1; //num2保存了值5;num2中的5与num1中的5是完全独立的,是num1中5的一个副本,互不影响。
当一个变量向另一个变量复制引用类型的值时,同样也会将存储在变量对象中的值复制一份放到 为新变量分配的空间中。不同的是,这个值的副本实际上是一个指针,而这个指针指向存储在堆中的一个对象。复制操作结束后,两个变量实际上将引用同一个对象。因此,改变其中一个变量,就会影响另一个变量,如下所示:
var obj1 = new Object();
var obj2 = obj1; //obj1 和 obj2都指向同一个对象。
obj1.name = "Nicholas";
alert( obj2.name ); //"Nicholas"
4、传递参数
ECMAScript中所有函数的参数都是按值传递的. 也就是说, 把函数外部的值复制给函数内部的参数,就和把值从一个变量 复制到 另一个变量一样.
e.g. function addTen(num){ num+=10; return num; }
var count = 20;
var result = addTen( count );
alert( count ); //20 没有变化
alert( result ); //30
//在向参数传递基本类型的值时,被传递的值会被复制给一个局部变量
function setName( obj ){ obj.name = "Nicholas"; }
var person = new Object();
setName( person );
alert( person.name ); //"Nicholas"
//在向参数传递引用类型的值时,会把这个值在内存中的地址复制给一个局部变量,因此这个局部变量的变化会反映在函数的外部.
以上代码中创建一个对象,并将其保存在了变量person中。然后,这个对象被传递到setName()函数中之后就被复制给了obj。在这个函数内部,obj和person引用的是同一个对象。换句话说,即使这个对象是按值传递的,obj敢会按引用来访问同一个对象。于是,当在函数内部为obj添加name属性后,函数外部的person也将有所反映;因为person指向的对象在堆内存中只有一个,而且是全局对象。
有很多人错误地认为:在局部作用域修改的对象会在全局作用域中反映出来,就说明参数是按引用传递的。为了证明对象是按值传递的,看下例:
function setName( obj ){
obj.name = "Nicholas";
obj = new Object();
obj.name = "Greg";
}
var person = new Object();
setName( person );
alert( person.name ); //"Nicholas"
如果person是按引用传递的,那么person就会自动被修改为指向其name属性值为"Greg"的新对象。但是,当接下来再访问person.name时,显示的值仍然是"Nicholas"。这说明即使在函数内部修改了参数的值,但原始的引用仍然保持不变。实际上,当在函数内部重写obj时,这个变量引用的就是一个局部对象了。而这个局部对象会在函数执行完毕后立即被销毁。