中文字幕日韩一区二区_国产一区二区av_国产毛片av_久久久久国产一区_色婷婷电影_国产一区二区精品

js 面向?qū)ο蟮募夹g(shù)創(chuàng)建高級(jí) Web 應(yīng)用程序

JavaScript 對(duì)象是詞典
在 C++ 或 C# 中,在談?wù)搶?duì)象時(shí),是指類或結(jié)構(gòu)的實(shí)例。對(duì)象有不同的屬性和方法,具體取決于將它們實(shí)例化的模板(即類)。而 JavaScript 對(duì)象卻不是這樣。在 JavaScript 中,對(duì)象只是一組名稱/值對(duì),就是說(shuō),將 JavaScript 對(duì)象視為包含字符串關(guān)鍵字的詞典。我們可以使用熟悉的“.”(點(diǎn))運(yùn)算符或“[]”運(yùn)算符,來(lái)獲得和設(shè)置對(duì)象的屬性,這是在處理詞典時(shí)通常采用的方法。以 下代碼段
復(fù)制代碼 代碼如下:
var userObject = new Object();
userObject.lastLoginTime = new Date();
alert(userObject.lastLoginTime);

的功能與下面的代碼段完全相同:
復(fù)制代碼 代碼如下:
var userObject = {}; // equivalent to new Object()
userObject[“l(fā)astLoginTime”] = new Date();
alert(userObject[“l(fā)astLoginTime”]);

我們還可以直接在 userObject 的定義中定義 lastLoginTime 屬性,如下所示:
復(fù)制代碼 代碼如下:
var userObject = { “l(fā)astLoginTime”: new Date() };
alert(userObject.lastLoginTime);

注意,它與 C# 3.0 對(duì)象初始值非常相似。而且,熟悉 Python 的人會(huì)發(fā)現(xiàn)在第二和第三個(gè)代碼段中實(shí)例化 userObject 的方法與在 Python 中指定詞典的方法完全相同。唯一的差異是 JavaScript 對(duì)象/詞典只接受字符串關(guān)鍵字,而不是像 Python 詞典那樣接受可哈希化的對(duì)象。
這 些示例還顯示 JavaScript 對(duì)象比 C++ 或 C# 對(duì)象具有更大的可延展性。您不必預(yù)先聲明屬性 lastLoginTime ― 如果 userObject 沒(méi)有該名稱的屬性,該屬性將被直接添加到 userObject。如果記住 JavaScript 對(duì)象是詞典,您就不會(huì)對(duì)此感到吃驚了,畢竟,我們一直在向詞典添加新關(guān)鍵字(和其各自的值)。
這 樣,我們就有了對(duì)象屬性。對(duì)象方法呢?同樣,JavaScript 與 C++/C# 不同。若要理解對(duì)象方法,首先需要仔細(xì)了解一下 JavaScript 函數(shù)。
JavaScript 函數(shù)是最棒的
在 很多編程語(yǔ)言中,函數(shù)和對(duì)象通常被視為兩樣不同的東西。在 JavaScript 中,其差別很模糊 ― JavaScript 函數(shù)實(shí)際上是具有與它關(guān)聯(lián)的可執(zhí)行代碼的對(duì)象。請(qǐng)如此看待普通函數(shù):
復(fù)制代碼 代碼如下:
function func(x) {
alert(x);
}
func(“blah”);

這就是通常在 JavaScript 中定義函數(shù)的方法。但是,還可以按以下方法定義該函數(shù),您在此創(chuàng)建匿名函數(shù)對(duì)象,并將它賦給變量 func
復(fù)制代碼 代碼如下:
var func = function(x) {
alert(x);
};
func(“blah2”);

甚至也可以像下面這樣,使用 Function 構(gòu)造函數(shù):
復(fù)制代碼 代碼如下:
var func = new Function(“x”, “alert(x);”);
func(“blah3”);

此示例表明函數(shù)實(shí)際上只是支持函數(shù)調(diào)用操作的對(duì)象。最后一個(gè)使用 Function 構(gòu)造函數(shù)來(lái)定義函數(shù)的方法并不常用,但它展示的可能性非常有趣,因?yàn)槟赡茏⒁獾剑摵瘮?shù)的主體正是 Function 構(gòu)造函數(shù)的 String 參數(shù)。這意味著,您可以在運(yùn)行時(shí)構(gòu)造任意函數(shù)。
為 了進(jìn)一步演示函數(shù)是對(duì)象,您可以像對(duì)其他任何 JavaScript 對(duì)象一樣,在函數(shù)中設(shè)置或添加屬性:
復(fù)制代碼 代碼如下:
function sayHi(x) {
alert(“Hi, “ + x + “!”);
}
sayHi.text = “Hello World!”;
sayHi[“text2”] = “Hello World... again.”;
alert(sayHi[“text”]); // displays “Hello World!”
alert(sayHi.text2); // displays “Hello World... again.”

