對於Javascript所有型別在Closure中表現的測試

 Mon, 15 Dec 2008 22:47:56 +0800

今天有一篇迴響講到我在「搞清楚lexical scope與closure」舉的例子有問題,我檢查了一下,的確有問題,不過在測試時發現好玩的現象,乾脆對所有型別做一些測試。

所作的測試包含使用Literal產生物件、使用Constructor產生物件、針對陣列元素、針對Date物件時間修改等做測試。程式如下:

var alterObj = function(v) {
v.a = "test altered.";
};
var alterArr = function(v) {
v[0] = "test altered";
}
var alterDat = function(v) {
v.setTime(v.getTime()+86400000);
}
var closureObj = function(v) {
return function() {
return v;
}
};
var testObjectLiteral = function(){
var str = {};
alert(typeof str);
str.a = "test02";
alert(str);
alert(str.a);
alterObj(str);
alert(str);
alert(str.a);
var f = closureObj(str);
str = null;
alert(f());
alert(f().a);
};
var testFunctionLiteral = function(){
var str = function(){};
alert(typeof str);
str.a = "test02";
alert(str);
alert(str.a);
alterObj(str);
alert(str);
alert(str.a);
var f = closureObj(str);
str = null;
alert(f());
alert(f().a);
};
var testArrayLiteral = function(){
var str = ["test01"];
alert(typeof str);
str.a = "test02";
alert(str);
alert(str.a);
alterObj(str);
alert(str);
alert(str.a);
var f = closureObj(str);
str = null;
alert(f());
alert(f().a);
};
var testArrayLiteralMember = function(){
var str = ["test3"];
alert(typeof str);
str[0] = "test02";
alert(str);
alert(str[0]);
alterArr(str);
alert(str);
alert(str[0]);
var f = closureObj(str);
str = null;
alert(f());
alert(f()[0]);
}
var testStringLiteral = function(){
var str = "test01";
alert(typeof str);
str.a = "test02";
alert(str);
alert(str.a);
alterObj(str);
alert(str);
alert(str.a);
var f = closureObj(str);
str = null;
alert(f());
alert(f().a);
};
var testNumberLiteral = function(){
var str = 3;
alert(typeof str);
str.a = "test02";
alert(str);
alert(str.a);
alterObj(str);
alert(str);
alert(str.a);
var f = closureObj(str);
str = null;
alert(f());
alert(f().a);
};
var testRegExpLiteral = function(){
var str = /[a-z]/gi;
alert(typeof str);
str.a = "test02";
alert(str);
alert(str.a);
alterObj(str);
alert(str);
alert(str.a);
var f = closureObj(str);
str = null;
alert(f());
alert(f().a);
};
var testBooleanLiteral = function(){
var str = false;
alert(typeof str);
str.a = "test02";
alert(str);
alert(str.a);
alterObj(str);
alert(str);
alert(str.a);
var f = closureObj(str);
str = null;
alert(f());
alert(f().a);
};
var testObjectObject = function(){
var str = new Object();
alert(typeof str);
str.a = "test02";
alert(str);
alert(str.a);
alterObj(str);
alert(str);
alert(str.a);
var f = closureObj(str);
str = null;
alert(f());
alert(f().a);
};
var testFunctionObject = function(){
var str = new Function("");
alert(typeof str);
str.a = "test02";
alert(str);
alert(str.a);
alterObj(str);
alert(str);
alert(str.a);
var f = closureObj(str);
str = null;
alert(f());
alert(f().a);
};
var testArrayObject = function(){
var str = new Array("test01");
alert(typeof str);
str.a = "test02";
alert(str);
alert(str.a);
alterObj(str);
alert(str);
alert(str.a);
var f = closureObj(str);
str = null;
alert(f());
alert(f().a);
};
var testArrayObjectMember = function(){
var str = new Array("test01");
alert(typeof str);
str[0] = "test02";
alert(str);
alert(str[0]);
alterArr(str);
alert(str);
alert(str[0]);
var f = closureObj(str);
str = null;
alert(f());
alert(f()[0]);
}
var testStringObject = function(){
var str = new String("test01");
alert(typeof str);
str.a = "test02";
alert(str);
alert(str.a);
alterObj(str);
alert(str);
alert(str.a);
var f = closureObj(str);
str = null;
alert(f());
alert(f().a);
};
var testBooleanObject = function(){
var str = new Boolean("test string object");
alert(typeof str);
str.a = "test02";
alert(str);
alert(str.a);
alterObj(str);
alert(str);
alert(str.a);
var f = closureObj(str);
str = null;
alert(f());
alert(f().a);
};
var testNumberObject = function(){
var str = new Number(3);
alert(typeof str);
str.a = "test02";
alert(str);
alert(str.a);
alterObj(str);
alert(str);
alert(str.a);
var f = closureObj(str);
str = null;
alert(f());
alert(f().a);
};
var testDateObject = function(){
var str = new Date();
alert(typeof str);
str.a = "test02";
alert(str);
alert(str.a);
alterObj(str);
alert(str);
alert(str.a);
var f = closureObj(str);
str = null;
alert(f());
alert(f().a);
};
var testDateObjectAlter = function(){
var str = new Date();
alert(typeof str);
str.a = "test02";
alert(str);
alert(str.a);
alterObj(str);
alterDat(str);
alert(str)
alert(str.a);
var f = closureObj(str);
str = null;
alert(f());
alert(f().a);
};
var testRegExpObject = function(){
var str = new RegExp();
alert(typeof str);
str.a = "test02";
alert(str);
alert(str.a);
alterObj(str);
alert(str);
alert(str.a);
var f = closureObj(str);
str = null;
alert(f());
alert(f().a);
};
//testObjectLiteral();
//testFunctionLiteral();
//testArrayLiteral();
//testArrayLiteralMember();
//testStringLiteral();
//testNumberLiteral();
//testRegExpLiteral();
//testBooleanLiteral();
//testObjectObject();
//testFunctionObject();
//testArrayObject();
//testArrayObjectMember();
//testStringObject();
//testBooleanObject();
//testNumberObject();
//testDateObject();
//testDateObjectAlter();
//testRegExpObject();
(註解掉是方便一次只執行一個function)

