對於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();
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的設計真的不太一致......