作為對(duì)象,函數(shù)還可以賦給變量、作為參數(shù)傳遞給其他函數(shù)、作為其他函數(shù)的值返回,并可以作為對(duì)象的屬性或數(shù)組的元素進(jìn)行存儲(chǔ)等等。圖 1 提供了這樣一個(gè)示例。
JavaScript 中的函數(shù)是最棒的
復(fù)制代碼 代碼如下:
// assign an anonymous function to a variable
var greet = function(x) {
alert(“Hello, “ + x);
};
greet(“MSDN readers”);
// passing a function as an argument to another
function square(x) {
return x * x;
}
function operateOn(num, func) {
return func(num);
}
// displays 256
alert(operateOn(16, square));
// functions as return values
function makeIncrementer() {
return function(x) { return x + 1; };
}
var inc = makeIncrementer();
// displays 8
alert(inc(7));
// functions stored as array elements
var arr = [];
arr[0] = function(x) { return x * x; };
arr[1] = arr[0](2);
arr[2] = arr[0](arr[1]);
arr[3] = arr[0](arr[2]);
// displays 256
alert(arr[3]);
// functions as object properties
var obj = { “toString” : function() { return “This is an object.”; } };
// calls obj.toString()
alert(obj);

記住這一點(diǎn)后,向?qū)ο筇砑臃椒▽⑹呛苋菀椎氖虑椋褐恍柽x擇名稱,然后將函數(shù)賦給該名稱。因此,我通過(guò)將匿名函數(shù)分別賦給相應(yīng)的方法名稱,在對(duì)象中定義了三 個(gè)方法:
代碼
復(fù)制代碼 代碼如下:
var myDog = {
“name” : “Spot”,
“bark” : function() { alert(“Woof!”); },
“displayFullName” : function() {
alert(this.name + “ The Alpha Dog”);
},
“chaseMrPostman” : function() {
// implementation beyond the scope of this article
}
};
myDog.displayFullName();
myDog.bark(); // Woof!

C++/C# 開(kāi)發(fā)人員應(yīng)當(dāng)很熟悉 displayFullName 函數(shù)中使用的“this”關(guān)鍵字 ― 它引用一個(gè)對(duì)象,通過(guò)對(duì)象調(diào)用方法(使用 Visual Basic 的開(kāi)發(fā)人員也應(yīng)當(dāng)很熟悉它,它在 Visual Basic 中叫做“Me”)。因此在上面的示例中,displayFullName 中的“this”的值是 myDog 對(duì)象。但是,“this”的值不是靜態(tài)的。通過(guò)不同對(duì)象調(diào)用“this”時(shí),它的值也會(huì)更改以便指向相應(yīng)的對(duì)象,如圖 2 所示。
“this”隨對(duì)象更改而更改
復(fù)制代碼 代碼如下:
function displayQuote() {
// the value of “this” will change; depends on
// which object it is called through
alert(this.memorableQuote);
}
var williamShakespeare = {
“memorableQuote”: “It is a wise father that knows his own child.”,
“sayIt” : displayQuote
};
var markTwain = {
“memorableQuote”: “Golf is a good walk spoiled.”,
“sayIt” : displayQuote
};
var oscarWilde = {
“memorableQuote”: “True friends stab you in the front.”
// we can call the function displayQuote
// as a method of oscarWilde without assigning it
// as oscarWilde's method.
//”sayIt” : displayQuote
};
williamShakespeare.sayIt(); // true, true
markTwain.sayIt(); // he didn't know where to play golf
// watch this, each function has a method call()
// that allows the function to be called as a
// method of the object passed to call() as an
// argument.
// this line below is equivalent to assigning
// displayQuote to sayIt, and calling oscarWilde.sayIt().
displayQuote.call(oscarWilde); // ouch!

圖 2 中的最后一行表示的是將函數(shù)作為對(duì)象的方法進(jìn)行調(diào)用的另一種方式。請(qǐng)記住,JavaScript 中的函數(shù)是對(duì)象。每個(gè)函數(shù)對(duì)象都有一個(gè)名為 call 的方法,它將函數(shù)作為第一個(gè)參數(shù)的方法進(jìn)行調(diào)用。就是說(shuō),作為函數(shù)第一個(gè)參數(shù)傳遞給 call 的任何對(duì)象都將在函數(shù)調(diào)用中成為“this”的值。這一技術(shù)對(duì)于調(diào)用基類構(gòu)造函數(shù)來(lái)說(shuō)非常有用,稍后將對(duì)此進(jìn)行介紹。
有 一點(diǎn)需要記住,絕不要調(diào)用包含“this”(卻沒(méi)有所屬對(duì)象)的函數(shù)。否則,將違反全局命名空間,因?yàn)樵谠撜{(diào)用中,“this”將引用全局對(duì)象,而這必然 會(huì)給您的應(yīng)用程序帶來(lái)災(zāi)難。例如,下面的腳本將更改 JavaScript 的全局函數(shù) isNaN 的行為。一定不要這樣做!
代碼
復(fù)制代碼 代碼如下:
alert(“NaN is NaN: “ + isNaN(NaN));
function x() {
this.isNaN = function() {
return “not anymore!”;
};
}
// alert!!! trampling the Global object!!!
x();
alert(“NaN is NaN: “ + isNaN(NaN));

到這里,我們已經(jīng)介紹了如何創(chuàng)建對(duì)象,包括它的屬性和方法。但如果注意上面的所有代碼 段,您會(huì)發(fā)現(xiàn)屬性和方法是在對(duì)象定義本身中進(jìn)行硬編碼的。但如果需要更好地控制對(duì)象的創(chuàng)建,該怎么做呢?例如,您可能需要根據(jù)某些參數(shù)來(lái)計(jì)算對(duì)象的屬性 值。或者,可能需要將對(duì)象的屬性初始化為僅在運(yùn)行時(shí)才能獲得的值。也可能需要?jiǎng)?chuàng)建對(duì)象的多個(gè)實(shí)例(此要求非常常見(jiàn))。
在 C# 中,我們使用類來(lái)實(shí)例化對(duì)象實(shí)例。但 JavaScript 與此不同,因?yàn)樗鼪](méi)有類。您將在下一節(jié)中看到,您可以充分利用這一情況:函數(shù)在與“new”運(yùn)算符一起使用時(shí),函數(shù)將充當(dāng)構(gòu)造函數(shù)。
構(gòu)造函數(shù)而不是類
前 面提到過(guò),有關(guān) JavaScript OOP 的最奇怪的事情是,JavaScript 不像 C# 或 C++ 那樣,它沒(méi)有類。在 C# 中,在執(zhí)行類似下面的操作時(shí):
Dog spot = new Dog();
將返回一個(gè)對(duì)象,該對(duì)象是 Dog 類的實(shí)例。但在 JavaScript 中,本來(lái)就沒(méi)有類。與訪問(wèn)類最近似的方法是定義構(gòu)造函數(shù),如下所示:
代碼
復(fù)制代碼 代碼如下:
function DogConstructor(name) {
this.name = name;
this.respondTo = function(name) {
if(this.name == name) {
alert(“Woof”);
}
};
}
var spot = new DogConstructor(“Spot”);
spot.respondTo(“Rover”); // nope
spot.respondTo(“Spot”); // yeah!

