首页 » 编写高质量代码:改善JavaScript程序的188个建议 » 编写高质量代码:改善JavaScript程序的188个建议全文在线阅读

《编写高质量代码:改善JavaScript程序的188个建议》建议160:关注各种引擎对ECMAScript v3的分歧

关灯直达底部

在下面描述中,IE表示Internet Explorer,FF表示Mozilla Firefox浏览器。

1.空白符

IE不支持v字符为空白符,会把它解析为字母v。

示例:


alert(/'v supported/'+(String.fromCharCode(11)==/'v/'));


输出:


IE:false FF:true

Opera:true Safari:true


2.保留字

ECMA Script v3定义了25个关键字:break、else、new、var、case、finally、return、void、catch、for、switch、while、continue、function、this、with、default、if、throw、delete、in、try、do、instanceof、typeof。

同时还预留了31个保留字用于未来版本的功能扩展:abstract、enum、int、short、boolean、export、interface、static、byte、extends、long、super、char、final、native、synchronized、class、float、package、throws、const、goto、private、transient、debugger、implements、protected、volatile、double、import、public。

所有保留字可以作为标识符在代码中使用,而IE仅允许下面23个保留字作为标识符使用:abstract、int、short、boolean、interface、static、byte、long、char、final、native、synchronized、float、package、throws、goto、private、transient、implements、protected、volatile、double、public。

3.字面量

IE借用C语言风格的转义字符,设置文字的换行符,但根据ECMAScript v3标准,这种行为将引发一个未结束的字符串常量的语法错误。

示例:


var s=/"this is a

multiline string/";


输出:


IE、FF、Opera、Safari:/"this is a multiline string/"


IE会把上面字符串视为单行字符串,FF、Opera、Safari与IE解析一致。

IE会忽略“”及其后面的字符。s.length将返回34(这包括在第二行中的前导空格)。

4.Arguments对象

不同引擎没有针对函数的arguments变量名实现统一的处理方式。在IE中,arguments并没有包含在变量所在的上下文环境中,调用eval动态执行代码将无法改变arguments的值。

示例:


function foo{

document.write(arguments);

document.write(arguments[0]);

eval(/"arguments=10;/");

document.write(arguments);

document.write(arguments[0]);

}

foo(/"test/");


输出:


IE:[object Object]test[object Object]test

FF:[object Object]test10undefined

Opera:testtest10undefined

Safari:[object Arguments]test10undefined


针对上面示例,如果不使用eval动态执行字符串,而是直接修改arguments变量的值:


function foo{

document.write(arguments);

document.write(arguments[0]);

arguments=10;

document.write(arguments);

document.write(arguments[0]);

}

foo(42);


则不同引擎的输出结果如下:


IE:[object Object]4210undefined

FF:[object Object]4210undefined

Opera:424210undefined

Safari:[object Arguments]4210undefined


5.Global对象

在IE中,全局对象(Global)不能使用this进行迭代。

示例:


var__global__=this;

function invisibleToIE{

document.write(/"IE can/'t see me/");

}

__global__.visibleToIE=function{

document.write(/"IE sees me/");

}

for(func in__global__){

var f=__global__[func];

if(func.match(/visible/)){

f;

}

}


输出:


IE:IE sees me

FF:IE sees meIE can/'t see me

Opera:IE can/'t see meIE sees me

Safari:IE can/'t see meIE sees me


在IE中,在使用delete运算符通过this指针删除全局成员时,将会产生运行时错误,而在FF等浏览器中删除返回false。根据标准,FF的行为是正确的,例如:


var__global__=this;

function invisibleToIE{

document.write(/"IE can’t see me/");

}

__global__.visibleToIE=function{

document.write(/"IE sees me/");

}

document.write(delete this.invisibleToIE);


输出:


IE:runtime error(object doesn’t support this action)

FF、Opera、Safari:false


在IE中全局对象(Global)不继承Object.prototype,即使它的类型是对象。根据JavaScript原型继承规则,全局对象应该继承Object.prototype,当然这些都必须依赖于实现的浏览器。作为内置的原型,必须遵循Object.prototype的标准方法。


var__global__=this;

document.write(typeof(__global__)+/'<br>/');

