對象:在 JavaScript 中,對象分為兩種。
一種可以稱為“普通對象”,就是我們所普遍理解的那些:數字、日期、使用者自訂的對象(如:{})等等。
還有一種,稱為“方法對象”,就是我們通常定義的 function。
自訂對象:
被稱為JavaScript Object Notation(縮寫JSON)的形式,翻譯為中文就是“JavaScript對象標記法”。
JSON為建立對象提供了非常簡單的方法。例如,
建立一個沒有任何屬性的對象:
var o = {};
方法對象:
第一種寫法
function MyFunc() {}; //定義一個空函數 var anObj = new MyFunc(); //使用new操作符,藉助MyFun函數,就建立了一個對象
第二種寫法
function MyFunc(){}; var anObj = {}; //建立一個對象 MyFunc.call(anObj); //將anObj對象作為this指標調用MyFunc函數
理解 JavaScript先用new操作符建立了一個對象(anObj),緊接著就將這個對象(anObj)作為this參數調用了後面的函數(MyFunc())。
原型prototype:
話說每一個方法對象被建立時,都會自動的擁有一個叫 prototype 的屬性。這個屬性並無什麼特別之處,它和其他的屬性一樣可以訪問,可以賦值。不過當我們用 new 關鍵字來建立一個對象的時候,prototype 就起作用了:它的值(也是一個對象)所包含的所有屬性,都會被複製到新建立的那個對象上去。
也可以這麼說,prototype提供了一群同類對象共用屬性和方法的機制。
下面是一個例子:
function Person(name) { this.name = name; //設定對象屬性,每個對象各自一份屬性資料 }; Person.prototype.SayHello = function() //給Person函數的prototype添加SayHello方法。 { alert("Hello, I'm " + this.name); } var BillGates = new Person("Bill Gates"); //建立BillGates對象 var SteveJobs = new Person("Steve Jobs"); //建立SteveJobs對象 BillGates.SayHello(); //通過BillGates對象直接調用到SayHello方法 SteveJobs.SayHello(); //通過SteveJobs對象直接調用到SayHello方法 alert(BillGates.SayHello == SteveJobs.SayHello); //因為兩個對象是共用prototype的SayHello,所以顯示:true
繼承
原型模型需要一個建構函式來定義對象的成員,而方法卻依附在該建構函式的原型上。大致寫法如下:
//定義建構函式 function Person(name) { this.name = name; //在建構函式中定義成員 }; //方法定義到建構函式的prototype上 Person.prototype.SayHello = function() { alert("Hello, I'm " + this.name); }; //子類建構函式 function Employee(name, salary) { Person.call(this, name); //調用上層建構函式 this.salary = salary; //擴充的成員 }; //子類建構函式首先需要用上層建構函式來建立prototype對象,實現繼承的概念 Employee.prototype = new Person() //只需要其prototype的方法,此對象的成員沒有任何意義! //子類方法也定義到建構函式之上 Employee.prototype.ShowMeTheMoney = function() { alert(this.name + " $" + this.salary); }; var SteveJobs = new Employee("Steve Jobs", 1234); SteveJobs.SayHello(); SteveJobs.ShowMeTheMoney();
原型類模型雖然不能類比真正的私人變數,而且也要分兩部分來定義類,顯得不怎麼“優雅”。不過,對象間的方法是共用的,不會遇到記憶體回收問題,而且效能優於“閉包”模型。正所謂“有失必有得”嘛。
在原型模型中,為了實作類別繼承,必須首先將子類建構函式的prototype設定為一個父類的對象執行個體。建立這個父類對象執行個體的目的就是為了構成原型鏈,以起到共用上層原型方法作用。但建立這個執行個體對象時,上層建構函式也會給它設定對象成員,這些對象成員對於繼承來說是沒有意義的。雖然,我們也沒有給建構函式傳遞參數,但確實建立了若干沒有用的成員,儘管其值是undefined,這也是一種浪費啊。
我們已經知道,用 var anObject = new aFunction() 形式建立對象的過程實際上可以分為三步:
第一步是建立一個新對象;
第二步將該對象內建的原型對象設定為建構函式prototype引用的那個原型對象;
第三步就是將該對象作為this參數調用建構函式,完成成員設定等初始化工作。
對象建立之後,對象上的任何訪問和操作都只與對象自身及其原型鏈上的那串對象有關,與建構函式再扯不上關係了。換句話說,建構函式只是在建立對象時起到介紹原型對象和初始化對象兩個作用。
那麼,我們能否自己定義一個對象來當作原型,並在這個原型上描述類,然後將這個原型設定給新建立的對象,將其當作對象的類呢?我們又能否將這個原型中的一個方法當作建構函式,去初始化建立的對象呢?例如,我們定義這樣一個原型對象:
//代碼1
var Person = //定義一個對象來作為原型類 { Create: function(name, age) //這個當建構函式 { this.name = name; this.age = age; }, SayHello: function() //定義方法 { alert("Hello, I'm " + this.name); }, HowOld: function() //定義方法 { alert(this.name + " is " + this.age + " years old."); } };
然後
function anyfunc(){}; //定義一個函數軀殼 anyfunc.prototype = Person; //將原型對象放到中轉站prototype var BillGates = new anyfunc(); //建立對象的內建原型將是我們期望的原型對象
代碼2
var o = {}; // 我發現了一個東西。 o.eat = function(){return "I am eating."} // 我發現它會吃; o.sleep = function(){return "ZZZzzz..."} // 我發現它會睡; o.talk = function(){return "Hi!"} // 我發現它會說話; o.think = function(){return "Hmmm..."} // 我發現它還會思考。 var Human = new Function(); // 我決定給它起名叫“人”。 Human.prototype = o; // 這個東西就代表了所有“人”的概念。 var h = new Human(); // 當我發現其他同它一樣的東西, alert(h.talk()) // 我就知道它也是“人”了!
Javascript的一種模組模式
全域變數是魔鬼。在YUI中,我們僅用兩個全域變數:YAHOO和YAHOO_config。YUI的一切都是使用YAHOO對象級的成員或這個成員範圍內的變數。我們建議在你的應用程式也使用類似的規則。
建立一個命名空間對象:如果你使用YUI,可以用YAHOO.namespace()方法: YAHOO.namespace("myProject");
這分配了一個空的myProject對象,是YAHOO的一個成員(如 果myProject已存在的話,則不會被覆蓋)。現在我們可以開始添加YAHOO.myProject的成員。
對你的命名空間對象分配一個匿名函數傳回值:YAHOO.myProject.myModule = (function () { return { myPublicProperty: "我作為YAHOO.myProject.myModule.myPublicProperty被訪問。"; myPublicMethod: function () { YAHOO.log("我作為YAHOO.myProject.myModule.myPublicMethod被訪問。"); }};})(); 讓我們來看看jquery是怎麼做的
(function() { Me = function() { return new Me.prototype.init(); } Me.fn= Me.prototype = { init: function() { return this; }, showname: function() { alert("Jane"); }, showage: function() { alert(Infinity); } } Me.fn.init.prototype = Me.fn; })(); Me().showname(); Me().showage();
入口:
var jQuery = window.jQuery = window.$ = function(selector, context) {
return new jQuery.fn.init(selector, context);}
上面的代碼①可以看出$(xx)或Jquery(xx)得到不是真正的jQuery函數產生的對象,而是jQuery.fn.init函數產生的對象。也是就是jQuery的對象繼承的是jQuery.fn.init的原型。jQuery.fn = jQuery.prototype={..}。我們基本上不用new jQuery(xx),而是直接jQuery(xx),就是採用了new jQuery(xx),先產生jQuery函數的對象,把原型中的繼承下來,返回的也是jQuery.fn.init函數產生的對象。而jQuery函數的對象也拋棄了。可見給jQuery.prototype上增加方法的意義不大。同時也可以看出採用new jQuery(xx)的效率更低。jQuery.fn.init是通過jQuery.fn.init.prototype = jQuery.fn;來獲得的。