那么,結(jié)果會(huì)怎樣呢?暫時(shí)忽略 DogConstructor 函數(shù)定義,看一看這一行:
var spot = new DogConstructor(“Spot”);
“new”運(yùn)算符執(zhí)行的操作很簡(jiǎn)單。首先,它創(chuàng)建一個(gè)新的空對(duì)象。然后執(zhí)行緊隨其后的函數(shù)調(diào)用,將新的空對(duì)象設(shè)置為該函數(shù)中“this”的值。換句話說(shuō), 可以認(rèn)為上面這行包含“new”運(yùn)算符的代碼與下面兩行代碼的功能相當(dāng):
復(fù)制代碼 代碼如下:
// create an empty object
var spot = {};
// call the function as a method of the empty object
DogConstructor.call(spot, “Spot”);

正如在 DogConstructor 主體中看到的那樣,調(diào)用此函數(shù)將初始化對(duì)象,在調(diào)用期間關(guān)鍵字“this”將引用此對(duì)象。這樣,就可以為對(duì)象創(chuàng)建模板!只要需要?jiǎng)?chuàng)建類似的對(duì)象,就可以與 構(gòu)造函數(shù)一起調(diào)用“new”,返回的結(jié)果將是一個(gè)完全初始化的對(duì)象。這與類非常相似,不是嗎?實(shí)際上,在 JavaScript 中構(gòu)造函數(shù)的名稱通常就是所模擬的類的名稱,因此在上面的示例中,可以直接命名構(gòu)造函數(shù) Dog:
代碼
復(fù)制代碼 代碼如下:
// Think of this as class Dog
function Dog(name) {
// instance variable
this.name = name;
// instance method? Hmmm...
this.respondTo = function(name) {
if(this.name == name) {
alert(“Woof”);
}
};
}
var spot = new Dog(“Spot”);

在上面的 Dog 定義中,我定義了名為 name 的實(shí)例變量。使用 Dog 作為其構(gòu)造函數(shù)所創(chuàng)建的每個(gè)對(duì)象都有它自己的實(shí)例變量名稱副本(前面提到過(guò),它就是對(duì)象詞典的條目)。這就是希望的結(jié)果。畢竟,每個(gè)對(duì)象都需要它自己的實(shí) 例變量副本來(lái)表示其狀態(tài)。但如果看看下一行,就會(huì)發(fā)現(xiàn)每個(gè) Dog 實(shí)例也都有它自己的 respondTo 方法副本,這是個(gè)浪費(fèi);您只需要一個(gè)可供各個(gè) Dog 實(shí)例共享的 respondTo 實(shí)例!通過(guò)在 Dog 以外定義 respondTo,可以避免此問(wèn)題,如下所示:
復(fù)制代碼 代碼如下:
function respondTo() {
// respondTo definition
}
function Dog(name) {
this.name = name;
// attached this function as a method of the object
this.respondTo = respondTo;
}