var f=[/'toString/',/'toLocaleString/',/'valueOf/',/'hasOwnProperty/',/'isPrototypeOf/',/'propertyIsEnumerable/'];

for(i=0;i<f.length;i++){

test(f[i]);

}

function test(s){

if(__global__[s]){

document.write(s+/'supported/'+/'<br>/');

}

}


输出:


IE:

object

toString supported

FF、Opera、Safari:

object

toString supported

toLocaleString supported

valueOf supported

hasOwnProperty supported

isPrototypeOf supported

propertyIsEnumerable supported


6.初始化数组

在IE中,向数组尾部添加逗号分隔符将会增加数组的长度。IE把尾部逗号后的空白视为一个值为undefined的元素。实际上,这是一个非法解析的错误。

示例:


document.write([1,2,3,].length);


输出:


IE:4

FF、Opera、Safari:3


7.函数表达式

在IE中,函数表达式中的标识符在闭包的上下文环境中是可见的,因为这种表达被视为函数声明。

示例:


var foo=function bar(b){

if(b==true){

bar;//可以工作,因为在函数内部bar标识符是可见的

}else{

document.write(/"hello/");

}

}

foo;//可以工作,因为foo标识符指向一个函数对象

bar(false);//失败,因为在全局环境中bar是不可见的


输出:


IE:/"hellohello/"

FF:/"hello/"接着显示一个语法错误(bar is not defined)

Opera:/"hello/"接着显示一个引用错误(Reference to undefined variable:bar)

Safari:/"hello/"


IE把嵌套在函数表达式中的一个函数名称作为一个函数声明,这个函数声明位于封闭的上下文环境中。在下面的示例中,IE可以向前引用x,而在其他浏览器中将显示语法错误。


function f(x){

x;

y=function x{

document.write(/"inner called/")

};

document.write(x);

document.write(arguments[0]);

}

document.write(/"test 4/");

f(/"param/");


输出:


IE:test 4 inner called function x{document.write(/"inner called/")}function x{document.write(/"inner called/")}

FF、Opera、Safari:test 4


再如:


function foo{

function bar{}

var x=function baz(z){

document.write(/"baz/"+z);

if(z)baz(false);

};

x(true);//合法的

bar;//合法的

baz(true);//原来是非法的,现在是合法的

}

foo;


输出:


IE:baztruebazfalsebaztruebazfalse FF:baztruebazfalse(followed by an error-baz not defined)Opera:same as FF

FF、Opera、Safari:baztruebazfalse(followed by an error-baz not defined)


8.抽象关系比较算法

IE使用or而不是and进行字符串比较计算。

示例:


document.write(/'1<10==/'+(1<10)+/'<br>/');

document.write(/'NaN<1==/'+(NaN<1)+/'<br>/');

document.write(/'1<Infinity==/'+(1<Infinity)+/'<br>/');

document.write(/'/"10/"<1==/'+(/"10/"<1)+/'<br>/');

document.write(/'1</"a/"==/'+(1</"a/")+/'<br>/');

document.write(/'/"a/"</"b/"==/'+(/"a/"</"b/")+/'<br>/');


输出:


IE、FF、Opera、Safari:

1<10==true

NaN<1==false

1<Infinity==true

/"10/"<1==false

1</"a/"==false

/"a/"</"b/"==true


9.函数体内的函数声明

当使用函数声明定义一个函数时,不管怎样使用with语句修改函数的作用域,IE都会把它绑定到全局作用域上。

示例:


var v=/'value 1/';

var o={v:/'value 2/'};

function f1{

alert(/'v==/'+v);

};

with(o){

function f2{

alert(/'v==/'+v);

};

}

f1;

f2;

//修改变量的值

v=/'modified value 1/';

o.v=/'modified value 2/';

f1;

f2;


输出:


IE、Opera:

v==value 1

v==value 1

v==modified value 1

v==modified value 1

FF、Safari:

v==value 1

v==value 2

v==modified value 1

v==modified value 2


针对上面示例,如果使用函数表达式定义f1和f2函数(代码如下),那么输出结果与FF相同,说明在IE、Opera浏览器中with作用域会影响函数表达式,但不会影响函数声明。


