Thinking in Technique
tangrui.cn: en
剛剛?cè)滩蛔∈职W,下載了一個(gè)Firefox 2.0 Beta 1(可能過幾天這個(gè)鏈接就換了)。寫了一段JS,看來果然在JS1.7當(dāng)中,Iterator是非常好用的。
代碼如下
================================
<html>
<head>
<script language=javascript>
// add function for JavaScript Object
Object.prototype.newfunc = function(){};
function init() {
var a = [1,2,3,4];
var it;
var s;
// old method in JavaScript 1.5
s = “”;
for (it in a) {
s += it + ” ==> ” + a[it] + “<BR>”;
}
document.getElementById(”old_in”).innerHTML = s;
// new one in JavaScript 1.7
s = “”;
try {
it = Iterator(a, true);
while (true) {
var key = it.next();
s += key + ” ==> ” + a[key] + “<BR>”;
}
} catch (err if err == StopIteration) {
//alert(”End of record.\n”);
} catch (err) {
//alert(”Unknown error: ” + err.description + “\n”);
}
document.getElementById(”new_in”).innerHTML = s;
}
</script>
</head>
<body onload=”init()”>
<div id=old_in></div>
<hr>
<div id=new_in></div>
<hr>
</body>
</html>
================================
運(yùn)行結(jié)果如下:
0 ==> 1
1 ==> 2
2 ==> 3
3 ==> 4
newfunc ==> function () { }
可見,使用Iterator是可以避免在for之中錯(cuò)誤的使用一些未知的新添加的方法的。
有一點(diǎn)遺憾的就是,Iterator的結(jié)束是很丑陋的,不知道有沒有什么好的方法。還需要繼續(xù)研究。
Firefox 2.0 beta 1推出了,肯定很多人已經(jīng)關(guān)注這個(gè)事情了,我則比較(更加)關(guān)心的是JavaScript的增強(qiáng)。JavaScript升級(jí)到1.7了(FF1.5支持JavaScript1.5,IE6sp1支持不完全的JavaScript1.5)
在JavaScript 1.7里面有了一些新功能的支持,比如Generator和Iterator,let語法的支持,非結(jié)構(gòu)化賦值(destructuring assignment)。
令我最關(guān)心和期待的就是Iterator的支持,據(jù)說只要自定義next方法和__iterator__屬性就可以根據(jù)自己的需要定義返回的值了。Iterator的出現(xiàn)最大的好處就是可以放心的在一些內(nèi)嵌的對(duì)象當(dāng)中添加方法了。
以前這樣的代碼是很危險(xiǎn)的:
var o = new Object();
for (var i in o){}
因?yàn)楹芸赡苣硞€(gè)第三方的庫(kù)文件在Object當(dāng)中添加了一些方法,(比如著名的prototype),這樣,在不同的第三方庫(kù)混用的時(shí)候,就很可能出現(xiàn)問題。
現(xiàn)在有了自定義的Iterator,那么我覺得是完全可以避免的,只要為Object寫好合適的iterator就可以了。具體的代碼我還沒有嘗試。
還有一個(gè)問題,就是不知道IE7支持JavaScript到什么版本,而且,IE6和Firefox1.5.*都還是JavaScript 1.5 并且很多人是不會(huì)升級(jí)的。
所以,JavaScript1.7也許在未來的一段時(shí)間內(nèi),還是一個(gè)美好的(但是不能實(shí)現(xiàn)的)愿景吧。
如我前面的文章說的,對(duì)于JavaScript,一個(gè)類,就是一個(gè)function,他的類方法(也就是static的)都是作為這個(gè)function的一部分,而實(shí)例方法,都是在prototype上面的。
function ClassA() {
}
ClassA.staticMethod = function () {
}
ClassA.prototype.instanceMethod = function () {
}
在我這個(gè)實(shí)現(xiàn)當(dāng)中,一個(gè)類的繼承是拷貝父類的所有類方法,這樣子類就有了父類的靜態(tài)方法。
然后讓子類的prototype.prototype指向父類的prototype.
然后可以根據(jù)自己的需要,重寫一些方法。
function ClassB() {
}
ClassB.staticMethod = ClassA.staticMethod;
ClassB.prototype.prototype = ClassA.prototype;
ClassB.prototype.instanceMethod = function () {
// method 2
}
對(duì)于子類,使用一個(gè)prototype的鏈,來實(shí)現(xiàn)方法的實(shí)例方法的繼承。之所以選擇這種實(shí)現(xiàn)方法,是因?yàn)樽宇愂且貙懫渲械哪承┓椒ǖ?。而prototype又是一個(gè)reference,所以直接的寫作ClassB.prototype = ClassA.prototype,會(huì)在重寫ClassB的實(shí)例方法的同時(shí),破壞ClassA的實(shí)例方法。而修改后的方法則會(huì)屏蔽父類的。
尋找方法的順序是,instanceA.prototype.method -> ClassA.prototype.
此時(shí)對(duì)于類方法的繼承,已經(jīng)實(shí)現(xiàn),現(xiàn)在需要實(shí)現(xiàn)在子類中,調(diào)用父類的方法。
對(duì)于Java,這樣的使用是很平常的
public void method() {
super.method();
}
在JavsScript中,為了實(shí)現(xiàn)此類功能,所以必須保留一個(gè)parent的reference,指向ParentClass.prototype.
ClassB.prototype.parent = ClassA.prototype.
那么在instanceB里面調(diào)用this.parent.method.call(this);就可以使用父類的方法了。使用call調(diào)用,是為了把自己的數(shù)據(jù)傳到父類。更漂亮的解決方法,我還沒有想到。
所以完成的代碼是
function ClassA() {
}
ClassA.prototype.method1 = function () {
}
ClassA.staticMethod = function () {
}
function ClassB(){
}
ClassB.staticMethod = ClassA.staticMethod;
ClassB.prototype.prototype = ClassB.prototype.parent = ClassA.prototype;
這個(gè)我抽象出來一個(gè)extend方法,
var LCore = function () {
}
LCore.extend = function (destination, source) {
// copy all functons
for (var prop in source) {
if (prop == “prototype”) {
continue;
}
destination.prototype[prop] = source[prop];
}
// make a reference for parent and reference prototype
destination.prototype.prototype = destination.prototype.parent = source.prototype;
return destination;
}
停了一段時(shí)間,今天說說JavaScript的面向?qū)ο?br>js不是一個(gè)天生的oo的語言。但是他的函數(shù)是可以擔(dān)負(fù)面向?qū)ο蟮母拍畹?。并且js也內(nèi)置了很多的對(duì)象,比如String,Date等等。
js有一個(gè)關(guān)鍵字new,可以認(rèn)為是實(shí)例化一個(gè)function的對(duì)象,把function作為一個(gè)class。
比如
function MyObj () {
}
var myObj = new MyObj();
這個(gè)時(shí)候,myObj就是MyObj的一個(gè)實(shí)例。
有兩點(diǎn)應(yīng)該具體的說一下
1,JavaScript是一個(gè)prototype的語言
2,this在JavaScript里面的應(yīng)用
首先我們說一下prototype,prototype是一個(gè)function的屬性,也可以認(rèn)為是一個(gè)類的屬性。他用來記錄這個(gè)類有那些方法。
比如我們要給MyObj定義一個(gè)sayHello的方法,我們可以這樣寫
MyObj.prototype.sayHello = function () {
alert(”Hello”);
}
這個(gè)時(shí)候,我們就可以調(diào)用myObj.sayHello()執(zhí)行這個(gè)方法。
而this則表示這個(gè)類本身(嚴(yán)格意義上說,應(yīng)該是這個(gè)函數(shù)本身),比如
MyObj.prototype.sayBye = function () {
alert(”bye” + this.name);
}
那么這里的this,就是實(shí)例變量本身。myObj.sayBye()。函數(shù)里面的this就是表示myObj。那么我們就可以實(shí)現(xiàn)帶參數(shù)的構(gòu)造函數(shù)。
function MyObj2(name) {
this.name = name;
}
var myO2 = new MyObj2(”tom”);
這樣,我們就可以實(shí)現(xiàn)一般情況下使用的面向?qū)ο罅恕?/p>
需 要主意的是,JavaScript沒有屬性的共有私有機(jī)制,我們可以通過myO2.name訪問他的name屬性。而且js也不是強(qiáng)行規(guī)定的,我們要訪問 myO2.age也是可以的,但是myO2并沒有age的屬性(或者說重來沒有給age賦值)那么得到的就是undefined。
通過這個(gè),我們就可以用Object來實(shí)現(xiàn)平常使用的Map(java語言,對(duì)于python應(yīng)該是字典類型)。
var m = new Object();
m[”name”] = “tom”;
m[”age”] = 20;
alert(m[”name”]);
注1:對(duì)于alert,JavaScript調(diào)用的是這個(gè)object的toString函數(shù),如果沒有,則會(huì)顯示 ie:[object] ff:[object, Object]
注2:在js當(dāng)中,new Object和{}是一樣的,我們可以寫 var m = {};
這也是js中簡(jiǎn)單的map的實(shí)現(xiàn)。不好的地方是遍歷不方便,因?yàn)闆]有地方記錄有那些key
for (var i in m) 這種遍歷方法,會(huì)把所有的對(duì)于object進(jìn)行擴(kuò)展的方法都顯示出來。
對(duì)Object的擴(kuò)展,是通過對(duì)Object的prototype添加方法實(shí)現(xiàn)的。
比如js的String方法是沒有trim的,我們可以自己做一個(gè)實(shí)現(xiàn)然后通過擴(kuò)展String來提供這個(gè)方法
String.prototype.trim = function () {}
var a = ” 123 “.trim();
上面提到了一下this,之所以說一下this,是因?yàn)樗⒉煌耆韧贑++或者Java里面的this變量。
this在js中表示緊貼著調(diào)用地點(diǎn)的,非prototype擴(kuò)展的方法。
比如上面提到的
MyObj.prototype.sayBye = function () {
alert(”Bye” + this.name);
}
這個(gè)里面的this,緊貼的非prototype的函數(shù)是MyObj(再次注意,js中class是通過函數(shù)實(shí)現(xiàn)的),所以this.name就是實(shí)例變量。
但是在這種情況
MyObj.prototype.doSomething = function () {
todo(function () {
alert(this.name);
});
}
這個(gè)時(shí)候,this表示的是這個(gè)匿名函數(shù)
function () {
alert(this.name)
}
那么這里就會(huì)出現(xiàn)錯(cuò)誤,如果想要這樣使用,應(yīng)該使用輔助變量。
MyObj.prototype.doSomething = function () {
var me = this; //把自己的reference賦值給變量me
todo (function () {
alert(me.name); //通過me來訪問myObj實(shí)例
});
}
而且所有的實(shí)例變量必須通過this來進(jìn)行訪問,直接寫變量名稱會(huì)認(rèn)為是全局的變量。
上例中的alert(this.name)是正確的,而alert(name)是錯(cuò)誤的。
還有最后說一下靜態(tài)方法。我們可以用js來模擬靜態(tài)方法(或者說是類方法)
就是直接把方法寫在函數(shù)上
MyObj.staticMethod = function () {
}
這種方法都是實(shí)例無關(guān)的,調(diào)用的時(shí)候,通過MyObj.staticMethod()來調(diào)用。其實(shí)這也是一個(gè)變通的方法。
下一次我再說說類的繼承。
大家有什么想要聽的,告訴我,如果我會(huì),那么我一定分享出來。
今天說一下函數(shù)參數(shù)。
函數(shù)參數(shù),可以理解為函數(shù)的參數(shù),還有就是函數(shù)作為參數(shù)(還是挺詭意)。
首先說一下函數(shù)的參數(shù)。
這個(gè)問題看上去沒有什么可說的。因?yàn)樽鳛榕拇a的,都肯定知道這個(gè)東西。
function method1(a, b) {
return a + b;
}
很簡(jiǎn)單,a,b就是函數(shù)method1的參數(shù)。一般情況下,這樣足夠了。
但是在JavaScript當(dāng)中,函數(shù)的調(diào)用,其中的參數(shù)個(gè)數(shù)是不強(qiáng)制的。(作為腳本語言,當(dāng)然類型也沒有強(qiáng)制)。
這里很象某些語言的不定參數(shù)列表(由于我沒有過多的研究,所以不便多說)。
比如上面的方法
var c = method1(1,2); //正確
var d = method(1); //也正確
此時(shí)c的值是3,d的值是NaN。
那么我們?nèi)绾蝸肀WC參數(shù)的個(gè)數(shù)呢?
JavaScript當(dāng)中,每一個(gè)方法里面,有一個(gè)內(nèi)欠的變量,arguments。這是一個(gè)數(shù)組變量,記錄著傳入這個(gè)方法一共有幾個(gè)參數(shù)。
很象java里面main函數(shù)的 args數(shù)組
static public void main(String args[]) {…}
我們可以使用arguments.length來判斷一共有幾個(gè)參數(shù)
function method2() {
if (arguments.length == 2) {
return arguments[0] + arguments[1];
} else {
return 0;
}
}
這樣我們就可以保證參數(shù)的個(gè)數(shù)了。
接下來,我們說一下函數(shù)作為參數(shù)。
在昨天的例子當(dāng)中,我使用了很多這樣的例子。比如
ServerHandler.getName(myID, fucntion (name) {
alert(”Hello:” + name);
});
這里面,getName方法的第二個(gè)參數(shù)就是一個(gè)方法,
function (name) {…}
在JavaScript 當(dāng)中,任何的東西都是Object,一個(gè)函數(shù)的類型是Function,他是Object的一個(gè)子類(姑且這么說,JavaScript不是一個(gè)純粹的面 向?qū)ο蟮恼Z言,所以說子類有一些牽強(qiáng))。所有的Object都可以作為參數(shù)被傳入函數(shù)。那么一個(gè)函數(shù)作為參數(shù)就不足為奇了。
暫停一下,我們現(xiàn)來看一下這個(gè)例子。
function method3() {
alert(”Hello”);
}
var m = method1;
var v = method1();
上面的兩個(gè)變量,m,v,他們的區(qū)別就是,v是method1運(yùn)算的結(jié)果,在這里是undefined,因?yàn)閙ethod3在alert以后什么都沒有返回。
而m,這句付值語句,是把method3的函數(shù)體,付值給了m(有興趣的話,可以使用alert(m)來看一下結(jié)果)。如果現(xiàn)在寫下如下的語句
m();
那么就會(huì)調(diào)用這個(gè)函數(shù),然后alert一個(gè)Hello。
好,我們回來,上面我想說明的就是,當(dāng)一個(gè)函數(shù)作為參數(shù)傳入到另外的一個(gè)函數(shù)當(dāng)中,我們是可以使用一個(gè)參數(shù)變量來接受這個(gè)函數(shù)的。并且在需要的時(shí)候,我們可以繼續(xù)的分發(fā),或者調(diào)用。
比如
function method4(m) {
m();
}
method4(method3); //注意,這里面的method3沒有后面的括號(hào)(),他表示是這個(gè)函數(shù)體作為參數(shù)而不是運(yùn)行結(jié)果作為參數(shù)。
在進(jìn)入到method4里面之后,通過參數(shù)變量m,可以調(diào)用method3。
利 用函數(shù)作為參數(shù),在JavaScript中使用的最普遍的就是callback方法了。當(dāng)需要執(zhí)行一個(gè)函數(shù),傳入一個(gè)callback function用來處理結(jié)果。這種方法我們?cè)诤芏嗟胤揭呀?jīng)使用的非常多了。現(xiàn)在隨著AJAX的流行,callback方法也越來的重要。如我昨天所說, 很多的和server通訊的方法都是無法預(yù)料何時(shí)能夠結(jié)束的。那么傳入一個(gè)callback function就很方便了。
這里,我一直有這樣的一個(gè)難點(diǎn),始終沒有很好的解決辦法。
就是一般情況,我們給出callback function,都是在無法控制返回時(shí)間的情況下采用的,那么此時(shí),大多數(shù)的情況就是這個(gè)方法會(huì)使用另外的線程來執(zhí)行,而程序會(huì)繼續(xù)向下走。
使用返回值的函數(shù)調(diào)用,是會(huì)在這個(gè)函數(shù)調(diào)用的時(shí)候,線程阻塞,直到返回結(jié)果。
我現(xiàn)在遇到的情況就是,很多的時(shí)候,我無法返回值(比如AJAX),而使用callback function也挺麻煩。所以一直沒有好的方法。
雖然我昨天說的信號(hào)量的方法可以變通的解決這個(gè)問題。但是在復(fù)雜的情況下(比如我現(xiàn)在正在開發(fā)的一套Web UI Framework)里面也是挺麻煩的。希望大家如果有好的方法可以告訴我。
最后,還要提及的一點(diǎn)就是,在函數(shù)調(diào)用的時(shí)候,還有一種方法就是call(或者apply)調(diào)用。
function method5(msg) {
alert(msg);
}
method5.call(x, “Hello”);
這個(gè)x就是某個(gè)Object的變量(從面向?qū)ο蟮慕嵌瓤梢哉f是一個(gè)實(shí)例(instance))。很類似于python的self。
這個(gè)使用方法在一般情況下意義不大。但是在某種面向?qū)ο髮?shí)現(xiàn)的時(shí)候是非常有用的。我會(huì)在面向?qū)ο蟮奈恼轮性俅握f明的。
從今天開始,我會(huì)不定期的寫一些關(guān)于JavaScript的東西,包括語言,應(yīng)用等方面。組成JavaScript系列。
如果沒有特殊的說明,這里假定JavaScript的執(zhí)行環(huán)境是在瀏覽器(browser)當(dāng)中的。
今天開始第一次,討論一下同步和異步。
曾經(jīng)查詢過一些JavaScript的信息,發(fā)現(xiàn)google出來的結(jié)果都是詢問JavaScript如何能夠?qū)崿F(xiàn)異步的代碼。
而我,很不幸,查詢的卻是如何讓JavaScript實(shí)現(xiàn)異步調(diào)用的同步(是不是挺起來很詭異)。
首先說一下JavaScript當(dāng)中的異步方法。
其實(shí)這個(gè)問題是大家經(jīng)常要碰到的。而且這個(gè)實(shí)現(xiàn)也很簡(jiǎn)單。我就不多說了。
給兩段代碼
我們應(yīng)該注意到setTimeout和setInterval都是window的方法。
我們可以直接使用,但是規(guī)范的還是調(diào)用window.setTimeout window.setInterval,之所以提及這個(gè),我會(huì)在以后的JavaScript系列中繼續(xù)講解。
現(xiàn)在該說一下我遇到的問題了。
我現(xiàn)在使用dwr作為AJAX的server端引擎,在調(diào)用dwr方法的時(shí)候,需要提供一個(gè)回調(diào)方法(callback function)來接受server的返回結(jié)果。
而這個(gè)回調(diào)方法是不會(huì)被阻塞的。此時(shí)browser回啟動(dòng)另外的現(xiàn)成處理。
這個(gè)很好理解,因?yàn)閐wr的這個(gè)方法執(zhí)行的時(shí)間是無法預(yù)料的,如果此時(shí)調(diào)用被阻塞,而server又花相當(dāng)長(zhǎng)的時(shí)間進(jìn)行處理。那么瀏覽器就會(huì)死在這里。從用戶體驗(yàn)的角度是根本無法接受的。
這里的例子代碼是
…
ServerHandler.getString(”Weiming”, function (str) { //”Weiming”是傳回server的參數(shù)
alert(str);
}); // ServerHandler是dwr提供的server方法的interface,具體使用請(qǐng)參見dwr網(wǎng)站。
alert(1);
在執(zhí)行的過程中,會(huì)先執(zhí)行alert(1),然后在一個(gè)無法預(yù)料的時(shí)間后執(zhí)行alert(str)。
如果一次簡(jiǎn)單的比如hello world的調(diào)用是不會(huì)出問題的。
但是如果我要執(zhí)行的一系列的dwr function是有前后順序的,比如后面執(zhí)行的需要前面的返回結(jié)果,簡(jiǎn)單的代碼書寫順序是無法保證執(zhí)行順序的。
var myID = null;
ServerHandler.getID(function (id) {
myID = id; //無法預(yù)料何時(shí)會(huì)執(zhí)行這句話
});
ServerHandler.getUserWithID(myID, function (name) {
/*
此時(shí)myID還沒有值,因?yàn)樯厦娴?myID = id這段代碼是需要一個(gè)時(shí)間段之后才會(huì)執(zhí)行的
*/
alert(”hello:” + name);
});
比如這樣的代碼就會(huì)出錯(cuò)。那么如何解決呢?
最簡(jiǎn)單的實(shí)現(xiàn)方法就是callback function的嵌套。
…
ServerHandler.getID(function (id) {
ServerHandler.getUserWithID(id, function (name) {
alert(”hello:” + name);
}
});
這樣我們就可以保證多個(gè)dwr方法調(diào)用的順序了。這樣貌似解決了問題。但是并不完美。
原因是當(dāng)我們把JavaScript和Browser作為一個(gè)操作的平臺(tái)和邏輯業(yè)務(wù)的平臺(tái)(AJAX的應(yīng)用程序,后面的JavaScript系列中會(huì)有提及),而不是一個(gè)簡(jiǎn)單的展示平臺(tái)的時(shí)候。這樣的回調(diào)函數(shù)嵌套就很難控制了。
這也就是我最開始指出的需要同步異步調(diào)用的一個(gè)方法。
最終我在公司的解決方案是這樣的。
寫一個(gè)信號(hào)量的類(JavaScript的面向?qū)ο髸?huì)稍后講解),當(dāng)我需要執(zhí)行一個(gè)方法的時(shí)候,我就申請(qǐng)一部分信號(hào)量。
把需要被執(zhí)行的方法放進(jìn)信號(hào)量的隊(duì)列進(jìn)行等待。等前面等待的方法(如果存在)執(zhí)行后在執(zhí)行。
信號(hào)量將作為一個(gè)參數(shù)被傳入執(zhí)行的方法,這樣這個(gè)方法可以決定釋放這個(gè)信號(hào)量還是繼續(xù)分發(fā)。
比如
var s = new Semaphore();
var myID = null;
s.p(function (e) { //把方法放入信號(hào)量隊(duì)列
ServerHandler.getID(function (id) {
myID = id;
s.v(); //釋放信號(hào)量
}
});
s.p(function (e) { //將第二個(gè)方法放到信號(hào)量隊(duì)列,只有當(dāng)前面的s.v()執(zhí)行之后,這個(gè)方法才會(huì)執(zhí)行。
ServerHandler.getName(myID, function (name) { //此時(shí),可以保證myID一定有值
alert(”Hello:” + name);
s.v();
})
})
這里只是對(duì)信號(hào)量這個(gè)方法進(jìn)行了簡(jiǎn)單的闡述。
信號(hào)量還支持創(chuàng)建自信號(hào)量,如果創(chuàng)建了子信號(hào)量,那么父信號(hào)量必須等帶所有的孩子都?xì)w還了信號(hào)量之后才可以執(zhí)行他里面的代碼。
由于代碼的版權(quán)是公司的,所以很抱歉,現(xiàn)在無法給出相應(yīng)的完整的信號(hào)量的實(shí)現(xiàn)。
如果下一端我有時(shí)間的話,我會(huì)給出一個(gè)我實(shí)現(xiàn)的版本的。
聯(lián)系客服