這樣,所有 Dog 實(shí)例(即用構(gòu)造函數(shù) Dog 創(chuàng)建的所有實(shí)例)都可以共享 respondTo 方法的一個(gè)實(shí)例。但隨著方法數(shù)的增加,維護(hù)工作將越來(lái)越難。最后,基本代碼中將有很多全局函數(shù),而且隨著“類”的增加,事情只會(huì)變得更加糟糕(如果它們的 方法具有相似的名稱,則尤甚)。但使用原型對(duì)象可以更好地解決這個(gè)問(wèn)題,這是下一節(jié)的主題。
原型
在 使用 JavaScript 的面向?qū)ο缶幊讨校蛯?duì)象是個(gè)核心概念。在 JavaScript 中對(duì)象是作為現(xiàn)有示例(即原型)對(duì)象的副本而創(chuàng)建的,該名稱就來(lái)自于這一概念。此原型對(duì)象的任何屬性和方法都將顯示為從原型的構(gòu)造函數(shù)創(chuàng)建的對(duì)象的屬性和 方法。可以說(shuō),這些對(duì)象從其原型繼承了屬性和方法。當(dāng)您創(chuàng)建如下所示的新 Dog 對(duì)象時(shí):
var buddy = new Dog(“Buddy“);
buddy 所引用的對(duì)象將從它的原型繼承屬性和方法,盡管僅從這一行可能無(wú)法明確判斷原型來(lái)自哪里。對(duì)象 buddy 的原型來(lái)自構(gòu)造函數(shù)(在這里是函數(shù) Dog)的屬性。
JavaScript 中,每個(gè)函數(shù)都有名為“prototype”的屬性,用于引用原型對(duì)象。此原型對(duì)象又有名為“constructor”的屬性,它反過(guò)來(lái)引用函數(shù)本身。這 是一種循環(huán)引用,圖 3 更好地說(shuō)明了這種循環(huán)關(guān)系。
 
圖 3 每個(gè)函數(shù)的原型都有一個(gè) Constructor 屬性 現(xiàn) 在,通過(guò)“new”運(yùn)算符用函數(shù)(上面示例中為 Dog)創(chuàng)建對(duì)象時(shí),所獲得的對(duì)象將繼承 Dog.prototype 的屬性。在圖 3 中,可以看到 Dog.prototype 對(duì)象有一個(gè)回指 Dog 函數(shù)的構(gòu)造函數(shù)屬性。這樣,每個(gè) Dog 對(duì)象(從 Dog.prototype 繼承而來(lái))都有一個(gè)回指 Dog 函數(shù)的構(gòu)造函數(shù)屬性。圖 4 中的代碼證實(shí)了這一點(diǎn)。圖 5 顯示了構(gòu)造函數(shù)、原型對(duì)象以及用它們創(chuàng)建的對(duì)象之間的這一關(guān)系。
復(fù)制代碼 代碼如下:
var spot = new Dog(“Spot”);
// Dog.prototype is the prototype of spot
alert(Dog.prototype.isPrototypeOf(spot));
// spot inherits the constructor property
// from Dog.prototype
alert(spot.constructor == Dog.prototype.constructor);
alert(spot.constructor == Dog);
// But constructor property doesn't belong
// to spot. The line below displays “false”
alert(spot.hasOwnProperty(“constructor”));
// The constructor property belongs to Dog.prototype
// The line below displays “true”
alert(Dog.prototype.hasOwnProperty(“constructor”));

 
圖 5 實(shí)例繼承其原型
某 些讀者可能已經(jīng)注意到圖 4 中對(duì) hasOwnProperty 和 isPrototypeOf 方法的調(diào)用。這些方法是從哪里來(lái)的呢?它們不是來(lái)自 Dog.prototype。實(shí)際上,在 Dog.prototype 和 Dog 實(shí)例中還可以調(diào)用其他方法,比如 toString、toLocaleString 和 valueOf,但它們都不來(lái)自 Dog.prototype。您會(huì)發(fā)現(xiàn),就像 .NET Framework 中的 System.Object 充當(dāng)所有類的最終基類一樣,JavaScript 中的 Object.prototype 是所有原型的最終基礎(chǔ)原型。(Object.prototype 的原型是 null。)
在 此示例中,請(qǐng)記住 Dog.prototype 是對(duì)象。它是通過(guò)調(diào)用 Object 構(gòu)造函數(shù)創(chuàng)建的(盡管它不可見(jiàn)):
Dog.prototype = new Object();


因此,正如 Dog 實(shí)例繼承 Dog.prototype 一樣,Dog.prototype 繼承 Object.prototype。這使得所有 Dog 實(shí)例也繼承了 Object.prototype 的方法和屬性。
每 個(gè) JavaScript 對(duì)象都繼承一個(gè)原型鏈,而所有原型都終止于 Object.prototype。注意,迄今為止您看到的這種繼承是活動(dòng)對(duì)象之間的繼承。它不同于繼承的常見(jiàn)概念,后者是指在聲明類時(shí)類之間的發(fā)生的繼 承。因此,JavaScript 繼承動(dòng)態(tài)性更強(qiáng)。它使用簡(jiǎn)單算法實(shí)現(xiàn)這一點(diǎn),如下所示:當(dāng)您嘗試訪問(wèn)對(duì)象的屬性/方法時(shí),JavaScript 將檢查該屬性/方法是否是在該對(duì)象中定義的。如果不是,則檢查對(duì)象的原型。如果還不是,則檢查該對(duì)象的原型的原型,如此繼續(xù),一直檢查到 Object.prototype。圖 6 說(shuō)明了此解析過(guò)程。
 
