讀古今文學網 > 編寫高質量代碼:改善JavaScript程序的188個建議 > 建議160:關注各種引擎對ECMAScript v3的分歧 >

建議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