var f1=function{

alert(/'v==/'+v);

};

with(o){

var f2=function{

alert(/'v==/'+v);

};

}


10.枚举和属性

IE不支持通过for in语句枚举类型的自定义属性,这些自定义属性通过Object.prototype进行映射进而实现继承。

示例:


function cowboy{

this.toString=function{

return/"cowboy/";

}

this.shoot=function{

return/"bang!/";

}

}

var p=new cowboy;

document.write(/"Enumerable properties:/");

for(var i in p){

document.write(/"/",i);

}

document.write(/"<br/>cowboy propertyIsEnumerable(/"toString/"):/",p.propertyIsEn-umerable(/"toString/"));

document.write(/"<br/>cowboy hasOwnProperty(/"toString/"):/",p.hasOwnProperty(/"toString/"));


输出:


IE:

Enumerable properties:shoot

cowboy propertyIsEnumerable(/"toString/"):false

cowboy hasOwnProperty(/"toString/"):true

FF、Opera、Safari:

Enumerable properties:toString shoot

cowboy propertyIsEnumerable(/"toString/"):true

cowboy hasOwnProperty(/"toString/"):true


11.try语句

在IE中,用于保存捕获异常的变量在当前上下文环境是可见的,在catch子句执行完毕后此变量依然存在,但在该上下文环境被注销后会随之消失。

示例:


function foo{

try{

throw/"hello/";

}catch(x){

document.write(x);

}

document.write(x);//x在这里应该是不可见的

}

foo;


输出:


IE:hellohello

FF、Opera、Safari:

hello(然后抛出一个错误,x is not defined)


try语句包含自己的作用域,当抛出异常时,这个作用域是封闭的,但在IE和FF浏览器中可以看到一些特殊的情况。示例如下:


function foo{

this.x=11;

}

x=/"global.x/";

try{

throw foo;

}catch(e){

document.write(x)//应该输出/"global.x/"

e;

document.write(x)//应该把x添加到e对象上,但IE和FF却修改了全局变量x

}

document.write(x);//应该输出/"global.x/"


输出:


IE、FF:global.x1111

Opera、Safari:global.x11global.x


12.jion数组原型

当分隔符为undefined时,IE会使用“undefined”字符串作为分隔符来连接数组成员值。

示例:


var array=[1,2];

alert(array.join);

alert(array.join(undefined));

alert(array.join(/'-/'));


输出:


IE:

1,2

1undefined2

1-2

FF、Opera、Safari:

1,2

1,2

1-2


13.unshift数组原型

Array.unshift方法能够把它的参数添加到数组的起始位置,同时返回结果数组的长度,但IE在调用Array.unshift方法时的返回值为undefined。

示例:


var a=new Array(1,2,3);

var l=a.unshift;

document.write(l,/"/");

document.write(a.length,/"/");

document.write(a,/"/");

l=a.unshift(2);

document.write(l,/"/");

document.write(a.length,/"/");

document.write(a,/"/");


输出:


IE:undefined 3 1,2,3 undefined 4 2,1,2,3

FF、Opera、Safari:

3 3 1,2,3 4 4 2,1,2,3


14.函数length属性

Object.length、String.fromCharCode.length、String.Prototype.indexOf.length、String.Prototype.lastIndexOf.length、String.prototype.slice.length等的返回值与标准值存在差异。

❑Object.length:IE返回值为0,标准解析为1。

❑String.fromCharCode.length:IE返回值为0,标准解析为1。

❑String.prototype.indexOf.length:IE返回值为2,标准解析为1。

❑String.prototype.lastIndexOf.length:IE返回值为2,标准解析为1。

❑String.prototype.slice.length:IE和FF返回值为0,标准解析为2。

15.split字符串原型

IE可以忽略捕获括号,FF能够使用空字符代替undefined。

示例:


alert(/"A<B>bold</B>and<CODE>coded</CODE>/".split(/<(/)?([^<>]+)>/));


输出:


IE:A,bold,and,coded

FF、Opera、Safari:

A,,B,bold,/,B,and,,CODE,coded,/,CODE,


16.toPrecision数值原型

如果参数的精度不确定,则IE将抛出RangeError异常。