圖 6 在原型鏈中解析 toString() 方法 (單 擊該圖像獲得較大視圖)
JavaScript 動(dòng)態(tài)地解析屬性訪問(wèn)和方法調(diào)用的方式產(chǎn)生了一些特殊效果:
繼承原型對(duì)象的對(duì)象上可以立即呈現(xiàn)對(duì)原型所做的更改,即使是在創(chuàng)建這些對(duì)象之后。
如果在對(duì)象中定義了屬性/方法 X,則該對(duì)象的原型中將隱藏同名的屬性/方法。例如,通過(guò)在 Dog.prototype 中定義 toString 方法,可以改寫(xiě) Object.prototype 的 toString 方法。
更改只沿一個(gè)方向傳遞,即從原型到它的派生對(duì)象,但不能沿相反方向傳 遞。
圖 7 說(shuō)明了這些效果。圖 7 還顯示了如何解決前面遇到的不需要的方法實(shí)例的問(wèn)題。通過(guò)將方法放在原型內(nèi)部,可以使對(duì)象共享方法,而不必使每個(gè)對(duì)象都有單獨(dú)的函數(shù)對(duì)象實(shí)例。在此示例 中,rover 和 spot 共享 getBreed 方法,直至在 spot 中以任何方式改寫(xiě) toString 方法。此后,spot 有了它自己版本的 getBreed 方法,但 rover 對(duì)象和用新 GreatDane 創(chuàng)建的后續(xù)對(duì)象仍將共享在 GreatDane.prototype 對(duì)象中定義的那個(gè) getBreed 方法實(shí)例。
繼承原型
復(fù)制代碼 代碼如下:
function GreatDane() { }
var rover = new GreatDane();
var spot = new GreatDane();
GreatDane.prototype.getBreed = function() {
return “Great Dane”;
};
// Works, even though at this point
// rover and spot are already created.
alert(rover.getBreed());
// this hides getBreed() in GreatDane.prototype
spot.getBreed = function() {
return “Little Great Dane”;
};
alert(spot.getBreed());
// but of course, the change to getBreed
// doesn't propagate back to GreatDane.prototype
// and other objects inheriting from it,
// it only happens in the spot object
alert(rover.getBreed());

靜態(tài)屬性和方法
有 時(shí),您需要綁定到類而不是實(shí)例的屬性或方法,也就是,靜態(tài)屬性和方法。在 JavaScript 中很容易做到這一點(diǎn),因?yàn)楹瘮?shù)是可以按需要設(shè)置其屬性和方法的對(duì)象。由于在 JavaScript 中構(gòu)造函數(shù)表示類,因此可以通過(guò)在構(gòu)造函數(shù)中設(shè)置靜態(tài)方法和屬性,直接將它們添加到類中,如下所示:
復(fù)制代碼 代碼如下:
function DateTime() { }
// set static method now()
DateTime.now = function() {
return new Date();
};
alert(DateTime.now());

JavaScript 中調(diào)用靜態(tài)方法的語(yǔ)法與在 C# 中幾乎完全相同。這不應(yīng)當(dāng)讓人感到吃驚,因?yàn)闃?gòu)造函數(shù)的名稱實(shí)際上是類的名稱。這樣,就有了類、公用屬性/方法,以及靜態(tài)屬性/方法。還需要其他什么嗎? 當(dāng)然,私有成員。但 JavaScript 本身并不支持私有成員(同樣,也不支持受保護(hù)成員)。任何人都可以訪問(wèn)對(duì)象的所有屬性和方法。但我們有辦法讓類中包含私有成員,但在此之前,您首先需要理 解閉包。

閉包
我 沒(méi)有自覺(jué)地學(xué)習(xí)過(guò) JavaScript。我必須快點(diǎn)了解它,因?yàn)槲野l(fā)現(xiàn)如果沒(méi)有它,在實(shí)際工作中編寫(xiě) AJAX 應(yīng)用程序的準(zhǔn)備就會(huì)不充分。開(kāi)始,我感到我的編程水平好像降了幾個(gè)級(jí)別。(JavaScript!我的 C++ 朋友會(huì)怎么說(shuō)?)但一旦我克服最初的障礙,我就發(fā)現(xiàn) JavaScript 實(shí)際上是功能強(qiáng)大、表現(xiàn)力強(qiáng)而且非常簡(jiǎn)練的語(yǔ)言。它甚至具有其他更流行的語(yǔ)言才剛剛開(kāi)始支持的功能。
JavaScript 的更高級(jí)功能之一是它支持閉包,這是 C# 2.0 通過(guò)它的匿名方法支持的功能。閉包是當(dāng)內(nèi)部函數(shù)(或 C# 中的內(nèi)部匿名方法)綁定到它的外部函數(shù)的本地變量時(shí)所發(fā)生的運(yùn)行時(shí)現(xiàn)象。很明顯,除非此內(nèi)部函數(shù)以某種方式可被外部函數(shù)訪問(wèn),否則它沒(méi)有多少意義。示例可 以更好說(shuō)明這一點(diǎn)。
假 設(shè)需要根據(jù)一個(gè)簡(jiǎn)單條件篩選一個(gè)數(shù)字序列,這個(gè)條件是:只有大于 100 的數(shù)字才能通過(guò)篩選,并忽略其余數(shù)字。為此,可以編寫(xiě)類似圖 8 中的函數(shù)。
根據(jù)謂詞篩選元素
復(fù)制代碼 代碼如下:
function filter(pred, arr) {
var len = arr.length;
var filtered = []; // shorter version of new Array();
// iterate through every element in the array...
for(var i = 0; i < len; i++) {
var val = arr[i];
// if the element satisfies the predicate let it through
if(pred(val)) {
filtered.push(val);
}
}
return filtered;
}
var someRandomNumbers = [12, 32, 1, 3, 2, 2, 234, 236, 632,7, 8];
var numbersGreaterThan100 = filter(
function(x) { return (x > 100) ? true : false; },
someRandomNumbers);
// displays 234, 236, 632
alert(numbersGreaterThan100);