alterObj函數用來將傳入物件的a屬性做修改,alterArr函數用來將傳入陣列的元素做修改,alterDat函數則用來修改傳入Date物件的日期。closureObj函數則用傳入參數來產生一個closure,並且返回一個函數來將local變數攜出,並且可以展示傳入參數被「綁住」在裡面的狀況。前面三個函數可以展現,傳入的參數基本上都是一個物件的Reference。這個結論對於closureObj函數也是一樣的。

Date以及Array可以透過內建方法來改變自己,所以另外透過testDateObjectAlter函數以及testArrayObjectMember、testArrayLiteralMember這兩個函數來測試,其他的物件基本上都是透過額外給他一個a屬性來做測試,基本上都可以表現出closure的效果。

比較特別的是String、Number與Boolean Literal,產生的變數無法再assign一個額外的a屬性,但是透過String、Number與Boolean Constructor卻可以。

下面一個是測試結果的表(我是在Firefox做的測試,IE跑出來的東西可能會不一樣,因為toString()的實作可能不一致。另外測試Date顯示的日期會因為執行的日期/時間而改變(初始時會抓current time)。):

測試函數 typeof結果 呼叫改變str變數的函數 改變前的str 改變前的str.a 改變後的str 改變後的str.a Closure回傳的str closure回傳的str.a
testObjectLiteral(); object alterObj() [object Object] test02 [object Object] test altered [object Object] test altered
testFunctionLiteral(); function alterObj() function(){} test02 function(){} test altered function(){} test altered
testArrayLiteral(); object alterObj() test01 test02 test01 test altered test01 test altered
testArrayLiteralMember(); object alterArr() test02 test02 test altered test altered test altered test altered
testStringLiteral(); string alterObj() test01 undefined test01 undefined test01 undefined
testNumberLiteral(); number alterObj() 3 undefined 3 undefined 3 undefined
testRegExpLiteral(); object alterObj() /[a-z]/gi test02 /[a-z]/gi test altered /[a-z]/gi test altered
testBooleanLiteral(); boolean alterObj() false undefined false undefined false undefined
testObjectObject(); object alterObj() [object Object] test02 [object Object] test altered [object Object] test altered
testFunctionObject(); function alterObj() function anonymous(){} test02 function anonymous(){} test altered function anonymous(){} test altered
testArrayObject(); object alterObj() test01 test02 test01 test altered test01 test altered
testArrayObjectMember(); object alterArr() test02 test02 test altered test altered test altered test altered
testStringObject(); object alterObj() test01 test02 test01 test altered test01 test altered
testBooleanObject(); object alterObj() true test02 true test altered true test altered
testNumberObject(); object alterObj() 3 test02 3 test altered 3 test altered
testDateObject(); object alterObj() Tue Dec 16 2008 00:00:24:04 GMT +800 test02 Tue Dec 16 2008 00:00:24:04 GMT +800 test altered Tue Dec 16 2008 00:00:24:04 GMT +800 test altered
testDateObjectAlter(); object alterDat() Tue Dec 16 2008 00:00:24:04 GMT +800 test02 Tue Dec 17 2008 00:00:24:04 GMT +800 test altered Tue Dec 17 2008 00:00:24:04 GMT +800 test altered
testRegExpObject(); object alterObj() /(?:)/ test02 /(?:)/ test altered /(?:)/ test altered

最後的一點小小感想:我前面文章的補充還是不太對,closure對所有型別都有效果。另外要說......ECMA-262 Edition 3的設計真的不太一致......