其實這已經很多人知道,Javascript大全中也解釋很清楚,但是不過這一關很難精通Javascript。
不多說廢話,馬上開始吧。
變數範圍(Scope)通常是接觸一個新的程式語言時最先要理解的東西,不過Javascript的變數範圍觀念跟我們習見的稍有不同,所以會有一些陷阱。
簡單地說,Javascript是靜態變數範圍(static scoping或lexical scoping)的程式語言,也就是說,變數範圍是依照程式定義時的上下文決定的。像下面這一段程式:
//這裡在 全域變數範圍(global)
var v1 = 1;
function a() {
//這裡在function a變數範圍,可以參考到全域
var v2 = 2;
function b() {
//這裡在function b變數範圍,可以參考到function a的變數範圍,也可以參考到全域
var v3 = 3;
alert(v1);
alert(v2);
alert(v3);
alert(v4);
alert(v5);
}
var v4 = 4;
alert(v1);
alert(v2);
// alert(v3);
alert(v4);
alert(v5);
b();
}
var v5 = 5;
a();
alert(v1);
//alert(v2);
//alert(v3);
//alert(v4);
alert(v5);
(註解掉的,就是會出問題的,也就是找不到的啦。)
更精確的定義,需要參考ECMA-262 Edition 3標準文件,最重要的觀念就是範圍鏈(scope chain)。
範圍鏈
ecma-262定義了三種可執行的程式碼,global, eval跟function,所有不在function中的程式碼就是global code,所有用eval()來執行的是eval code,函數中的程式碼就是function code。全域範圍的變數解析只限定在global物件;eval code的變數解析是與呼叫它的context相關;function code則比較複雜。
程式執行到function code時,會先建立一個範圍鏈,然後建立一個變數物件,把函數的參數加到這個變數物件,然後把函數中定義的變數加入到這個物件,最後把這個物件放到範圍鏈。如果函數外面還有函數,就依序把這些函數的變數物件放進範圍鏈,直到抵達global為止。
之後做變數解析時,就會到範圍鏈中找變數的定義與值。如果整個範圍鏈都找不到變數,那就產生一個變數,初始值是null。
因為範圍鏈是這樣定義的,所以就會有一個陷阱:
function a() {
b = 1;
}
像這樣就會在全域變數範圍中生成一個叫做b的變數,值是1。(沿著範圍鏈沒找到這個變數的定義,所以就在最後(也就是在global)生成一個變數,然後在function a()中把他的值指派為1。因為最後都會抵達全域,所以不論巢狀的函數定義有多深,這個變數最後會在全域中定義。所以在函數中使用變數,除非你要取外層的變數來用,不然別忘了用var來定義,不然你其實是產生的一個全域變數。
this
「this是什麼」也是在進入不同code時決定的,在全域時他就是global object,但是在函數中,他依照一個特別的規則:
如果直接呼叫一個函數,函數內的this指向global object。
function a() {
this.setTimeout(function(){alert('test');},100);
}
a();
如果透過new來建立一個物件,而函數定義是這個物件的建構子,那this會指向這個物件。
function a() {
this.v = 'test';
this.f = function(){
alert(this.v);
};
}
var b = new a();
b.f();
如果透過Object與.來執行這個函數,this會指向這個Object。
var a = {
v: 'test',
f: function() {alert(this.v);}
};
a.f();
call/apply
另外有一個特殊的方法可以讓你修改函數執行的context,就是使用函數物件的call()方法。
var a = {
v: 'test-a',
f: function(){alert(this.v);}
};
var b = {
v: 'test-b'
};
a.f.call(b);
apply的用法差不多,不同處在他第二個參數是要傳給呼叫函數的參數陣列,而call第一個參數後的參數都會當做參數傳給要呼叫的函數。
其實關於變數範圍,在Javascript大全裡面講得還蠻清楚的,google一下static scope/lexcial scope/scope chain等等關鍵字,也會找到很多資料,大家多上網找好文章吧。(回頭看了一下ECMA-262...如果真的照著講,恐怕還是很難懂)
像 v: 'test' 這樣的寫法, 叫什麼
在什麼時候會用到?
應該說是var a = {v: 'test'};
這叫做object literal或是object initialiser,上面這一段程式跑完,變數a就是一個Object物件的實例,有一個property名叫v,值是'test'。最常見的用法我想是傳遞configuration來初始化一個物件或功能。另外有一個常聽到的名稱,就是JSON。
例如:
http://jqueryui.com/demos/animate/
程式中會看到:
$("#button").toggle(
function() {
$("#effect").animate({backgroundColor: '#aa0000', color: '#fff', width: 500}, 1000);
},
function() {
$("#effect").animate({backgroundColor: '#fff', color: '#000', width: 240}, 1000);
}
);
傳給animate()的參數,就是動畫效果的設定。
補充一下語法:
{key:value}
當中,key只要不是關鍵字,就可以不加quote。value如果是字串,那一定要加quote,數字可以不用...其實就跟=右邊的規則一樣。
另外一種是array literal,長這樣:
var a = ['test',['sub','sub1']];
關於JSON,可以參考:
http://json.org/
另外要注意一點,JSON設計的用意是要拿來傳遞資料,所以不允許有Function物件當作value,但是這在object literal跟array literal中是允許的。
fillano 大
var a = {
v: 'test-a',
f: function(){alert(this.v);}
};
var b = {
v: 'test-b',
};
a.f.call(b);
(上面的 b 裡面的 v 多了一個 ,)
這一段程式是否也可以改成
var b=new a();
b.v='test-b';
b.f();
兩種寫法是否有應用上的差異?
嗯嗯,多了個逗點,在IE會出問題,謝謝你提醒。
要用new a()的話,a一定要是個function。所以有幾種做法:
第一種,就不要用new了...
var a = {};
a.v = 'test-b';
a.f = function(){alert(this.v);};
a.f();
第二種,非要用new不可的話...
var a=function(){
this.f=function(){alert(this.v);};
}
var b = new a();
b.v = 'test-b';
b.f();
第三種,function也可以移出去...
var a = function(){};
var b = new a();
b.v = 'test-b';
b.f = function(){alert(this.v);};
b.f();
Javascript很自由,不過這樣在instance動態加東西,或是在function外面加東西時沒用到prototype,動態加上去的就不會被繼承。但是藉由動態列舉然後copy property跟method給另一個物件其實很簡單,所以會用大量prototype inheritance的恐怕也不是很多。(prototype這個library我必較不熟啦,也許他們實作會用比較多正統的prototype繼承也說不定,但是我在jquery、yui3、ext其實都很少看到。)
感謝!獲益良多!^^b
費大我有用出來,是用你之前call back function
可是怎麼換行呢?
打兩列以上就會連在一起
function ObjectToXml()
{
var data=$('#dg').datagrid("getData");
var json=JSON.stringify(data);
var obj=new Object;
//get 4 question answer
if(data)
{
obj.firstname= data.rows.reduce(function(pre,cur)
{return pre+ cur.firstname},'');
obj.lastname=data.rows.reduce(function(pre,cur)
{return pre+ cur.lastname},'');
obj.phone=data.rows.reduce(function(pre,cur)
{return pre+ cur.phone},'');
obj.email=data.rows.reduce(function(pre,cur)
{return pre+ cur.email},'');
}
//json String
var j={"firstname":""+obj.firstname,"lastname":""+obj.lastname,"phone":""+obj.phone,"email":""+obj.email};
alert(JSON.stringify(j));
//TO XML
var xotree=new XML.ObjTree();
var xml;
var object=jQuery.parseJSON(JSON.stringify(j));
xml=xotree.writeXML(object);
alert(xml);
//document.getElementById("XML").value=xml;
}
"\n"就可以換行。
是加在這段嗎?
alert(JSON.stringify(j)+"\n")
沒有效果耶
加在這沒用啦,JSON.stringify()出來的東西沒格式的,純粹只是給程式用。