但是,現(xiàn)在要?jiǎng)?chuàng)建不同的篩選條件,假設(shè)這次只有大于 300 的數(shù)字才能通過(guò)篩選,則可以編寫(xiě)下面這樣的函數(shù):

復(fù)制代碼 代碼如下:
var greaterThan300 = filter(
function(x) { return (x > 300) ? true : false; },
someRandomNumbers);

然后,也許需要篩選大于 50、25、10、600 如此等等的數(shù)字,但作為一個(gè)聰明人,您會(huì)發(fā)現(xiàn)它們?nèi)慷加邢嗤闹^詞“greater than”,只有數(shù)字不同。因此,可以用類似下面的函數(shù)分開(kāi)各個(gè)數(shù)字:
復(fù)制代碼 代碼如下:
function makeGreaterThanPredicate(lowerBound) {
return function(numberToCheck) {
return (numberToCheck > lowerBound) ? true : false;
};
}

這樣,您就可以編寫(xiě)以下代碼:

代碼
復(fù)制代碼 代碼如下:
var greaterThan10 = makeGreaterThanPredicate(10);
var greaterThan100 = makeGreaterThanPredicate(100);
alert(filter(greaterThan10, someRandomNumbers));
alert(filter(greaterThan100, someRandomNumbers));

通過(guò)觀察函數(shù) makeGreaterThanPredicate 返回的內(nèi)部匿名函數(shù),可以發(fā)現(xiàn),該匿名內(nèi)部函數(shù)使用 lowerBound,后者是傳遞給 makeGreaterThanPredicate 的參數(shù)。按照作用域的一般規(guī)則,當(dāng) makeGreaterThanPredicate 退出時(shí),lowerBound 超出了作用域!但在這里,內(nèi)部匿名函數(shù)仍然攜帶 lowerBound,甚至在 makeGreaterThanPredicate 退出之后的很長(zhǎng)時(shí)間內(nèi)仍然如此。這就是我們所說(shuō)的閉包:因?yàn)閮?nèi)部函數(shù)關(guān)閉了定義它的環(huán)境(即外部函數(shù)的參數(shù)和本地變量)。
開(kāi) 始可能感覺(jué)不到閉包的功能很強(qiáng)大。但如果應(yīng)用恰當(dāng),它們就可以非常有創(chuàng)造性地幫您將想法轉(zhuǎn)換成代碼,這個(gè)過(guò)程非常有趣。在 JavaScript 中,閉包最有趣的用途之一是模擬類的私有變量。

模擬私有屬性
現(xiàn) 在介紹閉包如何幫助模擬私有成員。正常情況下,無(wú)法從函數(shù)以外訪問(wèn)函數(shù)內(nèi)的本地變量。函數(shù)退出之后,由于各種實(shí)際原因,該本地變量將永遠(yuǎn)消失。但是,如果 該本地變量被內(nèi)部函數(shù)的閉包捕獲,它就會(huì)生存下來(lái)。這一事實(shí)是模擬 JavaScript 私有屬性的關(guān)鍵。假設(shè)有一個(gè) Person 類:
代碼
復(fù)制代碼 代碼如下:
function Person(name, age) {
this.getName = function() { return name; };
this.setName = function(newName) { name = newName; };
this.getAge = function() { return age; };
this.setAge = function(newAge) { age = newAge; };
}

參數(shù) name 和 age 是構(gòu)造函數(shù) Person 的本地變量。Person 返回時(shí),name 和 age 應(yīng)當(dāng)永遠(yuǎn)消失。但是,它們被作為 Person 實(shí)例的方法而分配的四個(gè)內(nèi)部函數(shù)捕獲,實(shí)際上這會(huì)使 name 和 age 繼續(xù)存在,但只能嚴(yán)格地通過(guò)這四個(gè)方法訪問(wèn)它們。因此,您可以:

代碼
復(fù)制代碼 代碼如下:
var ray = new Person(“Ray”, 31);
alert(ray.getName());
alert(ray.getAge());
ray.setName(“Younger Ray”);
// Instant rejuvenation!
ray.setAge(22);
alert(ray.getName() + “ is now “ + ray.getAge() +
“ years old.”);

未在構(gòu)造函數(shù)中初始化的私有成員可以成為構(gòu)造函數(shù)的本地變量,如下所示:

代碼
復(fù)制代碼 代碼如下:
function Person(name, age) {
var occupation;
this.getOccupation = function() { return occupation; };
this.setOccupation = function(newOcc) { occupation =
newOcc; };
// accessors for name and age
}

注意,這些私有成員與我們期望從 C# 中產(chǎn)生的私有成員略有不同。在 C# 中,類的公用方法可以訪問(wèn)它的私有成員。但在 JavaScript 中,只能通過(guò)在其閉包內(nèi)擁有這些私有成員的方法來(lái)訪問(wèn)私有成員(由于這些方法不同于普通的公用方法,它們通常被稱為特權(quán)方法)。因此,在 Person 的公用方法中,仍然必須通過(guò)私有成員的特權(quán)訪問(wèn)器方法才能訪問(wèn)私有成員:
復(fù)制代碼 代碼如下:
Person.prototype.somePublicMethod = function() {
// doesn't work!
// alert(this.name);
// this one below works
alert(this.getName());
};