示例:


var number=123.456;

document.write(/'number.toString==/'+number.toString+/'<br>/');

document.write(/'number==/'+number+/'<br>/');

try{

document.write(/'number.toPrecision(undefined)==/'+number.toPrecision(undefined)+/'<br>/');

}catch(e){

document.write(/'Exception thrown./'+e.name+/':/'+e.message+/'<br>/');

}


输出:


IE:

number.toString==123.456

number==123.456

Exception thrown.RangeError:The precision is out of range

FF、Opera、Safari:

number.toString==123.456

number==123.456

number.toPrecision(undefined)==123.456


17.valueOf日期原型

直接调用日期原型的valueOf方法,IE将返回0,而标准规定为NaN。

示例:


document.write(/'Date.prototype.valueOf==/'+Date.prototype.valueOf);


输出:


IE:0

FF、Opera、Safari:NaN


18.Disjunction

IE将使用空字符代替undefined值。

示例:


//重写Array.prototype.toString,使字符串包含在引号中,同时显示undefined值

Array.prototype.toString=function{

var s=/'/';

for(var i=0;i<this.length;i++){

if(s){

s+=/',/';

}

switch(typeof this[i]){

case/'string/':

s+=/"/'/"+this[i]+/"/'/";

break;

case/'undefined/':

s+=/'undefined/';

break;

default:

s+=this[i];

break;

}

}

return/'[/'+s+/']/';

}

var a=/((a)|(ab))((c)|(bc))/.exec(/'abc/');

document.write(a);


输出:


IE:[/'abc/',/'a/',/'a/',/'/',/'bc/',/'/',/'bc/']

FF、Opera、Safari:NaN

[/'abc/',/'a/',/'a/',undefined,/'bc/',undefined,/'bc/']


19.数列

IE不清除子表达式中重复匹配的选项。

示例:


Array.prototype.toString=function{

//执行代码

}

var a1=/(z)((a+)?(b+)?(c))*/.exec(/'zaacbbbcac/');

var a2=/(a*)*/.exec(/'b/');

document.write(a1);

document.write(/'<br>/');

document.write(a2);


输出:


IE:

[/'zaacbbbcac/',/'z/',/'ac/',/'a/',/'bbb/',/'c/']

[/'/',/'/']

FF、Opera、Safari:

[/'zaacbbbcac/',/'z/',/'ac/',/'a/',undefined,/'c/']

[/'/',undefined]


20.正则表达式实现

如果正则表达式的标志中包含任何字符,那么超出了“g”、“i”、“m”,或者这几个字符重复都将抛出SyntaxError异常。但IE不会抛出SyntaxError或TypeError异常,它会抛出一个TypeError异常,由此可知,IE对正则表达式字符串的要求是宽松的,在标志重复的情况下,不抛出任何异常。

示例:


function test(p,f){

try{

var r=new RegExp(p,f);

document.write(r.toString+/'<br>/');

}catch(e){

document.write(e.name+/':/'+e.message+/'<br>/');

}

}test(new RegExp(/'foo/'));//ok

test(new RegExp(/'foo/'),undefined);//ok

test(new RegExp(/'foo/'),/'gim/');//TypeError

test(/'foo/');//ok

test(/'foo/',undefined);//ok

test(undefined,/'gim/');//ok

test(/'foo/',/'gimgim/');//SyntaxError

test(/'foo/',/'pvl/');//SyntaxError


21.getYear日期原型

对于IE来说,Date.prototype.getYear类似于Date.prototype.getFullYear。

示例:


var d=new Date(/"10 July 2001/");

var y=d.getYear;

document.write(y+/'<br>/');


输出:


IE:2001

FF、Opera、Safari:101


22.setYear日期原型

对于IE来说,Date.prototype.setYear类似于Date.prototype.setFullYear。

示例:


var d=new Date(+0);

d.setYear(95);

y=d.getYear;

document.write(/"setYear:/"+y+/"/");

d.setFullYear(95);

y=d.getYear;

document.write(/"setFullYear:/"+y);


输出:


IE:setYear:95 setFullYear:95

FF、Opera、Safari:setYear:95 setFullYear:-1805