Douglas Crockford 是著名的發(fā)現(xiàn)(或者也許是發(fā)布)使用閉包來(lái)模擬私有成員這一技術(shù)的第一人。他的網(wǎng)站 Javascript.crockford.com 包含有關(guān) JavaScript 的豐富信息,任何對(duì) JavaScript 感興趣的開(kāi)發(fā)人員都應(yīng)當(dāng)仔細(xì)研讀。

從類繼承
到 這里,我們已經(jīng)了解了構(gòu)造函數(shù)和原型對(duì)象如何使您在 JavaScript 中模擬類。您已經(jīng)看到,原型鏈可以確保所有對(duì)象都有 Object.prototype 的公用方法,以及如何使用閉包來(lái)模擬類的私有成員。但這里還缺少點(diǎn)什么。您尚未看到如何從類派生,這在 C# 中是每天必做的工作。遺憾的是,在 JavaScript 中從類繼承并非像在 C# 中鍵入冒號(hào)即可繼承那樣簡(jiǎn)單,它需要進(jìn)行更多操作。另一方面,JavaScript 非常靈活,可以有很多從類繼承的方式。
例 如,有一個(gè)基類 Pet,它有一個(gè)派生類 Dog,如圖 9 所示。這個(gè)在 JavaScript 中如何實(shí)現(xiàn)呢?Pet 類很容易。您已經(jīng)看見(jiàn)如何實(shí)現(xiàn)它了:
 
復(fù)制代碼 代碼如下:
// class Pet
function Pet(name) {
this.getName = function() { return name; };
this.setName = function(newName) { name = newName; };
}
Pet.prototype.toString = function() {
return “This pet's name is: “ + this.getName();
};
// end of class Pet
var parrotty = new Pet(“Parrotty the Parrot”);
alert(parrotty);

現(xiàn)在,如何創(chuàng)建從 Pet 派生的類 Dog 呢?在圖 9 中可以看到,Dog 有另一個(gè)屬性 breed,它改寫(xiě)了 Pet 的 toString 方法(注意,JavaScript 的約定是方法和屬性名稱使用 camel 大小寫(xiě),而不是在 C# 中建議的 Pascal 大小寫(xiě))。圖 10 顯示如何這樣做。

從Pet類派生
復(fù)制代碼 代碼如下:
// class Dog : Pet
// public Dog(string name, string breed)
function Dog(name, breed) {
// think Dog : base(name)
Pet.call(this, name);
this.getBreed = function() { return breed; };
// Breed doesn't change, obviously! It's read only.
// this.setBreed = function(newBreed) { name = newName; };
}
// this makes Dog.prototype inherits
// from Pet.prototype
Dog.prototype = new Pet();
// remember that Pet.prototype.constructor
// points to Pet. We want our Dog instances'
// constructor to point to Dog.
Dog.prototype.constructor = Dog;
// Now we override Pet.prototype.toString
Dog.prototype.toString = function() {
return “This dog's name is: “ + this.getName() +
“, and its breed is: “ + this.getBreed();
};
// end of class Dog
var dog = new Dog(“Buddy”, “Great Dane”);
// test the new toString()
alert(dog);
// Testing instanceof (similar to the is operator)
// (dog is Dog)? yes
alert(dog instanceof Dog);
// (dog is Pet)? yes
alert(dog instanceof Pet);
// (dog is Object)? yes
alert(dog instanceof Object);

所使用的原型 ― 替換技巧正確設(shè)置了原型鏈,因此假如使用 C#,測(cè)試的實(shí)例將按預(yù)期運(yùn)行。而且,特權(quán)方法仍然會(huì)按預(yù)期運(yùn)行。

模擬命名空間
在 C++ 和 C# 中,命名空間用于盡可能地減少名稱沖突。例如,在 .NET Framework 中,命名空間有助于將 Microsoft.Build.Task.Message 類與 System.Messaging.Message 區(qū)分開(kāi)來(lái)。JavaScript 沒(méi)有任何特定語(yǔ)言功能來(lái)支持命名空間,但很容易使用對(duì)象來(lái)模擬命名空間。如果要?jiǎng)?chuàng)建一個(gè) JavaScript 庫(kù),則可以將它們包裝在命名空間內(nèi),而不需要定義全局函數(shù)和類,如下所示:
復(fù)制代碼 代碼如下:
var MSDNMagNS = {};
MSDNMagNS.Pet = function(name) { // code here };
MSDNMagNS.Pet.prototype.toString = function() { // code };
var pet = new MSDNMagNS.Pet(“Yammer”);

命名空間的一個(gè)級(jí)別可能不是唯一的,因此可以創(chuàng)建嵌套的命名空間:

代碼
復(fù)制代碼 代碼如下:
var MSDNMagNS = {};
// nested namespace “Examples”
MSDNMagNS.Examples = {};
MSDNMagNS.Examples.Pet = function(name) { // code };
MSDNMagNS.Examples.Pet.prototype.toString = function() { // code };
var pet = new MSDNMagNS.Examples.Pet(“Yammer”);

可以想象,鍵入這些冗長(zhǎng)的嵌套命名空間會(huì)讓人很累。 幸運(yùn)的是,庫(kù)用戶可以很容易地為命名空間指定更短的別名:
復(fù)制代碼 代碼如下:
// MSDNMagNS.Examples and Pet definition...
// think “using Eg = MSDNMagNS.Examples;”
var Eg = MSDNMagNS.Examples;
var pet = new Eg.Pet(“Yammer”);
alert(pet);

如果看一下 Microsoft AJAX 庫(kù)的源代碼,就會(huì)發(fā)現(xiàn)庫(kù)的作者使用了類似的技術(shù)來(lái)實(shí)現(xiàn)命名空間(請(qǐng)參閱靜態(tài)方法 Type.registerNamespace 的實(shí)現(xiàn))。有關(guān)詳細(xì)信息,請(qǐng)參與側(cè)欄“OOP 和 ASP.NET AJAX”。

應(yīng)當(dāng)這樣編寫(xiě) JavaScript 代碼嗎?
您 已經(jīng)看見(jiàn) JavaScript 可以很好地支持面向?qū)ο蟮木幊獭1M管它是一種基于原型的語(yǔ)言,但它的靈活性和強(qiáng)大功能可以滿足在其他流行語(yǔ)言中常見(jiàn)的基于類的編程風(fēng)格。但問(wèn)題是:是否應(yīng) 當(dāng)這樣編寫(xiě) JavaScript 代碼?在 JavaScript 中的編程方式是否應(yīng)與 C# 或 C++ 中的編碼方式相同?是否有更聰明的方式來(lái)模擬 JavaScript 中沒(méi)有的功能?每種編程語(yǔ)言都各不相同,一種語(yǔ)言的最佳做法,對(duì)另一種語(yǔ)言而言則可能并非最佳。
JavaScript 中,您已看到對(duì)象繼承對(duì)象(與類繼承類不同)。因此,使用靜態(tài)繼承層次結(jié)構(gòu)建立很多類的方式可能并不適合 JavaScript。也許,就像 Douglas Crockford 在他的文章 Prototypal Inheritance in JavaScript 中說(shuō)的那樣,JavaScript 編程方式是建立原型對(duì)象,并使用下面的簡(jiǎn)單對(duì)象函數(shù)建立新的對(duì)象,而后者則繼承原始對(duì)象:
復(fù)制代碼 代碼如下:
function object(o) {
function F() {}
F.prototype = o;
return new F();
}

然后,由于 JavaScript 中的對(duì)象是可延展的,因此可以方便地在創(chuàng)建對(duì)象之后,根據(jù)需要用新字段和新方法增大對(duì)象。
這 的確很好,但它不可否認(rèn)的是,全世界大多數(shù)開(kāi)發(fā)人員更熟悉基于類的編程。實(shí)際上,基于類的編程也會(huì)在這里出現(xiàn)。按照即將頒發(fā)的 ECMA-262 規(guī)范第 4 版(ECMA-262 是 JavaScript 的官方規(guī)范),JavaScript 2.0 將擁有真正的類。因此,JavaScript 正在發(fā)展成為基于類的語(yǔ)言。但是,數(shù)年之后 JavaScript 2.0 才可能會(huì)被廣泛使用。同時(shí),必須清楚當(dāng)前的 JavaScript 完全可以用基于原型的風(fēng)格和基于類的風(fēng)格讀取和寫(xiě)入 JavaScript 代碼。

展望
隨 著交互式胖客戶端 AJAX 應(yīng)用程序的廣泛使用,JavaScript 迅速成為 .NET 開(kāi)發(fā)人員最重要的工具之一。但是,它的原型性質(zhì)可能一開(kāi)始會(huì)讓更習(xí)慣諸如 C++、C# 或 Visual Basic 等語(yǔ)言的開(kāi)發(fā)人員感到吃驚。我已發(fā)現(xiàn)我的 JavaScript 學(xué)習(xí)經(jīng)歷給予了我豐富的體驗(yàn),雖然其中也有一些挫折。如果本文能使您的體驗(yàn)更加順利,我會(huì)非常高興,因?yàn)檫@正是我的目標(biāo)。

JavaScript技術(shù)js 面向?qū)ο蟮募夹g(shù)創(chuàng)建高級(jí) Web 應(yīng)用程序,轉(zhuǎn)載需保留來(lái)源!

鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。

主站蜘蛛池模板: 久久福利 | 国产精品精品3d动漫 | 亚洲一区二区三区福利 | 人人爽日日躁夜夜躁尤物 | 亚州成人| 久久国| 日韩久久久久 | 欧美激情在线一区二区三区 | 国产日韩欧美在线 | 色888www视频在线观看 | 午夜视频一区二区 | 精品成人 | 99热精品在线观看 | 一级电影免费看 | 天天射影院| 成人日韩 | 蜜桃视频在线观看免费视频网站www | 日本三级做a全过程在线观看 | 久草欧美 | 特黄毛片 | 国产剧情一区二区三区 | 国产精品乱码一区二区三区 | 欧美激情久久久 | 中文字幕成人av | 亚洲aⅴ精品 | 成年人黄色一级毛片 | 日日噜噜噜夜夜爽爽狠狠视频, | 黄免费看 | 一级毛片视频在线 | 蜜桃视频在线观看免费视频网站www | 久热中文字幕 | www.97zyz.com| 亚洲高清在线 | 一区二区三区视频在线观看 | 五月婷婷导航 | 日本精品一区二区三区在线观看视频 | 久久国产亚洲 | 福利一区在线观看 | 欧美激情精品久久久久久免费 | 亚洲成人av| 老子午夜影院 |